Tengo un archivo DLL que se ejecuta un subproceso de trabajo y envía eventos de nuevo a la aplicación - funcionado perfectamente en formas de las ventanas, cambiado a WPF y todo lo dejado de funcionar. He estado golpeando mi cabeza contra una pared de ladrillo durante 4 horas tratando de hacer que esto funcione. Pero la solución con la que terminé, gracias a UI Thread Safe marshalling de Microsoft EnableCollectionSynchronization, ofrece una implementación realmente limpia para resolver esto.
Esta colección amplía ObservableCollection e implementa EnableCollectionSynchronization haciendo que estos objetos se puedan utilizar entre WPF y también trabajadores en segundo plano.
Edición: Microsoft's docs decir lo siguiente, así que voy a asumir que el intercambio de contexto del objeto, no importa.
El parámetro de contexto es un objeto arbitrario que puede usar para obtener información conocida cuando habilita la sincronización de recopilación. El contexto puede ser nulo.
ThreadSafeCollection.cs
using System.Collections.ObjectModel;
using System.Windows.Data;
namespace NSYourApplication
{
/// <summary>
/// This ObservableCollection is thread safe
/// You can update it from any thread and the changes will be safely
/// marshalled to the UI Thread WPF bindings
/// Thanks Microsoft!
/// </summary>
/// <typeparam name="T">Whatever type of collection you want!</typeparam>
public class ThreadSafeCollection<T> : ObservableCollection<T>
{
private static object __threadsafelock = new object();
public ThreadSafeCollection()
{
BindingOperations.EnableCollectionSynchronization(this, __threadsafelock);
}
}
}
Ejemplo WindowViewModel WindowViewModel.cs
namespace NSYourApplication
{
/// <summary>
/// Example View
/// BaseModelView implements "PropertyChanged" to update WPF automagically
/// </summary>
class TestViewModel : BaseModelView
{
public ThreadSafeCollection<string> StringCollection { get; set; }
/// <summary>
/// background thread implemented elsewhere...
/// but it calls this method eventually ;)
/// Depending on the complexity you might want to implement
/// [MethodImpl(MethodImplOptions.Synchronized)]
/// to Synchronize multiple threads to prevent chase-conditions,deadlocks etc
/// </summary>
public void NonUIThreadMethod()
{
// No dispatchers or invokes required here!
StringCollection.Add("Some Text from a background worker");
}
/// <summary>
/// Somewhere in the UIThread code it'll call this method
/// </summary>
public void UIThreadMethod()
{
StringCollection.Add("This text come from UI Thread");
}
/// <summary>
/// Constructor, creates a thread-safe collection
/// </summary>
public TestViewModel()
{
StringCollection = new ThreadSafeCollection<string>();
}
}
}
de uso en un cuadro de lista en un xaml ventana/de control MainWindow.xaml
<ListBox x:Name="wpfStringCollection" ItemsSource="{Binding StringCollection,Mode=OneWay}">
</ListBox>
Entonces, al agregarlos a la colección de elementos en un hilo de fondo ¿se actualizarán los controles en el hilo de la interfaz de usuario? ¿Cómo funciona? – Mark
No, los agrega en el subproceso de despachador, pero con una prioridad más baja y en chucnks más pequeños para que la UI siga siendo receptiva – Bubblewrap
Ya veo, gracias voy a intentarlo – Mark