2011-02-10 17 views
10

En mi WPF intento separar la lógica de mis clases de cualquier información relacionada con la interfaz y solo proporciono las propiedades de ObservableCollection para el enlace.Separación verdadera del código y la presentación al utilizar el Dispatcher

El problema es que cuando accedo a esos OC enlazados de otros hilos, debo hacerlo a través del despachador. El resultado es que estoy obligado a agregar muchas llamadas Dispatcher.Invoke(), ocultas dentro de mis clases, cada vez que uno de los métodos intenta actualizar los OC.

¿Cómo puedo hacer eso de una manera más limpia y separada, para que las llamadas del operador sean abstraídas de mis métodos?

Respuesta

2

Opción 1

Creo que usted debe buscar en una mejor separación de su código utilizando el patrón MVVM, si no está familiarizado con él, le recomiendo ver el following video ya que explica exactamente lo estas buscando.

Específicamente, sin embargo, en su caso, usted debe tener el modelo de clase con la recogida periódica (por ejemplo Lista) en la que se hace todo el trabajo en las roscas. Su ViewModel debe contener los ObservableCollections y conectar sin apretar con las colecciones que existen en el modelo, por ejemplo, puede optar por suscribirse a través de un evento de su ViewModel a una cierta lógica de actualización en su modelo. TODAVÍA tendrá que usar Dispatcher para actualizar el OC, pero solo tendrá que hacerlo una vez.

Opción 2

su lugar, puede simplemente utilizar la solución descrita here. Básicamente, creó una nueva clase derivada de OC que le permite enviar cambios desde el código automáticamente sin necesidad de actualizar el despachador usted mismo.

+1

1 debido a que hizo referencia maravilloso video en MVVM de Jason :). Creo que votaré cada respuesta que contenga un enlace. No puedo hacer suficiente hincapié en lo bueno que es. Bueno, y tu respuesta es correcta. – Anvaka

0

me temo que va a tener que esperar a la próxima versión de WPF

De this post:

Unos pepitas podemos esperar ver en la próxima versión de WPF incluyen:

  • Alojamiento del contenido Silverlight con el nuevo elemento SilverlightHost, sin problemas de espacio aéreo (la imposibilidad de superponer el contenido WPF sobre el contenido nativo de Windows hWnd)
  • general una mejor gestión del espacio aéreo con contenido basado en CVent nativa organizada como el WebBrowser, HwndHost y WindowsFormsHost
  • Activación de la notificación de unión y el cambio de las colecciones que se crean en un subproceso de fondo
  • Una mejor integración con la virtualización de la interfaz de usuario
  • integración del control de la cinta
  • Y más
3

no tengo una bala de plata. Pero si está seguro y listo para asumir la responsabilidad de la delegación de IU implícita, siempre puede heredar de ObservableCollection, anular los métodos y enviar todas las solicitudes a la IU.

Pero el siguiente código hace que me da miedo:

// somewhere in thread pool: 
for(int i = 0; i < 1000; i++) 
{ 
    _dispatcherAwareCollection.Add(i); 
} 

Parece inocente, pero bajo los bloques que la campana de llamada hilo 1000 veces. Las alternativas pueden ser sus métodos específicos BulkXXX(), que retrasarán la notificación hasta que se procesen todos los elementos. Esta solución tampoco es perfecta, ya que quería una abstracción que le permitiera intercambiar colecciones sin problemas, pero los métodos BulkXXX() son muy específicos de la nueva colección.

+0

se olvidó de esta opción :) 1 –

0

Use SynchronizationContext en lugar de Dispatcher. SynchronizationContext es una característica común para la sincronización de hilos en .NET, mientras tanto, Dispatcher se desarrolló intencionalmente para WPF.

1

Bueno, podría escribir usted mismo un AsyncObservableCollection, si sabe cómo escribirlo en hilo. Luego puede encapsular las llamadas Dispatcher. El problema es que no usaría el estándar ObservableCollection entregado dentro de .Net - Framework. Aumentaría el riesgo de errores en su aplicación.

