我试图完全理解在C#WPF应用程序中使用调度程序的机制,并提出了我找不到答案的问题。我希望社区能够为我提供帮助。
想象一下,我们有一个带有按钮和标签的用户控件。按下按钮后,一些耗时的操作开始,一旦完成,它将结果(例如,执行持续时间)放置在标签中。
为了获得良好的用户体验,应满足以下条件:
最简单的xaml
:
<UserControl x:Class="WhichDispatcher">
<StackPanel Orientation="Horizontal">
<Button x:Name="button" Content="Execute time-consuming operation" Click="button_Click" />
<Label x:Name="label" Content="Not yet executed" />
</StackPanel>
</UserControl>
以及背后的代码:
public partial class WhichDispatcher : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
this.button.IsEnabled = false;
Task.Run(() =>
{
this.TimeConsumingOperation();
});
}
private void TimeConsumingOperation()
{
throw new NotImplementedException();
}
}
调用耗时操作的方法将在单独的线程上运行,以防止UI锁定。
我现在重点介绍将执行耗时的操作的方法的实现。完成后,需要在调度程序中更新UI元素,因为无法从另一个线程调用它们。
private void TimeConsumingOperation()
{
TimeSpan runningTime = new TimeSpan();
// Run the time-consuming operation
// Let's assume that the runningTime variable will be set as a result of the above operation.
this.Dispatcher.Invoke(() =>
{
this.button.IsEnabled = true;
this.label.Content = string.Format("The time-consuming operation took {0} seconds", runningTime.TotalSeconds);
});
}
UI更新将在UserControl
的调度程序中执行。现在开始提问。
UI更新也可以在元素的分派器中单独执行,如下所示:
private void TimeConsumingOperation()
{
TimeSpan runningTime = new TimeSpan();
// Run the time-consuming operation
// Let's assume that the runningTime variable will be set as a result of the above operation.
this.button.Dispatcher.Invoke(() =>
{
this.button.IsEnabled = true;
});
this.label.Dispatcher.Invoke(() =>
{
this.label.Content = string.Format("The time-consuming operation took {0} seconds", runningTime.TotalSeconds);
});
}
问题是:我使用的调度程序有什么区别?我是否应该总是尝试使用最小范围的调度程序,还是没关系,并且我始终可以选择可用的最大范围调度程序(在这种情况下为UserControl
,因为它还是UI线程调度程序吗?< / p>
答案 0 :(得分:1)
您看到的“不同” Dispatcher
实际上是WPF中UI线程的同一Dispatcher
。
但是您的情况是async
方法的经典模式。
首先,在UI线程上准备所需的所有内容(禁用按钮)。 然后等待长时间运行的任务。 最后,对任务进行后处理(启用按钮,设置输出)。
您无需使用这种方法来烦扰Dispatcher
。
private async void button_Click(object sender, RoutedEventArgs e)
{
this.button.IsEnabled = false;
TimeSpan runningTime = new TimeSpan();
await Task.Run(() =>
{
this.TimeConsumingOperation();
});
this.label.Content = string.Format("The time-consuming operation took {0} seconds", runningTime.TotalSeconds);
this.button.IsEnabled = true;
}
但是,如果要从在后台线程上执行的长时间运行的任务中与UI 进行交互,则需要考虑以下几点:
Dispatcher
手动封送数据。不推荐,但有时不可避免。