Si trabajáis con aplicaciones multithreading, probablemente se os presente la ocasión en la que debáis acceder a controles UI creados en el thread principal desde otro thread distinto. Como sabréis una regla de la programación en Windows es que los cambios realizados en los controles UI deben realizarse desde el thread que creó el control. De lo contrario con .net 2.0 obtendríamos una excepción en tiempo de ejecución, pero en versiones anteriores es incluso peor porque los resultados son inesperados.
1 private void bttStart_Click(object sender, System.EventArgs e)
2 {
3 System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(DoWork));
4 thread.Name = "Worker Thread";
5 thread.IsBackground = true;
6 thread.Start();
7 }
8
9 private void DoWork()
10 {
11 int iteration = 0;
12 while (true)
13 {
14 iteration++;
15 textBox1.Text = iteration.ToString(System.Globalization.CultureInfo.InvariantCulture);
16 System.Threading.Thread.Sleep(3000);
17 }
18 }
Sin embargo esto es muy sencillo de evitar. Todos los controles UI tienen una propiedad llamada InvokeRequired que nos indica si el método que intenta hacer la modificación está en un thread distinto del que creo el control. Si se diera el caso tan solo tendríamos que volver a invocar el método en el thread correcto. Veamos como arreglarlo:
1 private void DoWork()
2 {
3 int iteration = 0;
4 while (true)
5 {
6 iteration++;
7 SetText(iteration.ToString(System.Globalization.CultureInfo.InvariantCulture));
8 System.Threading.Thread.Sleep(3000);
9 }
10 }
11
12 public delegate void TextUpdate(string text);
13
14 private void SetText(string text)
15 {
16 if (textBox1.InvokeRequired)
17 {
18 textBox1.Invoke(new TextUpdate(SetText), text);
19 }
20 else
21 {
22 textBox1.Text = text;
23 }
24 }
Como veréis los cambios son muy simples. Hemos creado un método SetText que comprueba si el método que va a modificar la propiedad Text está en un thread distinto, en cuyo caso crea una instancia del delegado TextUpdate que apunta de nuevo al método SetText. Al llamar al método Invoke del TextBox, lo único que hará será volver a llamar a SetText pero esta vez desde el thread en el que se creó el control.