Actualización de pantalla desde otro hilo de ejecución en C#
Cuando se desarrollan aplicaciones de escritorio es frecuente encontrarse en situaciones en las que es necesario realizar un proceso largo que puede producir que la interfaz se bloquee. En estas situaciones el usuario pierde el control de la aplicación y da la impresión de que está bloqueada. Lo ideal en estos casos es ejecutar el proceso en un hilo de ejecución separado y notificar al interfaz de los pasos que se van dando, de forma que el usuario pueda seguir teniendo acceso al interfaz y reciba además notificaciones sobre la evolución del proceso.
Para hacer esto en .NET se pueden utilizar los Threads, de los que ya he hablado alguna vez, o otros métodos como delegados o llamadas asíncronas, sin embargo hay que tener cuidado a la hora de modificar el interfaz desde estos hilos. Si intentamos modificar cualquier elemento del interfaz, por ejemplo el texto de un control, desde un hilo diferente al principal nos encontraremos con un mensaje como:
InvalidOperationException: Cross-thread operation not valid: Control ‘xxxxxxx’ accessed from a thread other than the thread it was created on.
Este mensaje nos informa de que las modificaciones sobre el interfaz hay que realizarlas desde el mismo hilo, y éste es el problema, que resulta que hemos creado un nuevo hilo precisamente para separar el proceso largo y realizar las notificaciones, y ahora nos obliga a hacer las modificaciones desde el mismo hilo.
Bueno, la solución es sencilla y se basa en utilizar el método Invoke
del que disponen los Controles. Este método acepta un delegado y lo ejecuta en el mismo hilo en el que se creo el control.
Imaginemos que disponemos de un formulario con un campo de texto de nombre
tbMsg
. Para poder actualizar su contenido desde otro hilo debemos crear un método que encapsule la modificación del texto y crear un delegado con la misma firma del método.
// declaración del TextBox private TextBox tbMsg; // actualiza el texto del TextBox private void UpdateText(string msg) { tbMsg.Text = msg; } // delegado con la misma firma del método public delegate void UpdateTextCallback(string msg); |
Una vez hecho esto se pueden realizar modificaciones en el hilo a través de una llamada al método Invoke del control, pasándole un delegado junto con los parámetros.
tbMsg.Invoke(new UpdateTextCallback(this.UpdateText), new object[]{"Texto generado desde otro hilo.”}); |
Si estás interesado en probar más cosas de este tipo, en la versión 2.0 del framework existe también la posibilidad de utilziar la clase BackgroundWorker, especialmente orientada a este tipo de situaciones. También en la versión 2.0 del framework se podrían utilizar los delegados anónimos como una forma de evitar la creación del método, en aquellos casos en los que se disponga de visibilidad directa del control desde el hilo, aunque a mi parecer es una solución bastante menos elegante y mantenible.
Referencias
muy buena explicacion aunque falto un ejemplo de codigo mas completo y variado
de todos modos gracias por la explicacion
Al igual que a ‘c shark’ me parece una buena explicación. La verdad es que he estado un buen rato tratando de solucionar ese problema y aunque casi todos lados me llevaban a lo del ‘.Invoke’ no lo expresaba tan claro y sencillo de entender como aquí.
Es cierto que podría haber más ejemplos o un código completo real, pero la verdad es que con la información que das es suficiente para que al menos a alguien como a mi le funcione a la primera.
Saludos y muchísimas gracias por esta aportación!!
Me ha sido de gran utilidad y he entendido y solucionado el problema gracias a esta esposición.
Muchas gracias de verdad.
perdón, exposición.
Qué tal? gracias por el aporte y tu explicación, relamente buena, pero no alcanzo a entender el como sería para la lectura de dicho textbox, no modificarlo, sino simplemente leer su valor…
Hola José, simplemente tendrías que utilizar tbMsg.Text
Falta un datillO:
Control.Invoke es síncrono, espera a que la función reporne para continuar.
Control.BeginInvoke es asíncrono, no espera a que la función retorne.
http://stackoverflow.com/questions/229554/whats-the-difference-between-invoke-and-begininvoke