65.9K
CodeProject 正在变化。 阅读更多。
Home

用于跨线程 Winforms 访问的通用方法

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (19投票s)

2009 年 6 月 18 日

CPOL

2分钟阅读

viewsIcon

80525

downloadIcon

1245

使用泛型检查 Winforms 控件的 InvokeRequired 的便捷快捷方式

引言

在编写 WinForms 应用程序时,很可能会遇到需要从另一个线程访问 Form 控件的情况,例如从 Timer.Interval 事件中。 为此,必须检查 Control.InvokeRequired 属性,如果为 true,则必须使用 Control.Invoke 方法的委托来完成控件访问。 

一个例子如下

void UpdateLabel(Label lbl, String text)
{
    if (lbl.InvokeRequired)
    { lbl.Invoke(new Action<Label, String>(UpdateLabel), new object[] { lbl, text }); }
    else
    { lbl.Text = text; }
}

虽然这有效,但它需要为每种控件类型以及希望从另一个线程执行的每个操作编写单独的方法。

为了封装这种跨线程访问行为,我们转向泛型。

Using the Code

我们将使用以下泛型实用程序方法来执行所有跨线程控件操作

public static void InvokeControlAction<t>(t cont, Action<t> action) where t : Control
{
    if (cont.InvokeRequired)
    { cont.Invoke(new Action<t, Action<t>>(InvokeControlAction), 
				new object[] { cont, action }); }
    else
    { action(cont); }
}

详细说明

  • t 是我们希望访问的控件的类型,例如 Label
  • cont 是我们将要操作的控件的实例。
  • action 是一个泛型委托 void ,它接受 t 的实例。 通过使用泛型委托,我们可以在调用代码中定义我们想要访问的 t 实例的属性/方法,而不是像以前那样在方法中假定控件类型和操作。
  • 最后,t 必须继承 Control 才能检查 InvokeRequired 属性。

所以我们执行如下

  1. 如果 cont.InvokeRequired 的计算结果为 true,则使用新的泛型 Action<> 委托调用 cont.Invoke,该委托符合 InvokeControlAction<t> 的签名,并将 contaction 递归地传递回去。
  2. 如果 cont.InvokeRequired 的计算结果为 false (要么执行了步骤 1,要么从包含 cont 的同一线程调用了此方法),我们执行委托 action,传入我们的控件实例... cont

要在实际示例中使用此方法:在我们的时钟应用程序中,我们有一个每秒触发一次的计时器。 这是 Elapsed 事件处理程序,我们在此处使用我们的实用程序方法

void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    //On tick, update the time
    CrossThreadUtility.InvokeControlAction<Label>
	(lblTime, lbl => lbl.Text = String.Format("The current time is: {0}", 
	DateTime.Now.ToString("h:mm:ss tt")));
}

在这里,我们将 Label lblTime 作为 cont 传递,对于委托 action,我们使用 C# 3.0 的 Lambda 表达式功能以内联方式编写我们的委托,以便获得简洁的一行代码。

我们在 lblTime 上执行的操作,(在 lambda 表达式中 lbl 的上下文中)很简单

lbl.Text = String.Format("The current time is: {0}", 
			DateTime.Now.ToString("h:mm:ss tt"))

关注点

您可以轻松地将 Label 替换为任何 Windows Forms 控件类型...尝试这样做以使时间显示在 Form 的标题栏上,而不是 Label lblTime

CrossThreadUtility.InvokeControlAction<Form>(this, frm => frm.Text = 
    String.Format("The current time is: {0}", DateTime.Now.ToString("h:mm:ss tt")));

结论

我们已经创建了一个实用程序方法,用于简洁的一行跨线程控件访问,而不管控件类型或访问操作如何。

历史

  • 2009 年 6 月 18 日:初始发布
© . All rights reserved.