martes, 31 de marzo de 2009

WPF y threading

Tras muchos años de desarrollo prácticamente exclusivo para entorno web, el proyecto basado en Surface en el que estoy trabajando actualmente ha hecho que algunas de las palabras que tenía archivadas en las zonas más oscuras de mi cerebro hayan tenido que ser recuperadas. Una de ellas era threading.

El elemento a desarrollar era teóricamente sencillo. Un flujo en segundo plano que actualizase el mapa de Virtual Earth que tenía en primer plano. Lo primero que tenía que hacer es ejecutar un flujo más o menos de la siguiente manera:

Threading.MapController controller = new Threading.MapController();
controller.Container = this;
App.ControllerThread = new Thread(new ThreadStart(controller.Run));
App.ControllerThread.Start();

Después, a la hora de implementar el método Run de la clase MapController, lo primero que se me podía ocurrir era algo similar a lo siguiente:

public void Run()
{
   while (true)
   {
      Thread.Sleep(100);
      this.Container.RefreshMap();
   }
}

Independientemente de la utilidad de este flujo y de las maneras de implementarlo me llamó la atención una excepción que saltaba siempre:

The calling thread cannot access this object because a different thread owns it.

Al parecer, desarrollando sobre WPF no se puede acceder a objetos que están controlados por un flujo distinto al actual. En este caso, desde el flujo que hemos lanzado en segundo plano se intenta acceder al objeto contenedor del mapa que, obviamente, está controlado por el flujo principal.

Tras un poco de investigación descubrí una manera muy simple de hacer lo que pretendía hacer. Aquí os dejo el trozo de código que resolvió el entuerto.

public void Run()
{
   while (true)
   {
      Thread.Sleep(100);
      this.Container.Dispatcher.Invoke(
         System.Windows.Threading.DispatcherPriority.Normal,
         new Action(delegate()         
            {
                this.Container.RefreshMap();         
            }
      ));  
   }
}

La clave está en utilizar el Dispatcher que tenemos disponible en cada uno de los controles WPF para ejecutar el código que accede a dicho objeto.

0 comentarios: