我在窗口上有一个文本框,用于报告操作的状态和/或成功。它绑定到ViewModel上的属性。
因此,当用户激活可能需要一段时间的操作时,我不想让他知道该操作已在textBox中启动。问题是他在动作完成之前不会写入文本框。
我想这是因为该主题不会让UI有机会完成它的工作,但不确定。
public class ViewModel : INotifyPropertyChanged
{
private string _report;
public string Report
{
get { return _report; }
set
{
_report = value;
RaisePropertyChanged("Report");
}
}
public void DoHeavyAction()
{
Report += "Heavy action readying";
ReadyHeavyAction();
Report += "Heavy action starting";
var result = DoTheHeavyAction();
if(!result.success)
{
report += "Heavy action failed";
return;
}
Report += result.value;
}
}
在这种情况下,程序滞后2秒,然后整个事情出现:
Heavy action readying
Heavy action starting
Heavy action failed
而不是一个一个地出现。
我可以以某种方式使用调度程序更新视图(注意我使用的是MVVM)。
答案 0 :(得分:2)
是的,这很容易。繁重的操作在一个单独的线程中运行,并将进度报告为
Dispatcher.Invoke((Action)(() => {
Report.value = "Started";
}));
编辑:
在实际情况下,操作由ICommand
激活。从UI调度所述ICommand
,因此它出现在UI线程中。所以问题是UI(及其更新)被繁重的操作阻止。
解决方案是在命令到达时启动一个新线程,然后在那里完成工作。可以使用上面的示例代码更新UI。
这个解决方案有一些替代方法,但是所有这些方法都是在后台线程中启动任务,尽管是隐式的。一个流行且好的方法是使用BackgroundWorker
并更新其ProgressChanged
事件中的状态(请注意,此事件位于UI线程中,因此在这种情况下您不需要使用Dispatcher) 。
答案 1 :(得分:2)
您可以将“繁重的工作”移动到后台任务,以使其脱离UI线程。
var task = Task.Factory.StartNew(() => DoHeavyAction());
Report += "Heavy Action Starting";
task.Wait();
答案 2 :(得分:1)
为什么不使用BackgroundWorker并设置ProgressChanged事件?
示例:
var worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += WorkerReportProgress;
...
private static void WorkerReportProgress(object sender, ProgressChangedEventArgs e) {
report += e.UserState.ToString();
}
并在DoWork中执行类似
的操作private static void DoHeavyAction(object sender, DoWorkEventArgs e) {
var worker = (BackgroundWorker)sender;
worker.ReportProgress(0, "Heavy action readying");
ReadyHeavyAction();
...