【发布时间】:2021-06-11 17:47:30
【问题描述】:
所以,我有一个大型应用程序,它在 UI 线程上做了一些长时间的工作。我知道这是糟糕的设计,但这是无法改变的。把它当作一个约束。
为了显示工作的进度,我想显示一个带有进度条的 WPF 对话框。该对话框将 DataBindings 用于 DependencyProperties 的标签和进度条。
当我第一次在对话框实例上调用Show() 时,对话框不会显示,因为调度程序正忙于处理长时间运行的任务。所以我在Show() 之后打电话给Dispatcher.Yield()。每次更新进度条的 DependencyProperty 后,我也会调用 Yield()。对话窗口出现了,但它仍然很空。 Dispatcher.Yield() 上的 DataBindings 未更新。
有没有机会在 Dispatcher 忙时让他们更新?
示例应用
创建一个新的 .NET Framework WPF 应用,将其命名为“BusyProgress”并更改这些文件。
MainWindow.xaml
<Window x:Class="BusyProgress.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BusyProgress"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="window"
Title="MainWindow"
Width="400"
Height="100"
mc:Ignorable="d">
<DockPanel DataContext="{Binding ElementName=window}">
<ProgressBar Height="20"
DockPanel.Dock="Bottom"
Maximum="10"
Value="{Binding Progress}" />
<TextBlock Text="{Binding LabelText}" />
</DockPanel>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace BusyProgress
{
public partial class MainWindow : Window
{
public string LabelText
{
get => (string)GetValue(LabelTextProperty);
set => SetValue(LabelTextProperty, value);
}
public static readonly DependencyProperty LabelTextProperty =
DependencyProperty.Register("LabelText", typeof(string), typeof(MainWindow), new PropertyMetadata("Initial Value"));
public int Progress
{
get => (int)GetValue(ProgressProperty);
set => SetValue(ProgressProperty, value);
}
public static readonly DependencyProperty ProgressProperty =
DependencyProperty.Register("Progress", typeof(int), typeof(MainWindow), new PropertyMetadata(0));
public MainWindow()
{
InitializeComponent();
}
}
}
App.xaml
<Application x:Class="BusyProgress.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup" />
App.xaml.cs
using System.Windows;
using System.Windows.Threading;
using System.Threading;
using System;
namespace BusyProgress
{
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
MainWindow window = new MainWindow();
window.LabelText = "Init";
window.Show();
Dispatcher.Yield();
// Do some long running stuff
int count = 0;
while (count++ < 10)
{
Console.WriteLine(count);
window.Progress = count;
window.LabelText = $"Working hard... ({count}/10)";
Dispatcher.Yield();
// Simulate Work
Thread.Sleep(500);
}
MessageBox.Show("Done");
}
}
}
【问题讨论】:
-
调度程序队列是优先队列。它将首先运行具有较高优先级的任务,而较低优先级(包括绑定更新 afaik)将没有机会运行。您可以尝试使用 in this answer 之类的东西来打破您的 UI 占用任务:调用足够低的优先级以确保绑定将得到更新。
-
感谢您的意见。
Dispatcher.Yield(DispatcherPriority.SystemIdle)不应该在调度程序上运行最低优先级的任务吗?我只是觉得我缺少一些禁用 DataBindings 的框架方法或属性......也许其他人知道它 =) -
我没有使用
Dispatcher.Yield的经验。试试我之前评论的建议。此外,您应该在遇到问题时显示minimal reproducible example,这可能不是我们目前怀疑的Dispatcher.Yield。 -
我刚试过这个,但它仍然没有更新绑定。当我跳过
Hide()调用我的对话框并让UI线程上的长时间运行任务完成时,对话框将更新并显示最后一个状态......所以我猜biniding被正确定义但只是没有更新而用户界面很忙... -
是的,你是对的。我很快就会准备一个例子
标签: c# wpf data-binding dispatcher