WPF MVVM和TPL数据流中的进度条

时间:2014-04-09 18:37:57

标签: c# wpf mvvm task-parallel-library tpl-dataflow

我在遵循MVVM模式的WPF应用程序中使用TPL数据流。 我有一个TransformBlock<object,object>和一个ActionBlock<object>,我将它们连接起来:

transformBlock.LinkTo(notificationBlock);

ActionBlock<object>应该使用当前进度更新视图中的进度条,但是UI似乎被冻结,只有在所有内容完成处理时才会更新。

我的CurrentProgress属性如下所示:

private double _CurrentProgress;

public double CurrentProgress
{
    get { return _CurrentProgress; }
    set
    {
        _CurrentProgress = value;
        RaisePropertyChanged("CurrentProgress");
    }
}

我将它绑定到我的View就像这样:

<ProgressBar Value="{Binding CurrentProgress, Mode=OneWay}" Name="uxProgressBar"/>

我错过了什么吗?为什么TPL会阻止UI线程?

修改

这就是我实例化TPL的方式:

foreach(var myObj in ObjList)
{
    transformBlock.Post(myObj);
}

变换块:

TransformBlock<object, object>(
temp =>
{
    var response = ProcessRecord(temp);
    return response.Status;
},
new ExecutionDataflowBlockOptions
{
    MaxDegreeOfParallelism =20
});

行动阻止:

ActionBlock<object>(
temp =>
{
    CurrentProgress = (double)temp.RecordNumber/(double)TotalRecords;
},
new ExecutionDataflowBlockOptions
{
    TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
});

更新

TransformBlock中调用的Web服务是遗留(asmx)Web服务,并且它不会被称为Async。解决此问题后,其他一切工作正常,而不使用Dispatcher或任何其他建议的解决方案。

从其中一条评论到问题,似乎WPF确实支持从另一个线程发布到UI线程。虽然我还没有找到任何关于此的官方文档。

3 个答案:

答案 0 :(得分:2)

首先,您的ActionBlock不必直接更改CurrentProgress媒体资源。

原因是RaisePropertyChanged函数将直接运行ProgressBar对象的代码。 这是不允许的,因为它是UIThread拥有的对象。

ActionBlock在他自己的线程中运行,他需要将progressBar更新顺序发布到UI线程(使用Dispatcher.BeginInvoke):

ActionBlock<object>(
temp =>
{
    double progress = (double)temp.RecordNumber/(double)TotalRecords;
    Dispatcher.BeginInvoke((Action)(() =>
    {
        CurrentProgress = progress;
    }));
},
new ExecutionDataflowBlockOptions
{
    TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
});

为了制作你想要的东西,你必须首先改变这一点。

其次(如果这不会改变任何内容),您必须检查您的UI线程是否在等待TransformBlock的完成。如果你的ICommand(如果你从一个按钮中调用它来作为例子)不是ASync并且做这样的事情:

transformBlock.Completion.Wait();

不行。因为您的UIThread将等待您的治疗结束,并且不会将之前的progressBar更新订单直到结束。

祝你好运! 如果新细节不起作用,请发布。

答案 1 :(得分:1)

尝试使用&#39; SendAsync&#39;

将代码发布到TransformBlock
foreach(var myObj in ObjList)
{
   await transformBlock.SendAsync(myObj);
}

答案 2 :(得分:1)

您应该使用Dispatcher通知UI线程您要从后台进程更新绑定值。

以下是文章的链接:

  

http://msdn.microsoft.com/en-us/magazine/cc163328.aspx

     

http://msdn.microsoft.com/en-us/library/vstudio/system.windows.threading.dispatcher

我将整理一个类似的代码示例并很快更新此响应。