Winforms 从其他线程更新UI

Winforms 从其他线程更新UI,winforms,multithreading,asynchronous,Winforms,Multithreading,Asynchronous,我知道以前有人问过这个问题,但我觉得问得不对 我有一个密集的操作,我希望用户界面保持响应。我读过一些帖子,说后台工作人员是最好的方法,但是,我认为这是假设您拥有完成密集任务的源代码 我有一个没有源代码的库,我可以检查进度的唯一方法是附加到触发的事件并以这种方式获取信息 我在MSDN站点上看到的示例假定其中一个具有源代码 我知道如何通过附加到事件来获得进度(百分比值),但如何将该值返回到UI?附加到第三方组件中的进度事件,并调用BackgroundWorker。将UI附加到事件以更新UI。通过使用

我知道以前有人问过这个问题,但我觉得问得不对

我有一个密集的操作,我希望用户界面保持响应。我读过一些帖子,说后台工作人员是最好的方法,但是,我认为这是假设您拥有完成密集任务的源代码

我有一个没有源代码的库,我可以检查进度的唯一方法是附加到触发的事件并以这种方式获取信息

我在MSDN站点上看到的示例假定其中一个具有源代码


我知道如何通过附加到事件来获得进度(百分比值),但如何将该值返回到UI?

附加到第三方组件中的进度事件,并调用
BackgroundWorker
。将UI附加到事件以更新UI。

通过使用第二个线程和线程安全队列,可以达到所需的效果

您可以创建第二个线程来侦听事件。 当新事件发生时,它将事件信息推送到队列(线程安全-同步)

使用一个计时器(Windows.Forms.Timer),每x次检查一次队列,如果存在新事件,可以更新UI


由于计时器在主线程中运行,因此它可以安全地更新UI,如果您将其设置为轻量级,它将不会阻塞足够长的时间以引起注意。

正在进行类似的讨论。如果出于任何原因不能或不想使用BackgroundWorker,您可以使用自己的线程并将事件从它封送回UI线程

private void Initialise() {      
    MyLibClass myLibClass = new MyLibClass();
    myLibClass.SomeEvent += SomeEventHandler;
    ThreadPool.QueueUserWorkItem(myLibClass.StartLongTask);
}


private void SomeEventHandler(EventArgs e) {     
   if (this.Dispatcher.Thread != Thread.CurrentThread) { 
      this.Dispatcher.Invoke(delegate { DoStuffOnUIThread(e); });
   }
   else {        
       DoStuffOnUIThread(e);     
   }
}

下面的答案是基于我的直觉,并没有实际使用第三方LIB进行测试

  • 像往常一样在简单的后台(而不是BackGroundWorker)线程中调用第三方库代码
  • 将库组件的事件附加到代码中的正常事件处理程序(用于更新UI)
  • 在事件处理程序中,代码应如下所示:

    private void EventHandler(object sender, DirtyEventArgs e)
    {
        if (myControl.InvokeRequired)
            myControl.Invoke(new MethodInvoker(MethodToUpdateUI), e);
        else
            MethodToUpdateUI(e);
    }
    
    private void MethodToUpdateUI(object obj) 
    {
        // Update UI
    }
    

@Vince-也许这是在主线程上引发事件的唯一方法,如果您没有访问“Control”类的权限,并且不想创建一个虚拟类,而只是通过Invoke方法引发事件,那么我可以只传递方法名称,还是需要先创建委托?检查更改。您可以使用MethodInvoker委托。只有方法名是不够的。