Acceso a controles desde subprocesos distintos al que lo creó en Windows Forms
Cuando trabajamos con aplicaciones de Windows Forms, lo habitual es que exista una interactuación entre usuario y aplicación que provoque ciertas actualizaciones dentro de la aplicación.
Dentro de las recomendaciones que debemos tener en cuenta en una aplicación de Windows Forms, está la correspondiente a la actualización de elementos dentro de la interfaz de usuario.
No obstante, esto se complica cuando las tareas son demasiado largas, y ejecutamos por ejemplo un hilo para ejecutar determinadas acciones y refrescar la interfaz de usuario.
Lo habitual es encontrarnos con problemas.
La aclaración a esto que comento, reside en que el formulario de Windows que contiene los elementos de interfaz, se ejecuta en una hebra, y por lo tanto, ésta hebra es la encargada de actualizar sus elementos.
Por eso, si insertamos en un formulario Windows un control button y un control label y ejecutamos por ejemplo this.label1.Text = «Hola»; el control label1 quedará actualizado inmediatamente.
Sin embargo, si ejecutamos una tarea que actualice el control label1, se desencadenará una excepción.
Supongamos por ejemplo, el siguiente código que corresponde con una aplicación de Windows Forms, con un control button y un control label dentro del formulario:
using System; using System.Threading.Tasks; using System.Windows.Forms; namespace WinFormsThreadUpdate { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { new Task(() => this.label1.Text = DateTime.Now.ToString()).Start(); } } }
Esta aplicación en tiempo de ejecución, y una vez pulsado el control button, desencadenará una excepción de tipo InvalidOperationException.
Para resolver este problema, tenemos diferentes aproximaciones.
Aquí voy a poner una de ellas, la que considero más simple y sencilla de abordar.
Por lo tanto, el código anterior que causa un problema en tiempo de ejecución como podemos ver, quedaría resuelto de la siguiente manera:
using System; using System.Threading.Tasks; using System.Windows.Forms; namespace WinFormsThreadUpdate { public partial class Form1 : Form { public Form1() { InitializeComponent(); } public delegate void SetLabelTextCallback(string text); private void button1_Click(object sender, EventArgs e) { var @delegate = new SetLabelTextCallback(SetLabelText); new Task(() => this.label1.BeginInvoke(@delegate, DateTime.Now.ToString())).Start(); } private void SetLabelText(string text) { this.label1.Text = text; } } }
Este ejemplo de código que hace, exactamente lo mismo que el anterior que generaba una excepción, se ejecuta ahora de forma correcta.
Como puedes ver, hemos creado un delegado, que será el encargado de llevar a cabo la acción de actualización a través del hilo principal del formulario Windows.
¡Happy Coding!