在单独的线程上启动事件处理程序类时的GUI更新?

时间:2009-04-06 22:39:17

标签: c# .net multithreading user-interface

我们有一个DLL来监控状态的变化,并从单独购买的产品中查找事件。实际上,它是西门子的ScreenPop API,对于那些可能知道它是什么的人来说。我使用C#.NET 3.5作为平台。

这个API需要很长的时间来初始化,所以我们想要使用一个单独的线程来初始化它。目前,我们在名为ScreenPop的类中具有该功能。该类监视2个事件,状态更改事件和屏幕弹出事件(数据告诉我们客户是谁正在呼叫)。

目前实施的方式不起作用,或者至少不能可靠地运作。在ScreenPop类中,有一个初始化方法,其中放置了所有长时间运行的启动代码。这是从类的构造函数中调用的,如下所示:

public ScreenPop( string Address, int Ext, CallbackStatusType pStatusFunc,
      CallbackScreenPopType pPopFunc 
)
{
    CallbackStatus = pStatusFunc;
    CallbackPopup = pPopupFunc;

    Thread t = new Thread( StartInBackground );
    t.Start();
}

在GUI代码中,pStatusFunc处的func更新状态标签,pPopupFunc处的func将触发其他一些代码来弹出屏幕 - 现在它只显示事件中的数据。

缺少很多胶水,但我希望你明白这一点。这种方法的问题是GUI没有更新。我知道事件激活和事件处理程序运行,并且回调函数被调用,它们似乎应该运行,但GUI永远不会更新。

所以,我的问题是,我是否应该放弃支持BackgroundWorker方法呢?或者我只是遗漏了让GUI更新的内容?

有关要求的更多信息...... 谢谢, 戴夫

2 个答案:

答案 0 :(得分:3)

您永远不能从另一个线程更新GUI - 只能从启动应用程序的UI线程更新。您需要使用Control.Invoke方法在UI线程上运行代码。表单实例,frmMain.Invoke。

答案 1 :(得分:2)

你不能在多线程公寓中使用WinForms来解决这个问题,你必须编组到UI线程来对其执行操作或获得结果。由于您使用的是C#3.5,因此您可以使用lambda,泛型和扩展方法来制作一个非常简洁易用的解决方案。

public static class ControlExtensions
{
  public static TResult InvokeEx<TControl, TResult>(this TControl control,
                            Func<TControl, TResult> func)
    where TControl : Control
  {
    if (control.InvokeRequired)
    {
      return (TResult)control.Invoke(func, control);
    }
    else
    {
      return func(control);
    }
  }
}

现在,您可以安全轻松地进行更改或获取价值。

this.InvokeEx(f => f.label1.Text = "Hello from another thread");

new Thread(() =>
  {
    string formTitle = this.InvokeEx(f => f.Text); // Safely get form title
  }).Start();