扩展方法使 WPF 线程安全编程更容易






3.25/5 (10投票s)
提供扩展方法,

引言
在编写多线程 WPF 应用程序时,您必须确保不要尝试从主线程以外的任何线程访问任何 UI 元素。 这可能导致重复且看起来杂乱的代码。 使用扩展方法,我们将研究一种简化方法,以提供线程安全的 UI 更新。
背景
Sacha Barber 在他的文章“有用的 WPF 线程处理扩展方法”中介绍了一种线程安全的 UI 更新方法。 本文扩展了他的想法,并提供了一个更简单的编码结构。
示例应用程序
当您运行示例应用程序时,您可以单击“开始”按钮。 这将启动一个计时器,该计时器每秒触发一次。 单击“停止”以停止计时器。 当“线程安全模式”复选框被选中时,蓝色框中的 UI 元素将每秒更新一次。 如果未选中“线程安全模式”复选框,则会发生异常。 当计时器未运行时,您也可以单击“从 UI 更新”按钮。 这将更新蓝色框中的控件,无论“线程安全模式”是否被选中,因为它从 UI 线程调用 UpdateControls。
Using the Code
使用 Sacha 的原始 Extension
方法,textBox1
的文本将设置如下
this.InvokeIfRequired(() =>
{
textBox1.Text = "Hello World";
},
DispatcherPriority.Background);
使用我的扩展方法,textBox1
的文本将设置如下
textBox1.SetTextThreadSafe("Hello World");
要使用 Sacha 的扩展方法获取 textBox1
的 Text
,将是
string s = "";
this.InvokeIfRequired(() =>
{
s = textBox1.Text = "Hello World";
},
DispatcherPriority.Background);
使用我的扩展方法设置 Text
将是
string s = textBox1.GetTextThreadSafe();
它是如何工作的?
我们只需为我们要获取或设置的值定义一个 Get
和 Set
方法。 为了方便起见,我将这些方法放在与 InvokeIfRequired
方法相同的 static
类中。
一个示例 Set
方法
public static string SetTextThreadSafe(this TextBox tb)
{
string s = "";
InvokeIfRequired(tb, () => { s = tb.Text; }, DispatcherPriority.Background);
return s;
}
一个示例 Get
方法
public static void SetTextThreadSafe(this TextBox tb, string s)
{
InvokeIfRequired(tb, () => { tb.Text = s; }, DispatcherPriority.Background);
}
扩展方法必须包含在 static
类中。 扩展方法也必须声明为 static
方法。 扩展方法的定义看起来有点奇怪,因为第一个参数是用关键字 this
定义的,后跟该方法扩展的类型,然后是要在方法中用于实例的名称。 仅以这种方式定义第一个参数。 其余的参数都正常定义。
最好尝试为具有您要使用的属性的最通用类创建这些方法。 这样可以防止您为每个控件类型创建类似的方法。 一个例子是 Visibility
属性。 这是一个 UIElement
的属性,因此为 UIElement
创建一个 Set
和 Get
方法而不是为 RadioButton
和 Label
等创建单独的方法是有意义的。
定义扩展方法后,它可由声明中定义的类型的任何实例或从该类型派生的任何类型使用。 它也可以被静态调用。
问答
使用 InvokeIfRequired 方法不是更容易吗?
如果您只需要使用它一两次,则可能就是这种情况。 如果您要多次访问多个 UI 元素,我认为我的扩展方法更容易。
我是否必须为每个 UI 元素属性编写一个扩展方法?
否则,您需要在每次访问 UI 元素的属性时使用 InvokeIfRequired
方法,那么为什么不将其包装在扩展方法中,这样您就只需要执行一次呢? 然后,您可以在任何需要它的应用程序中使用您的 WPFThreadingExtensions
类。
我没有看到我需要的属性的 Set 或 Get 扩展方法?
我在测试应用程序中提供了一些 Get
和 Set
扩展方法的示例,因此您可以使用它们来帮助您了解如何编写自己的扩展方法。
如果我想使用不同的 DispatcherPriority,我该怎么办?
我在我的扩展方法中使用了 DispatcherPriority.Background
。 您可以更改此项,创建一个将您需要的 DisplatcherPriority
作为参数的扩展方法,或使用 InvokeIfRequired
扩展方法。
历史
- 2009年10月20日:初始版本