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

本文关键字:错误 Dispatcher 我的 ViewModel | 更新日期: 2023-09-27 18:09:06

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

<<p> 视图/strong>:现在它只是一个简单的ListBox与ItemSource绑定到我的viewmodels可观察的聊天集合

:我有多个字符,可以在同一时间登录,每个字符有一个聊天类。聊天类启动一个后台工作程序,从游戏中获取下一行聊天内容,并触发一个名为IncomingChat的事件。

public event Action<Game.ChatLine> IncomingChat;

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

ViewModel :因为我有多个字符,我正在创建多个ChatViewModels。我将一个字符传递给ChatViewModels构造函数并订阅聊天事件。我创建了一个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);
}

这对我来说感觉不对。虽然它可以工作,但在我的视图模型中像这样使用Dispatcher对我来说似乎不合适。

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

虽然它可以工作,但在我的视图模型中像这样使用Dispatcher对我来说似乎不合适。

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

假设你的ViewModel是在UI线程上构建的(即:由视图,或响应视图相关事件),这是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();
}

视图模型是执行线程同步的好地方。从模型中删除DispatcherTimer,让VM处理它。

我喜欢Reed的回答,也同意你对Dispatcher使用不当的担忧。您的VM引用了App,在我看来,这是对UI工件(或控件)的引用。使用Application代替,或者更好的是,将正确的Dispatcher实例注入到VM中,这避免了在UI线程中实例化VM的需要。