我使用下面的代码来显示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上的进度条状态?
有任何线索吗?
答案 0 :(得分:6)
在放弃对调度程序线程的控制之前,UI元素不会更新。通常你会通过返回事件循环来做到这一点,这通常意味着只返回。但是如果你在循环中有大量的工作要做,那那就不太实际了。所以一般来说,你不希望在UI线程上做这种工作。
当你说你“使用依赖属性”时,你的意思是你的ViewModel类的属性是依赖属性吗?您可能想重新考虑这一点,因为它会阻止多线程使用。如果您将它们设为普通的非自动属性,您仍然可以使用数据绑定将它们连接到UI。要在属性更改时让UI更新,您需要实现INotifyPropertyChanged
,并使您的属性引发更改通知,然后您将发现您能够在工作线程上工作在您的第一个示例中,如果您将这些属性绑定到UI,您将不会获得线程异常,并且它将更新正常。
(WPF的数据绑定系统检测绑定属性何时在UI线程以外的线程上发生更改,并自动安排从正确的线程更新UI。但是如果要取得,则无法在VM中使用DP这样做的好处,因为DPS具有线程亲和力。)
其他人提出的DoEvents
样式方法确实有效,但它有问题,我避免使用它。它支持重入 - 如果用户以导致事件处理程序运行的方式与您的应用程序交互,它们将在对Dispatcher.PushFrame
的调用内运行。这不一定是个问题,但很容易搞得一团糟。
另一种方法是在工作线程上进行工作,但是使用Dispatcher.Invoke
来调用更新ProgressMessage
和ProgressBarValue
属性的方法。这样,您的工作发生在工作线程上(让UI线程可以自由更新),但您将在UI线程上更新这些DP,这样可以避免错误。