要从其他线程更新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();
}));
}
}
}
}
当我没有CheckAccess()并且总是执行BeginInvoke时,一切正常。
所以我的问题是为什么不总是使用BeginInvoke并跳过CheckAccess?
答案 0 :(得分:5)
所以我的问题是为什么不总是使用
BeginInvoke
并跳过CheckAccess
?
如果需要调用,那么你应该在大多数情况下做什么(即你正在触摸另一个线程所拥有的控件)。如果不需要调用,那么你应该跳过它们。
使用CheckAccess
意味着您的代码不知道或不想假设它将在"正确"上运行。线。这有两个主要原因:通用性(您的代码在库中,您无法预测它将如何使用)和便利性(您只需要一种方法来处理这两种情况,或者您想要自由在不破坏程序的情况下改变操作模式。)
您的示例属于第二类:同一方法为两种操作模式提供服务。在这种情况下,您有三种可能的选择:
始终在没有CheckAccess
的情况下调用。
这会给你带来性能损失(这里可以忽略不计),并且它还会使代码的读者认为该方法仅从工作线程调用。由于唯一的好处是您将编写少量代码,这是最糟糕的选择。
保持原样。
由于从UI和工作线程调用IncrementAndShowCounter
,因此使其适应这种情况可以让您继续处理其他问题。这很简单,也很好;在编写库代码时,它也是你能做的最好的事情(不允许任何假设)。
永远不要在方法中调用,根据需要从外部执行。
这是技术优点的最佳选择:因为您知道调用该方法的上下文,所以安排调用发生在它之外。这样,该方法不依赖于任何特定的线程,并且您不会受到不必要的性能损失。
以下是第三个选项的示例代码:
private void IncrementAndShowCounter()
{
i++;
myText.Text = i.ToString();
}
myButton.Click += (_, __) => IncrementAndShowCounter();
timer.Elapsed += (_, __) => Dispatcher.BeginInvoke(IncrementAndShowCounter);
答案 1 :(得分:2)
如果您100%确定调用线程是UI线程 - 您可以使用" DO"方法直接。
如果您100%确定调用线程不是UI线程,但操作应该在UI线程上完成,那么您只需调用 BeginInvoke
....
// 100% I'm sure the Click handler will be invoked on UI thread
myButton.Click += (_, __) => IncrementAndShowCounter();
// here I'm not sure
timer.Elapsed += (_, __) => Dispatcher.BeginInvoke(IncrementAndShowCounter);
// 100% not UI thred here:
Task.Factory.StartNew(() => Dispatcher.BeginInvoke(IncrementAndShowCounter), TaskScheduler.Default)
private void IncrementAndShowCounter()
{
i++;
myText.Text = i.ToString();
}