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

5

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

transformBlock.LinkTo(notificationBlock);
ActionBlock<object>应该使用当前进度更新我的视图中的进度条,但是界面似乎被冻结了,只有在所有处理完成后才会更新。

我的CurrentProgress属性如下所示:

private double _CurrentProgress;

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

我正在将它绑定到我的视图上,如下所示:

<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 服务,它没有被异步调用。修复了这个问题后,一切都可以正常工作,而不需要使用 Dispatcher 或其他建议的解决方案。

从问题的评论中可以看出,WPF 支持从另一个线程向 UI 线程发布。但我还没有找到任何官方文档关于此的说明。


1
你是如何实例化 TPL 的? - 123 456 789 0
发布相关代码,你可能没有正确地在代码上使用“await”。 - aybe
我喜欢在示例项目中使事情正常工作。我没有TPL示例,但请查看此项目。在此示例项目中使TPL正常运行,然后再转到您的生产项目。http://www.wpfsharp.com/2010/12/29/a-progress-bar-using-wpfs-progress-bar-control-backgroundworker-and-mvvm/ - Rhyous
你正在等待块的完成或类似的操作吗?有多少项需要处理,处理它们需要多长时间? - svick
这应该可以工作,你甚至不需要任务计划程序(WPF 可以处理来自其他线程的绑定属性更新)。也许有一些绑定错误?在调试模式下运行并检查输出中的 System.Windows.Data 错误。 - Eli Arbel
显示剩余2条评论
3个回答

2
首先,您的不需要直接更改属性。
原因是函数将直接运行对象的代码。 这是不允许的,因为它是一个由UIThread拥有的对象。 在自己的线程中运行,他需要通过使用将进度条更新命令发送到UI线程:
ActionBlock<object>(
temp =>
{
    double progress = (double)temp.RecordNumber/(double)TotalRecords;
    Dispatcher.BeginInvoke((Action)(() =>
    {
        CurrentProgress = progress;
    }));
},
new ExecutionDataflowBlockOptions
{
    TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
});

为了实现你想要的功能,首先必须更改这个。

其次(如果此举没有改变任何事情),您必须检查您的UI线程是否在等待TransformBlock的完成。如果您的ICommand(例如,如果您从按钮调用它)不是异步的并且像这样执行:

transformBlock.Completion.Wait();

不太好。因为你的UIThread会等待你的处理结束,直到结束前都不会执行之前的progressBar更新指令。

祝你好运!如果还是无法工作,请发布新的详细信息。


1
就像你所说的,它被吞掉了。只有在调试应用程序(使用Debug -> Exception -> catch exceptions选项)或等待ActionBlock.Completion.Wait()时才能看到它。 - Gerard Walace
这是任何TransformationBlock的常见行为吗? - Yuval Itzchakov
1
抛出的异常将会影响您的TPL网络(至少会终止ActionBlock线程),但是您的主线程(在这种情况下是UIThread)不会直接收到通知,除非您在他和TPL线程之间创建依赖关系(例如等待其中一个)。 如果您对此特定主题感兴趣,可以在此处找到一些有用的信息:异常处理(任务并行库) - Gerard Walace
1
我知道TPL传播异常的方式,不知道Dataflow是否也是这样的 :) - Yuval Itzchakov
1
这里有TPL Dataflow的简单示例。我刚刚运行了其中一个,没有使用try/catch块,确认主线程不知道异常。在ActionBlock方面,.Completion.IsFaulted变为true,它不再处理输入。就是这样。 - Gerard Walace
显示剩余3条评论

2
尝试使用“SendAsync”将您的代码发布到TransformBlock:
foreach (var myObj in ObjList)
{
   await transformBlock.SendAsync(myObj);
}

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接