我正在为我在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上收到了一个线程问题。
我通过使我的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对我来说似乎不合适。
答案 0 :(得分:38)
虽然它有效,但在我的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();
}
答案 1 :(得分:0)
View Model是进行线程同步的好地方。从模型中删除DispatcherTimer并让VM处理它。
答案 2 :(得分:0)
我喜欢里德的回答,同意你对使用Dispatcher
时出现问题的担忧。您的VM引用App
,在我看来,它是对UI工件(或控件)的引用。请改用Application
,或者更好地将正确的Dispatcher
实例注入您的VM,这样就无需在UI线程中实例化您的VM。