c# - 从 TPL 任务向 WPF View 报告进度的适当方法是什么?

标签 c# c#-4.0 progress-bar task-parallel-library

我试图以小“ block ”或页面的形式从数据库中读取大量行,并向用户报告进度;即,如果我正在加载 100 个“ block ”,则在加载每个 block 时报告进度。

我正在使用 C# 4.0 中的 TPL 从数据库中读取这些 block ,然后将完整的结果集交给另一个可以使用它的任务。我觉得与 BackgroundWorker 等相比,TPL 可以更好地控制任务取消和移交,但似乎没有内置的方式来报告任务的进度。

这是我为向 WPF 进度条报告进度而实现的解决方案,我想确保这是合适的并且没有更好的方法我应该采用。

我首先创建了一个简单的界面来表示不断变化的进度:

public interface INotifyProgressChanged
{
  int Maximum { get; set; }
  int Progress { get; set; }
  bool IsIndeterminate { get; set; }
}

这些属性可以绑定(bind)到 WPF View 中的 ProgressBar,接口(interface)由负责启动数据加载并最终报告总体进度的支持 ViewModel 实现(此示例已简化):

public class ContactsViewModel : INotifyProgressChanged
{
  private IContactRepository repository;

  ...

  private void LoadContacts()
  {
    Task.Factory.StartNew(() => this.contactRepository.LoadWithProgress(this))
      .ContinueWith(o => this.UseResult(o));
  }
}

您会注意到我将 ViewModel 作为 INotifyProgressChanged 传递给存储库方法,这是我想确保我没有做错什么的地方。

我的想法是,为了报告进度,实际执行工作的方法(即存储库方法)需要访问 INotifyProgressChanged 接口(interface),以便报告最终更新 View 的进度。下面是存储库方法的快速浏览(在此示例中已缩短):

public class ContactRepository : IContactRepository
{
  ...

  public IEnumberable<Contact> LoadWithProgress(INotifyProgressChanged indicator)
  {
    var resultSet = new List<Contact>();
    var query = ... // This is a LINQ to Entities query

    // Set the maximum to the number of "pages" that will be iterated
    indicator.Maximum = this.GetNumberOfPages(query.Count(), this.pageSize);

    for (int i = 0; i < indicator.Maximum; i++)
    {
      resultSet.AddRange(query.Skip(i * this.pageSize).Take(this.pageSize));
      indicator.Progress += 1; // As each "chunk" is loaded, progress is updated
    }

    // The complete list is returned after all "chunks" are loaded
    return resultSet.AsReadOnly();
  }
}

这就是存储库最终通过 ViewModel 向 View 报告进度的方式。这是正确的方法吗?我是否正确使用 TPL,是否违反了任何主要规则等?此解决方案正在发挥作用,正在按预期报告进度,我只是想确保我不会为失败做好准备。

最佳答案

这样做的“规定”方式是传递 TaskScheduler来自 TaskSheduler::FromCurrentSynchronizationContext 的实例至 ContinueWith您要确保在 WPF 调度程序线程上执行。

例如:

 public void DoSomeLongRunningOperation()
 {
    // this is called from the WPF dispatcher thread

    Task.Factory.StartNew(() =>
    {
        // this will execute on a thread pool thread
    })
    .ContinueWith(t =>
    {
        // this will execute back on the WPF dispatcher thread
    },
    TaskScheduler.FromCurrentSynchronizationContext());
 }

关于c# - 从 TPL 任务向 WPF View 报告进度的适当方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6684580/

相关文章:

c# - 企业库 5.0 强行关闭事件连接

c# - C#中 `event = method`是什么意思

c# - 通过 MSMQ 进行通信的 Windows 服务 - 我需要服务总线吗?

c#-4.0 - C# LDAP SetPassword 抛出 RPC 服务器不可用

javascript - 如何在jQuery函数执行时显示加载进度条?

c# - C# 中的多媒体键盘的多媒体控制

c# - 如何检查字符数组中是否存在特定字符

c# - 将重复的目录移回根目录

python - pygtk 运行两个窗口,弹出窗口和主窗口

android - 如何在每个 ListView 项目中显示进度条?