这是错误的用我的ViewModel内的调度? [英] Is it wrong to use the Dispatcher within my ViewModel?

查看:144
本文介绍了这是错误的用我的ViewModel内的调度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我转换聊天解析器游戏我玩,我在C#中的WinForms到WPF写的,主要是刚拿到的MVVM和WPF更好地处理。下面是一个运行下来我如何我的项目设置

查看
现在它只是一个的ItemSource简单列表框绑定到我的ViewModels观察的聊天集合

型号
我是在一个可同时登录多个字符,每个角色都有一个聊天类。聊天类开始,抓住一个背景工人和聊天的下一行,从游戏和火灾了与这条线称为IncomingChat的事件。

 公共事件动作< Game.ChatLine> IncomingChat;

我使用的是后台工作,因为当我使用一个计时器我一直得到一个线程问题火在我的backgroundworkers progresschaged事件的事件。起初,我纠正了这个改变我的定时器到DispatchTimer,但这似乎并没有权利,我有一个DispatchTimer在我的模型。

视图模型
因为我有多个字符我创建多个ChatViewModels。我传递一个字符到ChatViewModels构造函数和订阅聊天事件。我创建了一个ObservableColleciton当接收到此事件举行我的聊天行。现在我对我的ViewModel接收线程问题试图从我的聊天事件添加我收到线我的ObservableCollection时。

我解决这个得到通过我的ViewModels聊天呼入事件处理程序看起来像这样

 公开的ObservableCollection< Game.ChatLine)交流{搞定;私人集;}无效Chat_Incoming(Game.ChatLine线)
{
  App.Current.Dispatcher.Invoke(新动作(代表
  {
    Chat.Add(线)
  }), 空值);
}

这感觉不对我虽然。虽然它的作品,在我的ViewModel这样使用Dispatcher显得格格不入给我。


解决方案

  

虽然它的作品,在我的ViewModel这样使用Dispatcher显得格格不入给我。


这并不是一个完全不合理的做法,而且是很多人采取的做法。就个人而言,如果你使用WPF(或Silverlight 5),并有机会获得TPL,我preFER使用TPL来处理这个问题。

假设你的视图模型构建在UI线程(即:经查看,或响应查看相关的事件),是哪种情况几乎总是IMO,你可以添加到您的构造函数:

  //添加到类:
TaskFactory uiFactory;公共MyViewModel()
{
    //构造使用UI线程的上下文中的TaskFactory
    uiFactory =新TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
}

然后,当你得到你的活动,你可以用它来元帅吧:

 无效Chat_Incoming(Game.ChatLine线)
{
    uiFactory.StartNew(()=> Chat.Add(线));
}

请注意,这是的的比你原来的不同,因为它不再阻塞(这更像是使用的BeginInvoke 而不是调用)。如果您需要这阻塞,直到UI处理完邮件,你可以使用:

 无效Chat_Incoming(Game.ChatLine线)
{
    uiFactory.StartNew(()=> Chat.Add(线)).Wait();
}

I am converting a chat parser for a game i play that i wrote in c# winforms over to wpf, mainly just to get a better handle on MVVM and wpf. Here is a run down of how i have my project set up

View: For now its just a simple ListBox with ItemSource bound to my viewmodels observable chat collection

Model: I have multiple characters that can be logged in at one time and each character has a chat class. The chat class starts a background worker that grabs and next line of chat from the game and fires off an event called IncomingChat with this line.

public event Action<Game.ChatLine> IncomingChat;

I'm using a background worker to fire an event in my backgroundworkers progresschaged event because when i was using a timer i kept getting a threading issue. At first i corrected this by changing my Timer to a DispatchTimer, but this didn't seem right to me to have a DispatchTimer in my model.

ViewModel: Since i have multiple characters i am creating multiple ChatViewModels. I pass a character into the ChatViewModels constructor and subscribe to the Chat event. I create a ObservableColleciton to hold my chat lines when this event is received. Now I'm receiving a threading issue on my viewModel when trying to add the line i receive from my chat event to my observablecollection.

I got around this by making my viewmodels incoming chat event handler look like so

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);
}

This doesn't feel right to me though. Although it works, using Dispatcher in my viewmodel like this seems out of place to me.

解决方案

Although it works, using Dispatcher in my viewmodel like this seems out of place to me.

This isn't a completely unreasonable approach, and is the approach that many people take. Personally, if you're using WPF (or Silverlight 5), and have access to the TPL, I prefer to use the TPL to handle this.

Assuming your ViewModel is constructed on the UI thread (ie: by the View, or in response to a View related event), which is the case nearly always IMO, you can add this to your constructor:

// Add to class:
TaskFactory uiFactory;

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

Then, when you get your event, you can use this to marshal it:

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

Note that this is slightly different than your original, since it's no longer blocking (this is more like using BeginInvoke instead of Invoke). If you need this to block until the UI finishes processing the message, you can use:

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

这篇关于这是错误的用我的ViewModel内的调度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