Otra opción sería implementar un WrapperClass, que contiene y expone un ObservableCollection para el enlace y tiene métodos para modificar la colección.


public class WrapperClass<T> 
{ 
    public ObservableCollection<T> Collection {get; set;} 

    public void Add(T item) 
    { 
     //do your dispatcher magic here 
    } 
    ... 
} 

Para modificar la colección, implementa los métodos en ella. El problema aquí es que no hay garantía de que otros usen estos métodos también.

2

El enfoque común es tener una propiedad Dispatcher en su modelo de vista (probablemente en una clase base para todos los modelos de vista) que se puede inyectar fuera. Está bien tenerlo en un modelo de vista porque el modelo de vista DEBERÍA tener en cuenta los conceptos de la interfaz de usuario, pero no debería tener en cuenta una vista en particular (diseño, controles, etc.) y ciertamente no debería tener una referencia a la vista.

Lo que puede hacer es facilitar el envío de su código al hilo Dispatcher creando un ayudante o un servicio que abstraerá el despachador. Por ejemplo, puede crear un ayudante así:

public class AsyncHelper 
{ 
    public static void EnsureUIThread(Action action) 
    { 
     if (Application.Current != null && !Application.Current.Dispatcher.CheckAccess()) 
     { 
      Application.Current.Dispatcher.BeginInvoke(action, DispatcherPriority.Background); 
     } 
     else 
     { 
      action(); 
     } 
    } 
} 

Y cada vez que necesita para actualizar su colección observable, que se envuelva código en ese método de ayuda:

AsyncHelper.EnsureUIThread(() => 
{ 
    // Update you observable collections here 
}); 

O bien, puede ir más allá y use AOP (por ejemplo, PostSharp) para especificar declarativamente (utilizando atributos) que un método debe ejecutarse en el hilo de la interfaz de usuario.

Y, por último, tenga en cuenta que debe enviar solo actualizaciones de la colección al hilo de la interfaz de usuario. Las propiedades habituales se pueden actualizar de forma segura desde un hilo de fondo. Las actualizaciones se enviarán al subproceso de interfaz de usuario automáticamente mediante el mecanismo de enlace. Probablemente en futuras versiones de WPF también se admitirán actualizaciones de una colección de un hilo de fondo.

+0

He-he, no recuerdo dónde vi algo similar ... +1 – Anvaka

0

Probablemente desee utilizar algo como MTObservableCollection. Lo he usado en un proyecto y funcionó fantásticamente. Básicamente, hace todo el trabajo de envío por usted cuando se produce el evento de cambio de colección, al analizar el hilo del que se le asignó el controlador y enviarlo en consecuencia.

El artículo está bien vale la pena leer, incluso si usted no planea tomar esta opción.

0

que tienen una extensión para esto:

public static class DispatcherInvoker 
{  

    public static void AddOnUI<T>(this ICollection<T> collection, T item) 
    { 
     Action<T> addMethod = collection.Add; 
     Application.Current.Dispatcher.BeginInvoke(addMethod, item); 
    } 
} 

EDIT: lo robé a un puesto de stackoverflow, pero se olvidó de la que se

0

yo creo que hay mucho de acoplamiento si es necesario pensar en enhebrado en su capa de modelo.

Lo que debe hacer es no conectar su modelo directamente a la interfaz gráfica de usuario. Como han dicho otros, use una capa intermedia (MVVM).

Esto significa que deja que su capa de MVVM responder a las notificaciones de cambio de su colección observable. Es la capa MVVM la que decide si estas notificaciones se deben pasar a la GUI y cómo. Consulte here para obtener una forma de reducir la frecuencia de actualización de la GUI para mantenerla utilizable.

En resumen: Siga usando ObeservableCollection en su capa de modelo si lo desea, pero no lo use directamente en el enlace GUI. Permita que otra capa reciba las notificaciones y controle la actualización de la GUI.

Cuestiones relacionadas