Ludovic ROLAND

Blog technique sur mes expériences de développeur.

Windows Phone 7 : Créer un ecran de chargement

10 février 2012

Aujourd’hui nous allons voir comment créer un écran de chargement basique pour vos applications Windows Phone 7. Ces écrans sont importants d’un point de vue ergonomie. En effet, ils permettent d’indiquer à l’utilisateur que votre application travaille et qu’elle n’a pas plantée comme il pourrait le croire.

Plusieurs solutions sont possibles :

  • Créer un contrôle utilisateur qui vient se positionner par-dessus la page de l’application le temps que les traitements se fassent en arrière plan.
  • Positionner une simple ProgressBar à un endroit de votre page.

Dans ce billet, c’est la première solution que nous allons étudier.

Le contrôle utilisateur

La première étape consiste donc à créer un contrôle utilisateur qui fera office de page de chargement.

Ici nous allons considérer que nous sommes dans le cadre d’une application au format portrait. Notre contrôle utilisateur fera donc 480*800 pixels. Il contiendra une ProgressBar de type « non déterminée » ainsi qu’un TextBlock qui va permettre à l’utilisateur d’être informé que son application charge.

Pour mon exemple, j’ai appelé mon contrôle utilisateur DownloadScreen. Voici son code XAML :

<UserControl x:Class="TutoDownloadPage.DownloadScreen"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    d:DesignHeight="480" d:DesignWidth="480" Width="480" Height="800">

    <Grid x:Name="LayoutRoot" Width="480" Height="800">
        <ProgressBar x:Name="pgDownload" IsIndeterminate="true" ValueChanged="pgDownload_ValueChanged" />
        <TextBlock Height="30" HorizontalAlignment="Left" Margin="12,342,0,0" Name="txtDownload" Text="Downloading..." VerticalAlignment="Top" Width="456" TextAlignment="Center" />
    </Grid>
</UserControl>

La mise en place

Des attributs ?

L’affichage de ce contrôle utilisateur se passe au niveau de la page de l’application où se passe le traitement. Dans notre cas, il s’agit de la page MainPage.

L’idée est ici d’afficher notre contrôle utilisateur au premier plan tandis qu’en arrière plan notre application traite de l’information. Vous vous en doutez sûrement, mais qui dit premier et arrière plan, implique des threads !

Notre MainPage va donc contenir deux attributs à savoir un BackgroundWorker ainsi qu’une Popup qui nous permettra d’afficher notre contrôle utilisateur au premier plan.

namespace TutoDownloadPage
{
    public partial class MainPage : PhoneApplicationPage
    {
        BackgroundWorker backgroundWorker;
        Popup popup;

        // Constructeur
        public MainPage()
        {
            InitializeComponent();
        }
    }
}

Afficher l’ecran

Maintenant que les attributs sont définis, nous allons afficher notre écran de chargement. Pour ça, nous allons créer une fonction ShowDownloadPage() que nous allons appeler depuis le constructeur.

// Constructeur
public MainPage()
{
    InitializeComponent();

    ShowDownloadPage()
}

Bien évidemment, il faut implémenter cette nouvelle fonction !

C’est assez simple en fait. Il suffit d’instancier notre attribut popup puis de lui dire d’afficher notre DownloadScreen via l’attribut Child. Finalement, on affiche notre contrôle utilisateur en mettant l’attribut IsOpen de notre popup à vrai.

private void ShowDownloadPage()
{
    popup = new Popup();
    popup.Child = new DownloadScreen();
    popup.IsOpen = true;
}

Le traitement des donnees

Maintenant que notre écran de chargement s’affiche, nous pouvons passer à l’implémentation du thread qui s’occupe du traitement des données en arrière plan.

Pour se faire, la première étape consiste à appeler une méthode où le thread est « paramétré » puis lancé.

Dans mon projet, j’ai appelé cette méthode InitBackgroundWorker().

private void ShowDownloadPage()
{
    popup = new Popup();
    popup.Child = new DownloadScreen();
    popup.IsOpen = true;

    InitBackgroundWorker();
}

Comme je le disais, dans cette méthode nous allons « paramétrer » notre thread puis le lancer. Ici le paramétrage consiste à l’instancier puis à déterminer les méthodes à appeler à son lancement ainsi qu’à son arrêt.

private void InitBackgroundWorker()
{
    backgroundWorker = new BackgroundWorker();
    backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
    backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);

    backgroundWorker.RunWorkerAsync();
}

Vous vous en doutez, la prochaine étape consiste à écrire les fonctions backgroundWorker_doWork() et backgroundWorker_RunWorkerCompleted().

La fonction **backgroundWorker_doWork() **est assez basique puisque le but est ici un traitement de données. Le fonctionnement est donc très simple.

void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    try
    {
        Deployment.Current.Dispatcher.BeginInvoke(() =&gt;
        {
            //TRAITEMENT
        });
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Erreur", MessageBoxButton.OK);
    }
}

Finalement, nous devons implémenter notre fonction backgroundWorker_RunWorkerCompleted(). Dans cette méthode, il suffit d’indiquer  à notre popup qu’il n’est plus nécessaire qu’elle s’affiche en mettant sa propriété IsOpen à faux.

void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    popup.IsOpen = false;
}

Attention au menu !

Dans le cas où votre page contient un menu créé grâce à une ApplicationBar, le contrôle utilisateur ne le recouvrera pas. Les utilisateurs pourront alors cliquer sur les items de votre menu tandis que votre application affiche encore la page de chargement.

Une solution pour éviter ce comportement est de créer un attribut booléen initialisé à vrai. Sa valeur passe à faux uniquement quand le thread d’arrière plan a terminé son travail. Quand l’utilisateur cliquera sur un item de votre menu, il suffira alors de vérifier la valeur du booléen pour lui accorder ou non la navigation demandée.

Pour ceux qui en ont besoin, voici, le code complet de l’exemple précédent :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.ComponentModel;
using System.Windows.Controls.Primitives;

namespace TutoDownloadPage
{
    public partial class MainPage : PhoneApplicationPage
    {
        BackgroundWorker backgroundWorker;
        Popup popup;

        // Constructeur
        public MainPage()
        {
            InitializeComponent();

            ShowDownloadPage();
        }

        private void ShowDownloadPage()
        {
            popup = new Popup();
            popup.Child = new DownloadScreen();
            popup.IsOpen = true;

            InitBackgroundWorker();
        }

        private void InitBackgroundWorker()
        {
            backgroundWorker = new BackgroundWorker();
            backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
            backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);

            backgroundWorker.RunWorkerAsync();
        }

        void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =&gt;
                {
                    //TRAITEMENT
                });
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Erreur", MessageBoxButton.OK);
            }
        }

        void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            popup.IsOpen = false;
        }
    }
}

Commentaires