Ric*_*eft 0 c# wpf user-interface multithreading
要从其他线程更新UI,您需要调用调度程序的BeginInvoke方法.在调用方法之前,您可以检查调用线程是否与调度程序关联.
对于我的例子,我有2种方法来更新文本框; 通过单击按钮并通过计时器.代码:
using System;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
private int i = 0;
private TextBlock myText = new TextBlock();
private Button myButton = new Button();
private Timer timer = new Timer(2 * 1000);
private StackPanel panel = new StackPanel();
public MainWindow()
{
InitializeComponent();
myButton.Content = "Click";
panel.Children.Add(myText);
panel.Children.Add(myButton);
this.AddChild(panel);
myButton.Click += (_, __) => IncrementAndShowCounter();
timer.Elapsed += (_, __) => IncrementAndShowCounter();
timer.Start();
}
private void IncrementAndShowCounter()
{
i++;
if (this.Dispatcher.CheckAccess())
{
myText.Text = i.ToString();
}
else
{
this.Dispatcher.BeginInvoke((Action)(() =>
{
myText.Text = i.ToString();
}));
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
当我没有CheckAccess()并且总是执行BeginInvoke时,一切正常.
所以我的问题是为什么不总是使用BeginInvoke并跳过CheckAccess?
所以我的问题是为什么不总是使用
BeginInvoke和跳过CheckAccess?
如果需要调用(即您正在触摸另一个线程拥有的控件),那么这正是您应该在大多数情况下应该执行的操作.如果不需要调用,那么您应该跳过它们.
使用CheckAccess意味着您的代码不知道或不想假设它将在"正确"的线程上运行.这有两个主要原因:通用性(您的代码在库中,您无法预测它将如何使用)和方便性(您只需要一种方法来处理这两种情况,或者您希望自由更改不破坏程序的操作方式).
您的示例属于第二类:同一方法为两种操作模式提供服务.在这种情况下,您有三种可能的选择:
总是不用调用CheckAccess.
这将为您带来性能损失(这里可以忽略不计),并且它还将使代码的读者假定该方法仅从工作线程调用.由于唯一的好处是您将编写少量代码,这是最糟糕的选择.
保持一切不变.
由于IncrementAndShowCounter从UI和工作线程调用,因此使其适应这种情况可以让您继续处理其他问题.这很简单,也很好; 它也是编写库代码时最好的(不允许任何假设).
永远不要从方法中调用,根据需要从外部执行.
这是技术优点的最佳选择:因为您知道调用该方法的上下文,所以安排调用发生在它之外.这样,该方法不依赖于任何特定的线程,并且您不会受到不必要的性能损失.
这是第三个选项的示例代码:
private void IncrementAndShowCounter()
{
i++;
myText.Text = i.ToString();
}
myButton.Click += (_, __) => IncrementAndShowCounter();
timer.Elapsed += (_, __) => Dispatcher.BeginInvoke(IncrementAndShowCounter);
Run Code Online (Sandbox Code Playgroud)