在我正在使用MVVM模式编写的WPF应用程序中,我有一个后台进程可以做到这一点,但是需要从中获取状态更新到UI。
我正在使用MVVM模式,因此我的ViewModel几乎不知道将模型呈现给用户的视图(UI)。
假设我的ViewModel中有以下方法:
public void backgroundWorker_ReportProgress(object sender, ReportProgressArgs e)
{
this.Messages.Add(e.Message);
OnPropertyChanged("Messages");
}
在我看来,我有一个ListBox绑定到ViewModel的Messages属性(List<string>
)。 OnPropertyChanged
通过调用INotifyPropertyChanged
来履行PropertyChangedEventHandler
界面的角色。
我需要确保在UI线程上调用OnPropertyChanged
- 我该怎么做?我尝试了以下内容:
public Dispatcher Dispatcher { get; set; }
public MyViewModel()
{
this.Dispatcher = Dispatcher.CurrentDispatcher;
}
然后将以下内容添加到OnPropertyChanged
方法中:
if (this.Dispatcher != Dispatcher.CurrentDispatcher)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(delegate
{
OnPropertyChanged(propertyName);
}));
return;
}
但这不起作用。有什么想法吗?
答案 0 :(得分:34)
WPF会自动将属性更改封送到UI线程。但是,它不会编组集合更改,因此我怀疑您添加消息会导致失败。
您可以自己手动填充添加内容(请参阅下面的示例),或使用类似this technique之类的内容。我在一段时间后发表了博客。
手动编组:
public void backgroundWorker_ReportProgress(object sender, ReportProgressArgs e)
{
Dispatcher.Invoke(new Action<string>(AddMessage), e.Message);
OnPropertyChanged("Messages");
}
private void AddMessage(string message)
{
Dispatcher.VerifyAccess();
Messages.Add(message);
}
答案 1 :(得分:8)
我真的很喜欢杰里米的回答: Dispatching In Silverlight
要点:
在ViewModel中放置Dispatcher似乎不太优雅
创建动作&lt;动作&gt;属性,将其设置为仅在VM构造函数中运行操作
答案 2 :(得分:3)
本周我有一个类似的场景(MVVM也在这里)。我有一个单独的类做它的事情,报告事件处理程序的状态。正在按预期调用事件处理程序,我可以通过Debug.WriteLine的看到正确的结果。
但是对于WPF,无论我做什么,UI都会在流程完成之前更新。一旦该过程完成,UI将按预期更新。就好像它正在获取PropertyChanged,但等待线程完成,然后立即进行UI更新。
(令我沮丧的是,带有DoEvents和.Refresh()的Windows.Forms中的相同代码就像魅力一样。)
到目前为止,我已经通过在自己的线程上启动进程来解决这个问题:
//hook up event handler
myProcess.MyEvent += new EventHandler<MyEventArgs>(MyEventHandler);
//start it on a thread ...
ThreadStart threadStart = new ThreadStart(myProcess.Start);
Thread thread = new Thread(threadStart);
thread.Start();
然后在事件处理程序中:
private void MyEventHandler(object sender, MyEventArgs e) {
....
Application.Current.Dispatcher.Invoke(
DispatcherPriority.Send,
(DispatcherOperationCallback)(arg =>
{
//do UI updating here ...
}), null);
我不推荐这段代码,因为我仍然试图理解WPF线程模型,Dispatcher如何工作,以及为什么在我的情况下,即使事件处理程序被调用,UI也不会更新,直到进程完成正如所料(按设计?)。但到目前为止,这对我有用。
我发现这两个链接很有用:
答案 3 :(得分:0)
我处理BackgroundWorker.ReportProgress
之外的ViewModel
事件,并将实际的BackgroundWorker
实例和ViewModel
传递到定义async
方法的类中(或多个)。
async
方法然后调用bgWorker.ReportProgress
并传递一个包含委托作为UserState
(作为object
)的类。我作为匿名方法写的代表。
在事件处理程序中,我将它从object
转换回包装器类型,然后调用其中的委托。
所有这些意味着我可以直接从异步运行的代码中编写UI更改,但它只是围绕它进行编码。
这更详细地解释了它:
http://lukepuplett.blogspot.com/2009/05/updating-ui-from-asynchronous-ops.html
答案 4 :(得分:0)
这更像是对已接受答案的延伸,但我是在事件处理中做到的......
using System.Threading;
private void Handler(object sender, RoutedEventArgs e)
{
if (Thread.CurrentThread == this.Dispatcher.Thread)
{
//do stuff to this
}
else
{
this.Dispatcher.Invoke(
new Action<object, RoutedEventArgs>(Handler),
sender,
e);
}
}