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 !
| Suivant > |
|---|





