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

C# WPF MVVM和TPL数据流中的进度条,c#,wpf,mvvm,task-parallel-library,tpl-dataflow,C#,Wpf,Mvvm,Task Parallel Library,Tpl Dataflow,我在遵循MVVM模式的WPF应用程序中使用TPL数据流。 我有一个TransformBlock和一个ActionBlock,我这样链接它们: transformBlock.LinkTo(notificationBlock); ActionBlock应该用当前进度更新我视图中的进度条,但UI似乎已冻结,只有在所有操作完成后才会更新 我的CurrentProgress属性如下所示: private double _CurrentProgress; public double CurrentPro

我在遵循MVVM模式的WPF应用程序中使用TPL数据流。 我有一个
TransformBlock
和一个
ActionBlock
,我这样链接它们:

transformBlock.LinkTo(notificationBlock);
ActionBlock
应该用当前进度更新我视图中的进度条,但UI似乎已冻结,只有在所有操作完成后才会更新

我的
CurrentProgress
属性如下所示:

private double _CurrentProgress;

public double CurrentProgress
{
    get { return _CurrentProgress; }
    set
    {
        _CurrentProgress = value;
        RaisePropertyChanged("CurrentProgress");
    }
}
我把它和我的观点联系在一起,就像这样:

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

TransformBlock<object, object>(
temp =>
{
    var response = ProcessRecord(temp);
    return response.Status;
},
new ExecutionDataflowBlockOptions
{
    MaxDegreeOfParallelism =20
});
TransformBlock(
温度=>
{
var响应=过程记录(温度);
返回响应状态;
},
新的ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism=20
});
动作块:

ActionBlock<object>(
temp =>
{
    CurrentProgress = (double)temp.RecordNumber/(double)TotalRecords;
},
new ExecutionDataflowBlockOptions
{
    TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
});
ActionBlock(
温度=>
{
CurrentProgress=(双)临时记录编号/(双)总计记录;
},
新的ExecutionDataflowBlockOptions
{
TaskScheduler=TaskScheduler.FromCurrentSynchronizationContext()
});
更新

TransformBlock
中调用的web服务是一个遗留(asmx)web服务,它没有被称为
Async
。解决此问题后,其他一切都可以正常工作,无需使用
调度程序
或任何其他建议的解决方案


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

尝试使用“SendAsync”将代码发布到TransformBlock:

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

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

以下是一篇文章的链接,该文章解释:


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

首先,您的
ActionBlock
不必直接更改
CurrentProgress
属性

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

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

不好。因为您的UIThread将等待治疗结束,并且在治疗结束之前不会接受以前的progressBar更新命令

祝你好运!
如果它总是不起作用,请发布新的详细信息。

您如何实例化
TPL?
发布相关代码,您可能没有正确地“等待”您的代码。我喜欢在示例项目中工作。我没有第三方物流的例子,但看看这个项目。让TPL在这个示例项目中工作,然后转到您的生产项目。您是否正在等待块的
完成
,或者类似的事情?有多少项以及处理它们需要多长时间?@svick我对第三方物流数据流相当陌生。我发布的代码基本上是所有代码,在其他任何地方都没有
Wait()
。从后台线程访问CurrentProgress不会引发InvalidOperationException吗?他应该看到这一点,或者ActionBlock正在吞噬异常?正如您所说,它被吞噬了。只有在调试应用程序时(选中Debug->Exception->catch exceptions),或者在等待ActionBlock.Completion时,才能看到它。Wait()。抛出的异常将对您的TPL网络产生影响(至少它会杀死您的ActionBlock线程),但不会直接通知您的主线程(在本例中为UIThread),除非您在他和您的TPL线程之间创建依赖关系(例如,通过等待其中一个线程)。如果您对这个特定主题感兴趣,您可能会在这里找到一些有用的信息:我知道TPL传播异常的方式,我想知道数据流的行为是否相同:)我刚刚在没有try/catch块的情况下运行了其中一个,并且确认,主线程没有意识到异常。在ActionBlock端,
.Completion.IsFaulted
变为true,不再处理输入。这就是全部。
foreach (var myObj in ObjList)
{
   await transformBlock.SendAsync(myObj);
}
ActionBlock<object>(
temp =>
{
    double progress = (double)temp.RecordNumber/(double)TotalRecords;
    Dispatcher.BeginInvoke((Action)(() =>
    {
        CurrentProgress = progress;
    }));
},
new ExecutionDataflowBlockOptions
{
    TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
});
transformBlock.Completion.Wait();