Blog technique sur mes expériences de développeur.
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 :
Dans ce billet, c’est la première solution que nous allons étudier.
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>
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();
}
}
}
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;
}
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(() =>
{
//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;
}
A noter qu’il est possible de mettre plus de choses dans le constructeur. Ici, j’ai volontairement découpé ça en plusieurs fonctions dans un soucis de pédagogie !
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(() =>
{
//TRAITEMENT
});
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Erreur", MessageBoxButton.OK);
}
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
popup.IsOpen = false;
}
}
}