2008-10-09 15 views
7

Parece que el ObservableCollection solo admite agregar, quitar, borrar la operación del subproceso de UI, Lanza excepción de no admitir si se opera mediante un subproceso NO UI. Intenté anular los métodos de ObservableCollection, desafortunadamente, encontré muchos problemas. ¿Cualquiera puede proporcionarme una muestra de ObservableCollection que pueda ser operada por varios hilos? ¡Muchas gracias!No se puede operar ObservableCollection en varios subprocesos

+1

Salida [este mensaje] (http://kentb.blogspot.com/2008/01/cross-thread-collection-binding-in-wpf_19.html) en mi blog. –

+0

El siguiente enlace tiene una solución que le permite enlazar a la colección desde cualquier subproceso de interfaz de usuario y modificarlo desde cualquier subproceso: http://www.codeproject.com/Articles/64936/Multithreaded-ObservableImmutableCollection – Anthony

Respuesta

7

Usando el enlace proporcionado por Kent, podría utilizar el siguiente código para modificar una colección a través de hilos:

while (!Monitor.TryEnter(_lock, 10)) 
{ 
    DoEvents(); 
} 

try 
{ 
    //modify collection 
} 
finally 
{ 
    Monitor.Exit(_lock); 
} 

Sin embargo, si lo que buscas para modificar la colección en su hilo original puede probar a usar una devolución de llamada a su subproceso de interfaz de usuario. Que normalmente hago algo como esto:

this.Dispatcher.Invoke(new MyDelegate((myParam) => 
{ 
    this.MyCollection.Add(myParam); 
}), state); 
+0

Por los sonidos del original publicar, (s) que quiere una implementación de ObservableCollection que se puede utilizar desde cualquier tema sin tener en cuenta el enlace de la interfaz de usuario.Puede ser muy útil en su capa de negocios, por ejemplo. Por supuesto, podría haber entendido mal el propósito de la pregunta ... –

+0

Además, Mark, no necesariamente sé si el uso de BeginInvoke es la mejor idea, porque no se garantiza absolutamente el orden de los artículos. Invoke() es el camino a seguir. –

+0

Actualicé mi respuesta para reflejar ambas posibles respuestas. Gracias Kent. –

3

Has básicamente tiene que invocar o BeginInvoke hacia el hilo de interfaz de usuario para hacer esas operaciones.

Public Delegate Sub AddItemDelegate(ByVal item As T) 

Public Sub AddItem(ByVal item As T) 
    If Application.Current.Dispatcher.CheckAccess() Then 
     Me.Add(item) 
    Else 
     Application.Current.Dispatcher.Invoke(Threading.DispatcherPriority.Normal, New AddItemDelegate(AddressOf AddItem), item) 
    End If 
End Sub 
3

Personalmente, encuentro que el estilo de respuesta de Bob es más fácil de usar que el estilo de respuesta de Mark. Estos son los fragmentos C# WPF para hacer esto:

  1. en el constructor de la clase, obtener el despachador actual a medida que crea sus colecciones observables. Porque, como señaló, las modificaciones necesarias para se deben realizar en el thread original, que puede no ser el principal hilo GUI. So Application.Current.Dispatcher no es correcto, y no todas las clases tienen un this.Dispatcher.

    _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; 
    _data = new ObservableCollection<MyDataItemClass>(); 
    
  2. Utilice el despachador para invocar sus secciones de código que necesita el hilo original.

    _dispatcher.Invoke(new Action(() => { _data.Add(dataItem); })); 
    

Que debe hacer el truco para usted. Aunque hay situaciones, puede preferir . BeginInvoke en lugar de . Invoque.

2

Es posible que desee investigar mi respuesta a esto tal vez, pero please note que el código vino de aquí y no se puede acreditar a mí. Aunque traté de implementarlo en VB. : Original Site

Lo he usado para llenar un cuadro de lista WPF de una clase que tiene un ObservableCollectionEx poblado asincrónicamente desde una base de datos de acceso. Funciona.

public class ObservableCollectionEx<T> : ObservableCollection<T> 
{ 
    // Override the event so this class can access it 
    public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler  
CollectionChanged; 

    protected override void OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
    { 
    // Be nice - use BlockReentrancy like MSDN said 
    using (BlockReentrancy()) 
    { 
    System.Collections.Specialized.NotifyCollectionChangedEventHandler eventHandler = CollectionChanged; 
    if (eventHandler == null) 
     return; 

    Delegate[] delegates = eventHandler.GetInvocationList(); 
    // Walk thru invocation list 
    foreach (System.Collections.Specialized.NotifyCollectionChangedEventHandler handler in delegates) 
    { 
     DispatcherObject dispatcherObject = handler.Target as DispatcherObject; 
     // If the subscriber is a DispatcherObject and different thread 
     if (dispatcherObject != null && dispatcherObject.CheckAccess() == false) 
     { 
      // Invoke handler in the target dispatcher's thread 
      dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, handler, this, e); 
     } 
     else // Execute handler as is 
      handler(this, e); 
    } 
    } 
} 
} 
Cuestiones relacionadas