立即更新绑定的WPF属性

时间:2011-11-15 11:02:58

标签: wpf multithreading dispatcher

我在窗口上有一个文本框,用于报告操作的状态和/或成功。它绑定到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)。

3 个答案:

答案 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();
    ...