2011-09-19 12 views
17

Estoy convirtiendo un analizador de chat para un juego que juego que escribí en C# winforms en wpf, principalmente para obtener un mejor manejo en MVVM y wpf. Aquí está un funcionamiento abajo de la forma en que he establecido mi proyecto¿Es incorrecto utilizar el despachador dentro de mi ViewModel?

Ver: Por ahora es sólo un simple ListBox con ItemSource con destino a mis ViewModels colección de chat observables

Modelo: que tienen múltiples personajes que pueden iniciar sesión al mismo tiempo 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<Game.ChatLine> IncomingChat; 

estoy usando un trabajador de fondo para desencadenar un evento en mis backgroundworkers caso progresschaged porque cuando yo estaba usando un temporizador seguí recibiendo una cuestión de roscado. Al principio corregí esto cambiando mi Timer a DispatchTimer, pero no me pareció correcto tener un DispatchTimer en mi modelo.

ViewModel: Como tengo varios caracteres, estoy creando varios modelos de ChatView. 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.

llegué alrededor de esto haciendo mis ViewModels controlador de eventos de chat entrante parezca tan

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

Esto no se siente bien para mí, sin embargo. Aunque funciona, el uso de Dispatcher en mi modelo de vista como este parece fuera de lugar para mí.

Respuesta

37

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

Esto 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.

Asumiendo que su modelo de vista se construye en el hilo de interfaz de usuario (es decir: por la vista, o en respuesta a una vista evento relacionado), que es el caso casi siempre la OMI, se puede añadir 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()); 
} 

Entonces, cuando llegue a su evento, usted puede utilizar este para ordenar que:

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

Tenga en cuenta que esto es ligeramente distinto del original, puesto que ya no es el bloqueo (esto es más como el uso BeginInvoke en lugar de Invoke). Si necesita este para bloquear hasta que la interfaz de usuario termina de procesar el mensaje, puede utilizar:

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

El SynchronizationContext es el que se debe usar. Este patrón se usa en todo momento, por lo que el código de enhebrado se puede escribir genéricamente y seguir estando disponible ya sea que esté haciendo winforms, wcf, wpf, wf o ASP.NET (estoy asumiendo el último). – Will

+1

@Will: El OP estaba usando WPF (en el texto), pero sí, esto funcionará para cualquier técnico, por eso me gusta tanto ... –

+1

+1, no me di cuenta de que TPL tenía un planificador que podría usar SynchronizationContext. Mi código todavía usa SynchronizationContext Post/Send directamente. –

0

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

+0

No hay DispatcherTimer en lo anterior; el evento entrante obviamente no es un evento DispatcherTimer, ya que está sucediendo en un hilo de fondo. –

+0

Sí, pero mientras leía el cuerpo de la pregunta, poco usó un DispatcherTimer en el modelo. No mencionó explícitamente que lo retiró más tarde, por lo que veo. –

0

Me encanta la respuesta de Reed, y estoy de acuerdo con sus preocupaciones de que algo no está bien con su uso del Dispatcher. Su VM hace referencia al App, que es, en mi opinión, una referencia a un artefacto (o control) de UI. Utilice Application en su lugar o, mejor aún, inserte la instancia correcta Dispatcher 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.

Cuestiones relacionadas