4

我想实现以下结果 - UI 线程注册我的耗时操作的进度更改事件,然后运行方法“DoOperationAsync()”。然后该操作将报告进度更改,但是:必须在 UI 线程上调用事件,我在实现时遇到问题。事件触发,但是当我尝试更新 UI 时,我需要使用 Dispatcher,因为事件是从执行操作的线程触发的。我不觉得我的库应该强迫开发人员提前思考并在任何地方使用调度程序。

基本上我想做BackgroundWorker 所做的事情。BackgroundWorker 如何在创建它的线程上触发 ProgressChanged 事件?

4

3 回答 3

4

如果您使用的是 .NET 4.5,您将可以访问最新版本的 TPL 更改,其中包括IProgress<T>接口及其具体实现Progress<T>。该界面旨在支持两个异步任务之间的进度报告,特别是您所追求的后台到 UI 线程的报告。

接口本身很简单,将Report(T)方法定义为将类型的进度更新传递T给其他任务的机制。当您有一些进展要报告时,您调用该操作。如果您想传递百分比进度,可以传递0.1IProgress<float>实例以报告 10% 的进度。

private async Task BackgroundWorkAsync(IProgress<float> progress)
{
    ...

    progress.Report(0.1); // 10%

    ...

    progress.Report(1.0); // 100%
}

UI 线程应该创建具体Progress<T>实例并将其传递到后台任务的范围内。Progress<T>提供一个ProgressChanged您可以订阅的事件,但通常您将一个操作传递给构造函数,以便在每次更新进度时调用:

var progress = new Progress(value => // set progress bar);

await this.BackgroundWorkAsync(progress);

这是一个粗略的例子,但它展示了如何Progress<T>根据上下文同步回调的魔力,在这种情况下是 UI 线程。

于 2013-08-02T13:26:55.730 回答
2

ABackgroundWorker使用Event-based asynchronous pattern.

在内部,它使用一个实例class AsyncOperation来引发事件。

具体来说,它调用AsyncOperation.Post()以在适当的线程或上下文中引发事件。

您应该能够使用您的库代码来做到这一点。

于 2013-08-02T12:27:19.493 回答
0

很久以前,没有代码可以准确地记住我是如何做到的,但这是我所做的基础。在我创建后台工作人员的 UI 对象中,我还创建了一个在报告进度时触发的方法。然后,在后台工作人员中,我设置了可以读取并放入我的 UI 的公共属性。

public class myUIFormControlWhatever
{
   ...
   public void CallTheBackgroundWorker()
   {
      myBackgroundWorker bgw = new myBackgroundWorker();
      // attach "listening" when the background worker reports changes
      bgw.ProgressChanged += thisObjectShowChangedProgress;
      bgw.RunWorkerAsync();
   }

   protected void thisObjectShowChangedProgress( object sender, ProgressChangedEventArgs e )
   {
      this.SomeTextShownOnUI = ((myBackgroundWorker)sender).ExposedProperty;
   }
}


public class myBackgroundWorker : BackgroundWorker
{
   public myBackgroundWorker()
   {
      WorkerReportsProgress = true;
      // hook up internal to background worker any strings
      // you want to expose once reporting and any other listeners are out there.
      ProgressChanged += StatusUpdate;
   }

   protected void StatusUpdate( object sender, ProgressChangedEventArgs e )
   {
      // set property to what you want any other listeners to grab/display
      ExposedProperty = "something you are handling internally to background worker";
   }

   public string ExposedProperty
   { get; protected set; }

}

同样,其中大部分来自内存,查找我不记得签名的事件处理程序参数参数。因此,UI 创建了后台工作人员,但通过连接到“ProgressChanged”事件来监听任何报告的更改。因此,一旦后台工作人员完成了工作,UI 组件就会通过查看可以从后台工作人员本身的“对象发送者”参数读取的可见属性来处理它自己的段。

于 2013-08-02T12:46:56.940 回答