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

操作方法:管理跨线程 WinForms 控件访问

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.40/5 (9投票s)

2012年6月19日

CPOL

2分钟阅读

viewsIcon

47138

downloadIcon

407

使用泛型和扩展方法提供易于使用的跨线程 WinForms 控件操作辅助工具。

引言   

在开发复杂的业务应用程序时,开发人员经常需要处理在单独的线程上操作数据和 UI,因此必须管理跨线程控件访问。

在 WinForms 中,我们通过使用回调方法并在主线程上调用它们来实现这一点。

例如,考虑一个繁重的后台任务,它在计算机中搜索一些文件并在过程中发送回日志信息。我们应该实现一个处理事件的方法

private void AppendLogText(string logText)
{
    if (tbLog.InvokeRequired)
    {
        AppendLogTextCallBack callBack = new AppendLogTextCallBack(AppendLogText);
        Invoke(callBack, new object[] { logText });
    }
    else
    {
        if (!string.IsNullOrEmpty(tbLog.Text))
            tbLog.AppendText(Environment.NewLine);
        tbLog.AppendText(string.Concat(DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss"), " : ", logText));
    }
}

本文将展示如何实现方法来简化跨线程控件访问的实现。

背景

在本文中,我们使用 TPL(任务并行库)异步运行工作线程。TPL 是框架 4 中包含的一组 API,请访问此链接了解更多信息:此处

我们还使用 Action 类,它封装了一个方法。在这种情况下,它非常有用,可以封装回调方法,而无需将它们声明为类字段。要了解更多信息,请访问此链接:此处

由于工作线程正在异步运行,因此工作线程实现了一些事件以发送回信息。

第一步:创建一个管理 invoke 方法的辅助工具

所有指南的基础是不重复开发过程:当您需要在代码中管理多个控件的跨线程访问时,应该只实现一次该模式。

为此,我们使用一个辅助工具,它提供了一种管理控件访问和回调方法触发的方法

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

由于泛型,此辅助工具可完全用于所有控件:它检查 InvokeRequired 属性并触发嵌入在 Action 中的回调方法。Action 类可以由 lambda 表达式赋值,因此编写代码非常简单。

有了这个辅助工具,我们现在可以通过一种独特的方式管理所有控件访问,无论是否跨线程

private void AppendLogText(string logText)
{
    ControlThreadingHelper.InvokeControlAction(tbLog, () =>
    {
        if (!string.IsNullOrEmpty(tbLog.Text))
            tbLog.AppendText(Environment.NewLine);
        tbLog.AppendText(string.Concat(DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss"), " : ", logText));
    });
}

第二步:简化代码编写

为了简化代码编写,我们应该直接在 Control 类中实现控件访问方法。

我们可以通过扩展方法来实现这一点:

public static class ControlThreadingExtensions
{
    public static void Invoke<t>(this t control, Action action) where t : Control
    {
        ControlThreadingHelper.InvokeControlAction<t>(control, action);
    }
}

此扩展方法允许我们将控件访问直接实现到 WinForm 的控件上

private void AppendLogText(string logText)
{
    tbLog.Invoke(() =>
    {
        if (!string.IsNullOrEmpty(tbLog.Text))
            tbLog.AppendText(Environment.NewLine);
        tbLog.AppendText(string.Concat(DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss"), " : ", logText));
    });
}

我们已经分离了关注点:跨线程访问由辅助工具中的唯一方法管理,而特定的表单行为被分配到表单的后台代码中。

值得关注的点

在本文中,我们已经看到如何将 Action 类、辅助工具和扩展方法结合起来,帮助我们编写提供易于使用的跨线程控件访问方法。

历史 

  • 2012/06/19:Bug 修复:使用空 Action 代替 Action<t> 作为回调方法
  • 2012/06/19:原始版本。
© . All rights reserved.