理解为什么TPL任务可以使用OUT FromCurrentSynchronizationContext更新UI



我正在VS2012中做一些TPL,使用MVVM进行WPF。我有一个问题,我想我知道答案,但我想确定。考虑一下这个片段:

TaskCanceller = new CancellationTokenSource();
TaskLoader = Task<object>.Factory.StartNew(() =>
{
//Test the current query
DataRepository dr = new DataRepository(DataMappingSelected);
string test = dr.TestMappingConnection();
if (test.IsNotNullEmpty())
throw new DataConnectionException(test);
//Create the CSV File
DataQueryCsvFile CsvFile = new DataQueryCsvFile();
CsvFile.FileName = IO.Path.GetFileName(FilePath);
CsvFile.FilePath = IO.Path.GetDirectoryName(FilePath);
CsvFile.DataMapping = DataMappingSelected;
CsvFile.DataBrowserQuery = DataQueryHolder;
//Allow for updates to the UI
CsvFile.PageExportComplete += (s, e) =>
{
if (TaskCanceller.IsCancellationRequested)
(s as DataQueryCsvFile).IsSaveCancellationRequested = true;
StatusData = String.Format("{0} of {1} Complete", e.ProgressCount, e.TotalCount);
StatusProgress = (100 * e.ProgressCount / e.TotalCount);
};
CsvFile.SaveFile();
return CsvFile;
});

我有一个类DataQueryCsvFile。它的目的是基于传递的一组查询参数创建一个CSV文本文件,这些参数的结果可能非常大。因此,导出会对查询生成的表进行"分页",这样就不会占用用户的内存。它的成员中有一个名为PageExportComplete的事件,每当"页面"被写入文件时(比如一次1000条记录),就会调用该事件。下面的代码使用此事件更新UI上的进度指示器。

进度指示器(StatusData和StatusProgress)在VM中声明,并带有相应的通知,以使视图知道它们何时更改。例如:

public string StatusData
{
get { return _StatusData; }
set { NotifySetProperty(ref _StatusData, value, () => StatusData); }
}
private string _StatusData;

这是我的问题——事实上,这非常有效。但是为什么我没有声明任务通过ContinueWith中的UI线程(FromCurrentSynchronizationContext)运行或更新呢。

是因为MVVM模式吗?换言之,因为正在更新的属性是VM的本地属性,并且因为它们有更新视图的通知,并且因为通过绑定失去耦合,所以它起作用了吗?或者,由于这种情况,我只是很幸运,我应该通过声明ContinueWith来更新UI线程的进度?

UI related stuff can only be updated from UI thread虽然绑定到UI的任何CLR属性都可以从后台线程更新,但它们不存在线程相关性问题。

就像您在示例中发布的一样,you are only updating View model properties from background thread which is perfectly fine,但如果您是try updating Progress bar text directly, it will fall miserably,因为progressBar是UI组件,只能从UI线程更新。


假设您已将TextBlock绑定到ViewModel类中的Name属性:

<TextBlock x:Name="txt" Text="{Binding Name}"/>

如果你尝试直接更新文本,你会得到著名的线程亲和性问题:

Task.Factory.StartNew(() => txt.Text = "From background");

但如果您尝试更新ViewModel Name属性,它将正常工作,因为没有UI内容可以从后台线程访问:

ViewModelClass vm = DataContext as ViewModelClass;
Task.Factory.StartNew(() => vm.Name = "From background");

最新更新