2010-11-16 16 views
9

Me doy cuenta de que lo que estoy haciendo es probablemente bastante tonto, pero estoy en el medio de aprender WPF y me gustaría saber cómo hacerlo.WPF C# - Editar un cuadro de lista de otro hilo

Tengo una ventana con un cuadro de lista en él. El listbox se usa para enviar mensajes de estado sobre el programa mientras se está ejecutando. Por ejemplo, "Servidor iniciado", "Nueva conexión en IP #", etc. Quería que esto se actualizase constantemente en segundo plano, así que generé un nuevo hilo para gestionar la actualización de esto, pero cuando hice la llamada para agregar un elemento obtuve el mensaje de error "El hilo que llama no puede acceder a este objeto porque lo posee un hilo diferente".

¿Alguna idea de cómo puedo actualizar el listbox de otro hilo? O en el fondo, etc.

+5

Tengo demasiado sueño para escribir una respuesta completa, pero aquí hay una pista: mire (Google, SO o MSDN) para Dispatcher. Ahora, me voy a la cama. –

Respuesta

24


ACTUALIZACIÓN

Si está usando C# 5 y .NET 4.5 o superior se puede evitar que el otro hilo en el primer lugar usando async y await, por ejemplo:

private async Task<string> SimLongRunningProcessAsync() 
{ 
    await Task.Delay(2000); 
    return "Success"; 
} 

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    button.Content = "Running..."; 
    var result = await SimLongRunningProcessAsync(); 
    button.Content = result; 
} 

Fácil:

Dispatcher.BeginInvoke(new Action(delegate() 
    { 
    myListBox.Items.Add("new item")); 
    })); 

Si está en el código subyacente. De lo contrario se puede acceder al despachador (que está en cada UIElement) usando:

Application.Current.MainWindow.Dispatcher.BeginInvoke(... 

Ok eso es mucho en una línea que me deje ir sobre ella:

Cuando desee actualizar un control de interfaz de usuario que, como se el mensaje dice, tiene que hacerlo desde el hilo de UI. Está construido para pasar un delegado (un método) al subproceso de UI: Dispatcher. Una vez que tenga el Dispatcher, puede Invoke() de BeginInvoke() pasando un delegado para que se ejecute en el subproceso de la interfaz de usuario. La única diferencia es Invoke() solo se devolverá una vez que se haya ejecutado el delegado (es deciren su caso, se ha agregado el nuevo elemento de ListBox), mientras que BeginInvoke() regresará inmediatamente para que el otro subproceso desde el que está llamando pueda continuar (el Dispatcher ejecutará su delegado lo antes posible, lo que probablemente sea inmediato).

me pasó un delegado anónimo anterior:

delegate() {myListBox.Items.Add("new item");} 

zona entre el {} es el bloque de método. Esto se llama anónimo porque solo se crea uno y no tiene un nombre (generalmente se puede hacer esto usando una expresión lambda pero en este caso C# no puede resolver el método BeginInvoke() para llamar). O que podría haber una instancia de un delegado:

Action myDelegate = new Action(UpdateListMethod); 

void UpdateListMethod() 
{ 
    myListBox.Items.Add("new item"); 
} 

entonces pasó lo siguiente:

Dispatcher.Invoke(myDelegate); 

que también se utiliza la clase de acción que está construido en un delegado, pero que podría haber creado su propia - se puede leer más sobre los delegados en MSDN ya que esto va un poco fuera de tema ...

+0

¡Hola, gracias, realmente aprecio la explicación detallada! Soy un estudiante de programación, pero a todos en mi escuela no les gusta C#. Me gusta su explicación más larga ya que me ayuda a comprender lo que está sucediendo. C# y WPF es un lenguaje bastante grande, y hay mucho que puedo sacar directamente de un libro. – cost

+0

Me complace (es v. Similar a otra respuesta que he dado). Pls lo marcan como la respuesta si es :) – markmnl

+3

Me gusta cómo, mirando hacia atrás en esto tres años después, sé realmente lo que un despachador, invocando, delegados, y todas estas otras cosas es (¡y de hecho saben cómo usarlo!) :) – cost

2

Querrá usar Dispatcher.BeginInvoke. Por ejemplo, si el cuadro de lista se llama listbox

// On worker thread 
    listbox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, 
    new Action(delegate() { listbox.Items.Add("Server started") }); 

Esto cola del delegado para ser ejecutado en el subproceso de interfaz de usuario que pertenece a listbox.

+0

Oye, gracias, apuntaste en la dirección correcta, pero al compilador no le gustó lo que dijiste aquí. Dio un error sobre cómo no puede convertir el cálculo de lamba en un delegado. Pude cambiarlo para que funcione, cualquier idea de lo que estaba mal con el tuyo, o simplemente usabas notación. No entiendo – cost

+0

No, yo era demasiado flojo para controlarme con un programa real. –

5

También puede usar el delegado de Acción con métodos anónimos para actualizar el hilo principal del hilo de trabajo. Para obtener más información sobre la clase de acción que podía mirar aquí:

http://msdn.microsoft.com/en-us/library/018hxwa8.aspx

Si desea actualizar el cuadro de lista de múltiples puntos Sugiero establecer explicitally el delegado, sin embargo, si sólo desea actualizar el hilo a una un solo punto en un método con una sola llamada se podría hacer de la siguiente manera:

 listbox.Dispatcher.BeginInvoke(new Action(delegate() 
     { 
      listbox.Items.Add(item); //where item is the item to be added and listbox is the control being updated. 
     })); 

Nota que utilizo la clase de acción, ya que toma encapsula un método que tiene un solo parámetro (en este caso un listItem).

+1

Creo que esto lo hace. La otra respuesta que se me dio me dio un error de compilación acerca de cómo no podía convertir el cálculo de lamba en un delegado. Hice algunas lecturas en el sitio msdn y busqué en Google cómo trabajar con el despachador; Modifiqué el código que me dio y resultó exactamente igual a lo que tienes allí. Respondí hace 13 minutos, debería haber golpeado actualizar, jaja. – cost

Cuestiones relacionadas