1. Le chargement asynchrone : pourquoi faire ?
2. Solution proposée
3. Réalisation
3.1 MyViewModel.cs
3.2 MainWindow.cs

Asynchronous WPF Application Source

1. Le chargement asynchrone : pourquoi faire ?

Lorsque l’on crée une application il est souvent plus simple de charger les données de façon synchrone. Bien que plus rapide à mettre en place cette solution présente de forts inconvénients, notamment en terme d’expérience utilisateur. Une interface « freezée » est considérée comme un bug par les utilisateurs qui peuvent alors voir la fenêtre bloquée (ou voir un fond blanc sous Windows Vista et plus.)
Pour parer à ce problème, il est nécessaire de dissocier l’interface graphique du chargement logique des données. Ceci se fait à travers le multithreading et permet d’obtenir un rendu propre et compréhensible par l’utilisateur.

2. Solution proposée

Dans l’exemple que nous allons voir, nous simulons le long chargement d’une liste de « personnes » quand nous cliquerons sur le bouton « Refresh ». Cette liste une fois chargée sera bindée à une DataGrid.

Nous souhaitons qu’au clic et pendant toute la durée du chargement, un voile blanc opaque remplisse la fenêtre. Au centre de cette dernière une barre de progression nous informera de l’avancement du chargement ; elle sera accompagnée d’un label indiquant le nom de l’étape (ex : « 2/5 loaded »).

Chargement en cours

Chargement terminé

3. Réalisation

Comme évoqué précédemment nous allons séparer les données de l’interface graphique. Notre projet sera composé de 4 fichiers :

–       App.xaml : créé par défaut, il détermine notamment le point d’entrée de l’application)
–       MainWindows.xaml : créé par défaut, il s’agit de la fenêtre principale)
–       People.cs : Classe réduite à un attribut (prénom) pour simplifier l’exemple
–       MyViewModel : Classe chargeant les données

3.1. MyViewModel.cs

La classe MyViewModel étend l’interface INotifyPropertyChanged. Le principe est d’instancier un BackgroundWorker qui se chargera de créer le thread nécessaire à l’asynchrone et d’exposer des propriétés en publique (donc accessibles depuis la partie interface graphique de l’application).

Ces propriétés sont les suivantes :

–       IsBusy (indique si le chargement est en cours ou non)
–       LoadingPercentage (indique le % du chargement en cours)
–       LoadingStep (indique l’étape du chargement en cours)

Nous surchargeons la méthode set des propriétés IsBusy et LoadingPercentage pour déclencher un évènement :

public int LoadingPercentage
{
    get { return _loadingPercentage; }
    private set
    {
        if (_loadingPercentage != value)
        {
            _loadingPercentage = value;
            OnPropertyChanged("LoadingPercentage");
        }
    }
}
 
public bool IsBusy
{
    get { return _isBusy; }
    private set
    {
        if (_isBusy != value)
        {
            _isBusy = value;
            OnPropertyChanged("IsBusy");
        }
    }
}

Nous exposons une méthode LoadPeople() qui sera appelée depuis l’interface graphique pour démarrer le chargement.

public void LoadPeople()
{
    IsBusy = true;
    Peoples.Clear();
    _worker.RunWorkerAsync();
}

3.2. MainWindow.cs

3.2.1 Constructeur
Dans le constructeur nous créons l’instance de MyViewModel. On s’abonne à l’évènement PropertyChanged qui nous servira à piloter l’affichage de l’interface graphique.

public MainWindow()
{
    InitializeComponent();
    _model = new MyViewModel();
    _model.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_model_PropertyChanged);
}

3.2.2 Les contrôles
Nous définissons les actions CleanTheGridBtn_Click et Refresh_Click telles que :

private void CleanTheGridBtn_Click(object sender, RoutedEventArgs e)
{
    dataGrid1.DataContext = null;
}
 
private void Refresh_Click(object sender, RoutedEventArgs e)
{
    _model.LoadPeople();
}

Un clic sur le bouton Refresh va lancer le chargement asynchrone via la méthode publique LoadPeople de notre modèle. Quant au bouton CleanTheGrid, ce dernier vide les données actuellement affichées.

3.2.3 Evènement PropertyChanged
L’abonnement à cet évènement permet de mettre à jour l’interface graphique en fonction de l’évolution du chargement. Dans notre modèle, seuls 2 propriétés déclenchent l’évènement. Il s’agit de « IsBusy » et « LoadingPercentage ». Nous obtenons donc le code suivant :

void _model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    switch (e.PropertyName)
    {
        case "IsBusy":
            if (_model.IsBusy)
            {
                //Display ProgressBar
                backgroundMask.Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                //Hide ProgressBar and fill the grid
                backgroundMask.Visibility = System.Windows.Visibility.Hidden;
                dataGrid1.DataContext = _model.Peoples;
            }
 
            break;
 
        case "LoadingPercentage":
            loadingProgressBar.Value = _model.LoadingPercentage;
            loadingSetp.Content = _model.LoadingStep;
            break;
    }
}

La propriété “IsBusy” change de valeur lorsque l’on démarrage un chargement ou que celui-ci vient de se terminer.

La propriété LoadingPercentage change de valeur au cours de l’avancement (0,20,40,60,80 et 100% dans notre exemple), elle permet de mettre à jour la valeur de la progressBar.
Il est à noter que dans certains cas, tels que le chargement d’une table en base de données ne nous permet pas de donner un pourcentage d’avancement. Dans ce cas il suffit de définir la propriété IsIndeterminate à true.

Last modified: 27 July 2011

Author

Comments

Write a Reply or Comment

Your email address will not be published.