在我的ViewModel中使用Dispatcher是错误的吗?

我正在转换一个聊天解析器,用于我在c#winforms中编写的游戏到wpf,主要是为了更好地处理MVVM和wpf。 这是我如何设置项目的故事

查看 :现在它只是一个简单的ListBox,ItemSource绑定到我的viewmodels可观察聊天集合

型号 :我有多个可以一次登录的角色,每个角色都有一个聊天类。 聊天课开始一个后台工作人员从游戏中抓取和下一行聊天,然后使用此行触发一个名为IncomingChat的事件。

public event Action IncomingChat; 

我正在使用后台工作人员在我的backgroundworkers progresschaged事件中触发事件,因为当我使用计时器时,我一直遇到线程问题。 起初我通过将我的Timer更改为DispatchTimer来纠正这个问题,但这对我来说在我的模型中有一个DispatchTimer似乎不对。

ViewModel :由于我有多个字符,我正在创建多个ChatViewModel。 我将一个字符传递给ChatViewModels构造函数并订阅Chat事件。 我创建了一个ObservableColleciton来在收到此事件时保留我的聊天行。 现在,当我尝试将我从聊天事件中收到的行添加到我的observablecollection时,我在viewModel上收到了一个线程问题。

我通过使我的viewmodels传入聊天事件处理程序看起来像这样来解决这个问题

 public ObservableCollection<Game.ChatLine) Chat {get; private set;} void Chat_Incoming(Game.ChatLine line) { App.Current.Dispatcher.Invoke(new Action(delegate { Chat.Add(line) }), null); } 

但这对我来说并不合适。 虽然它有效,但在我的viewmodel中使用Dispatcher对我来说似乎不合适。

虽然它有效,但在我的viewmodel中使用Dispatcher对我来说似乎不合适。

这不是一种完全不合理的方法,也是许多人采取的方法。 就个人而言,如果您正在使用WPF(或Silverlight 5)并且可以访问TPL,我更愿意使用TPL来处理这个问题。

假设您的ViewModel是在UI线程上构建的(即:通过View,或者响应View相关事件),几乎总是IMO,您可以将其添加到构造函数中:

 // Add to class: TaskFactory uiFactory; public MyViewModel() { // Construct a TaskFactory that uses the UI thread's context uiFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()); } 

然后,当你收到你的活动时,你可以用它来整理它:

 void Chat_Incoming(Game.ChatLine line) { uiFactory.StartNew( () => Chat.Add(line) ); } 

请注意,这与原始版本略有不同,因为它不再阻塞(这更像是使用BeginInvoke而不是Invoke )。 如果您需要阻止它直到UI完成处理消息,您可以使用:

 void Chat_Incoming(Game.ChatLine line) { uiFactory.StartNew( () => Chat.Add(line) ).Wait(); } 

View Model是进行线程同步的好地方。 从模型中删除DispatcherTimer并让VM处理它。

我喜欢里德的回答,并同意你对使用Dispatcher不对的担忧。 您的VM引用了App ,在我看来,它是对UI工件(或控件)的引用。 请改为使用Application或者更好地将正确的Dispatcher实例注入到VM中,这样就无需在UI线程中实例化VM。