在我的 ViewModel 中使用 Dispatcher 有错吗?
Posted
技术标签:
【中文标题】在我的 ViewModel 中使用 Dispatcher 有错吗?【英文标题】:Is it wrong to use the Dispatcher within my ViewModel? 【发布时间】:2011-11-20 09:34:30 【问题描述】:我正在将我在 c# winforms 中编写的游戏的聊天解析器转换为 wpf,主要是为了更好地处理 MVVM 和 wpf。这是我如何设置项目的简要说明
查看: 现在它只是一个简单的 ListBox,其中 ItemSource 绑定到我的 viewmodels 可观察聊天集合
型号: 我有多个角色可以一次登录,每个角色都有一个聊天类。聊天类启动一个后台工作者,从游戏中抓取下一行聊天,并使用该行触发一个名为 IncomingChat 的事件。
public event Action<Game.ChatLine> IncomingChat;
我正在使用后台工作者在我的 backgroundworkers progresschaged 事件中触发一个事件,因为当我使用计时器时,我一直遇到线程问题。起初我通过将 Timer 更改为 DispatchTimer 来纠正此问题,但在我的模型中使用 DispatchTimer 对我来说似乎不合适。
视图模型: 由于我有多个角色,我正在创建多个 ChatViewModel。我将一个字符传递给 ChatViewModels 构造函数并订阅 Chat 事件。当收到此事件时,我创建了一个 ObservableColleciton 来保存我的聊天行。现在,当我尝试将我从聊天事件收到的行添加到我的 observablecollection 时,我的 viewModel 上出现了线程问题。
我通过让我的视图模型传入聊天事件处理程序看起来像这样来解决这个问题
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);
但我觉得这不对。虽然它有效,但在我的视图模型中使用 Dispatcher 对我来说似乎不合适。
【问题讨论】:
【参考方案1】:虽然它有效,但在我的视图模型中使用 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();
【讨论】:
SynchronizationContext 是要使用的。这种模式贯穿始终,因此您的线程代码可以通用编写,并且无论您使用的是 winforms、wcf、wpf、wf 还是 ASP.NET(我假设是最后一个),都可以使用。 @Will:OP 使用的是 WPF(在正文中),但是是的,这适用于任何技术,这就是我非常喜欢它的原因...... +1,我没有意识到 TPL 有一个可以使用 SynchronizationContext 的调度程序。我的代码仍然直接使用 SynchronizationContext Post/Send。 @Dan:它非常有用,尤其是在与延续混合时(参见:reedcopsey.com/2010/04/19/…) @Reed,没错;我一直在注册在延续体内调用 SynchronizationContext.Post 的延续。【参考方案2】:视图模型是进行线程同步的好地方。从模型中删除 DispatcherTimer 并让 VM 处理它。
【讨论】:
上面没有 DispatcherTimer - 传入的事件显然不是 DispatcherTimer 事件,因为它发生在后台线程上。 是的,但正如我从问题正文中看到的那样,poco 确实在模型中使用了 DispatcherTimer。据我所知,他没有明确提到他后来删除了它。【参考方案3】:我喜欢 Reed 的回答,并且同意您对使用 Dispatcher
的问题的担忧。您的 VM 引用 App
,在我看来,这是对 UI 工件(或控件)的引用。请改用Application
,或者更好的是,将正确的Dispatcher
实例注入您的VM,这样就无需在UI 线程中实例化您的VM。
【讨论】:
以上是关于在我的 ViewModel 中使用 Dispatcher 有错吗?的主要内容,如果未能解决你的问题,请参考以下文章
ViewModel 中的 MediaElement.play()