¿Es incorrecto usar el Dispatcher dentro de mi ViewModel?

Estoy convirtiendo un analizador de conversación para un juego que juego que escribí en c # winforms en wpf, principalmente para obtener un mejor manejo de MVVM y wpf. Aquí hay un resumen de cómo tengo mi proyecto configurado

Vista : por ahora solo es un simple ListBox con ItemSource vinculado a mi viewmodels colección de chat observable

Modelo : tengo varios personajes que pueden iniciar sesión a la vez y cada personaje tiene una clase de chat. La clase de chat inicia un trabajador de segundo plano que capta y la siguiente línea de chat del juego y dispara un evento llamado IncomingChat con esta línea.

public event Action IncomingChat; 

Estoy utilizando un trabajador de fondo para iniciar un evento en el evento progresswork de mis backgroundworkers porque cuando estaba usando un temporizador seguí recibiendo un problema de subprocesamiento. Al principio corregí esto cambiando mi Timer a DispatchTimer, pero no me pareció correcto tener un DispatchTimer en mi modelo.

ViewModel : dado que tengo varios caracteres, estoy creando múltiples ChatViewModels. Paso un personaje en el constructor ChatViewModels y me suscribo al evento Chat. Creé una ObservableColleciton para contener mis líneas de chat cuando se recibe este evento. Ahora recibo un problema de subprocesamiento en mi viewModel al intentar agregar la línea que recibo de mi evento de chat a mi colección observable.

Lo solucioné haciendo que mi manejador de eventos de chat entrante de viewmodels se viera así

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

Aunque esto no me parece bien. Aunque funciona, el uso de Dispatcher en mi modelo de vista como este parece fuera de lugar para mí.

Aunque funciona, el uso de Dispatcher en mi modelo de vista como este parece fuera de lugar para mí.

Este no es un enfoque completamente irrazonable, y es el enfoque que toman muchas personas. Personalmente, si está usando WPF (o Silverlight 5) y tiene acceso al TPL, prefiero usar el TPL para manejar esto.

Suponiendo que su ViewModel está construido en el hilo de la interfaz de usuario (es decir, por la vista o en respuesta a un evento relacionado con la vista), que es el caso casi siempre de IMO, puede agregar esto a su constructor:

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

Luego, cuando obtenga su evento, puede usarlo para ordenarlo:

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

Tenga en cuenta que esto es ligeramente diferente de su original, ya que ya no está bloqueando (esto es más como usar BeginInvoke lugar de Invoke ). Si necesita bloquear esto hasta que la interfaz de usuario termine de procesar el mensaje, puede usar:

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

Un modelo de vista es un buen lugar para realizar una sincronización de subprocesos. Elimine DispatcherTimer de su modelo y deje que la VM lo maneje.

Me encanta la respuesta de Reed y estoy de acuerdo con sus preocupaciones de que algo no está bien con su uso del Dispatcher . Tu VM hace referencia a la App , que es, en mi opinión, una referencia a un artefacto de UI (o control). Utilice la Application lugar o, mejor aún, inserte la instancia de Dispatcher correcta en su máquina virtual, lo que evita la necesidad de crear una instancia de su máquina virtual en el hilo de la interfaz de usuario.