WPF – Traitements asynchrones modifiant les contrôles de l’IHM

L’IHM d’une application WPF est constituée de formulaires et de contrôles qui sont créés par le thread principal de l’application. Le Framework .NET interdisant la modification des propriétés de ces contrôles par un autre thread, il n’est pas possible à un traitement s’exécutant de manière asynchrone de modifier, par exemple, la valeur d’une barre de progression afin d’indiquer à l’utilisateur sa progression.
Ainsi, l’exécution du bloc de code présenté ci-dessous, provoque la levée d’une exception du type InvalidOperationException lorsque le thread exécuté tente de modifier la valeur de la barre de progression :

private void CmdExecuter_Click(object sender, RoutedEventArgs e)
{
    // Traitement asynchrone.
    Action taskAction = new Action(() =>
    {
        int progress = 0;

        while (progress <= 100)
        {
            // Simulation d'un traitement de données.
            System.Threading.Thread.Sleep(150);

            // Progression.
            progress++;

            // Modification de la valeur de la barre de progression.
            PgbTraitement.Value = progress;
        }
    });

    // Exécution du traitement.
    Task.Run(taskAction);
}

La solution à implémenter est la suivante. Chaque contrôle de l’IHM possède une propriété nommée Dispatcher de type System.Windows.Threading.Dispatcher, héritée de la classe DispatcherObject. Elle permet, entre autre, de demander au thread ayant créé le contrôle, d’exécuter un bloc d’instructions (qui modifie la valeur de la barre de progression) au travers d’un délégué. Voici l’algorithme à implémenter :

private void CmdExecuter_Click(object sender, RoutedEventArgs e)
{
    // Traitement asynchrone.
    Action taskAction = new Action(() =>
    {
        int progress = 0;

        // Instanciation d'un délégué.
        Action invokeAction = new Action(() => { PgbTraitement.Value = progress; });

        while (progress <= 100)
        {
            // Simulation d'un traitement de données.
            System.Threading.Thread.Sleep(150);

            // Progression.
            progress++;

            // Modification de la valeur de la barre de progression.
            PgbTraitement.Dispatcher.Invoke(invokeAction);
        }
    });

    // Exécution du traitement.
    Task.Run(taskAction);
}

About: James RAVAILLE

Travaillant avec la plateforme Microsoft .NET depuis 2002, j’alterne les missions de formation et d’ingénierie avec cette plateforme. J’écris ce blog pour transmettre mes connaissances à tout développeur, qu’il soit débutant ou expérimenté.