进度条未在WPF UI上更新

时间:2013-12-31 12:18:25

标签: c# wpf multithreading dependency-properties dispatcher

我使用下面的代码来显示WPF(MVVM)应用程序中的进度条状态,该应用程序基本上是在一个线程上,但是如果我在调度程序线程上,它的状态没有在UI上更新。

使用Thread:

Main()
    {


      Thread LoadApp = new Thread(MyData);
            LoadApp.Start();
    }

public void MyData()
{
        ProgMessage = "10%";
        ProgValue = 10;
        var varAction = new List<Func<object>>();
        varAction .Add(() => (MainViewMode1)ViewModel1.ViewModel1);
        varAction .Add(() => (MainViewMode2)ViewModel2.ViewModel2);
        varAction .Add(() => (MainViewMode3)ViewModel3.ViewModel3);

        foreach (var action in varAction )
        {
            var obj = action();
            ProgressMessage = // My message from VM
            ProgressBarValue += // My message valuefrom VM;
        }

}

这工作正常但是从现在开始我使用依赖属性我得到了交叉线程异常(STA)所以我将代码更改为:

Main()
{

           Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
            {
               MyData();

            }));

}



 public void MyData()
    {
            ProgMessage = "10%";
            ProgValue = 10;
            var varAction = new List<Func<object>>();
            varAction .Add(() => (MainViewMode1)ViewModel1.ViewModel1);
            varAction .Add(() => (MainViewMode2)ViewModel2.ViewModel2);
            varAction .Add(() => (MainViewMode3)ViewModel3.ViewModel3);

            foreach (var action in varAction )
            {
                var obj = action();
                ProgressMessage = // My message from VM
                ProgressBarValue += // My message valuefrom VM;
            }

   }

现在我摆脱了线程错误,但它没有更新UI上的进度条状态?

有任何线索吗?

1 个答案:

答案 0 :(得分:6)

在放弃对调度程序线程的控制之前,UI元素不会更新。通常你会通过返回事件循环来做到这一点,这通常意味着只返回。但是如果你在循环中有大量的工作要做,那那就不太实际了。所以一般来说,你不希望在UI线程上做这种工作。

当你说你“使用依赖属性”时,你的意思是你的ViewModel类的属性是依赖属性吗?您可能想重新考虑这一点,因为它会阻止多线程使用。如果您将它们设为普通的非自动属性,您仍然可以使用数据绑定将它们连接到UI。要在属性更改时让UI更新,您需要实现INotifyPropertyChanged,并使您的属性引发更改通知,然后您将发现您能够在工作线程上工作在您的第一个示例中,如果您将这些属性绑定到UI,您将不会获得线程异常,并且它将更新正常。

(WPF的数据绑定系统检测绑定属性何时在UI线程以外的线程上发生更改,并自动安排从正确的线程更新UI。但是如果要取得,则无法在VM中使用DP这样做的好处,因为DPS具有线程亲和力。)

其他人提出的DoEvents样式方法确实有效,但它有问题,我避免使用它。它支持重入 - 如果用户以导致事件处理程序运行的方式与您的应用程序交互,它们将在对Dispatcher.PushFrame的调用内运行。这不一定是个问题,但很容易搞得一团糟。

另一种方法是在工作线程上进行工作,但是使用Dispatcher.Invoke来调用更新ProgressMessageProgressBarValue属性的方法。这样,您的工作发生在工作线程上(让UI线程可以自由更新),但您将在UI线程上更新这些DP,这样可以避免错误。