WPF France

  • Augmenter la taille
  • Taille par défaut
  • Diminuer la taille

Utilisation du Dispatcher pour mettre à jour l'interface graphique

Envoyer Imprimer PDF
Note des utilisateurs: / 3
MauvaisTrès bien 
Parraléliser différentes parties de votre code est en générale une bonne chose : cela permet de ne pas geler l'interface graphique lorsque l'opération s'effectue et cela permet d'utiliser pleinement le potentiel des processeurs multicores de nos machines modernes. Pour cela le framework .NET nous fournit toute une panoplie d'objet : appel asyncrhone de Delegate (avec la méthode BeginInvoke), utilisation de la classe BackgroundWorker, de Thread ou encore toutes les bonnes choses apparues avec le framework 4.0.

Cependant il ne faut pas tomber dans le travers de vouloir modifier des éléments de notre interface directement depuis nos appels asynchrones sous risque de lever une exception du type : "InvalidOperationException - The calling thread cannot access this object because a different thread owns it.” ou encore "NotSupportedException - This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread."

Dans cet article nous allons voir comment mettre à jour l'interface depuis un Thread à l'aide du Dispatcher.


Définition du problème


Prenons un exemple d'application très simple, nous allons afficher une liste de numéros dans une listbox. Nous allons le faire avec les toutes nouvelles (toutes belles) API de parallélisme de .NET 4.0.
Notre code behind :
public void AddItemsToTheList()
{
Task.Factory.StartNew(() =>
{
  for (int i = 0; i <= 5000; i++)
  {
      _maListbox.Items.Add(i.ToString()); 
  }
}
);
Task.Factory.StartNew(() =>
{
  for (int i = 5001; i <= 10000; i++)
  {
     _maListbox.Items.Add(i.ToString()); 
  }
}
);
}
 
private void Button_Click(object sender, RoutedEventArgs e)
{
AddItemsToTheList();
}

Et voici notre (magnifique) fenêtre :

<Window x:Class="DispatcherExemple.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="{Binding StringMember}" Height="700"
    Width="300">
  <DockPanel>
    <Button DockPanel.Dock="Bottom" Click="Button_Click" Content="Launch !" />
    <ListBox x:Name="_maListbox"  />
  DockPanel>
Window>


En appuyant sur le bouton, une exception est directement levée : impossible d’intervenir sur les éléments de l’interface depuis une opération asynchrone car il faut être sur le Thread de l’UI !

A noter que lorsque vous suivez le magnifique pattern M-V-VM, il est tout à fait possible de modifier les éléments bindés depuis un autre Thread à l’exception des ObservableCollection (vous pouvez les remplacer mais pas ajouter/supprimer des éléments dedans)…

 

La solution : utilisez le Dispatcher

 

Pour pouvoir intervenir sur le Thread de l’UI Microsoft met à notre disposition le dispatcher de l’UI : il est disponible sur les éléments dérivant de DispatcherObject, autant dire tout les contrôles…

Ce Dispatcher nous permet d'e faire des appels directement sur le Thread de l’interface graphique et donc de la mettre à jour sans lever d’exception. Voici le code modifié pour fonctionner présentant les deux possibilités :

public void AddItemsToTheList()
{
Task.Factory.StartNew(() =>
{
  for (int i = 0; i <= 5000; i++)
  {
    //On invoque sur le Thread de l'UI de manière synchrone
    _maListbox.Dispatcher.BeginInvoke(
      (Action)(() => { _maListbox.Items.Add(i.ToString()); })
      ,
      //Quelle priorité utiliser ?
      DispatcherPriority.Background
      );
  }
}
);
Task.Factory.StartNew(() =>
{
  for (int i = 5001; i <= 10000; i++)
  {
    //On invoque sur le Thread de l'UI de manière synchrone
    _maListbox.Dispatcher.BeginInvoke(
      (Action)(() => { _maListbox.Items.Add(i.ToString()); })
      ,
      //Quelle priorité utiliser ?
      DispatcherPriority.Background
      );
  }
}
);
}
 
private void Button_Click(object sender, RoutedEventArgs e)
{
AddItemsToTheList();
}

 

Et voila !

Commentaires (2)
Mise à jour le Lundi, 19 Avril 2010 20:20  

Partagez


Article au hasard

  • Premier article d'une série sur le multitouch. Aujourd'hui nous allons commencer par quelque chose de très simple : faire une petite application consistant en un contrôle utilisateur qui suit un doigt. Nous utilisons .NET 3.5 sans librairie externe. Commentaires (0)...
    Lire la suite...