Accueil Nos publications Blog Windows Phone 7 et popups

Windows Phone 7 et popups

Une application Windows Phone peut échouer aux tests de certification pour une toute petite raison : le manque d’information à l’utilisateur concernant des erreurs de processus. Ces processus peuvent être divers, mais plus fréquents concernant la consommation de données distantes. En effet, il ne faut pas négliger les informations que l’on renvoie aux utilisateurs, le moment aussi où l’on doit renvoyer ces informations. Non seulement les informations, mais aussi la forme.

Identification du problème

C’est malheureusement ce qui m’est arrivé. Mon application consomme des services et si jamais il est impossible d’accéder aux services, bien qu’elle ne plante pas, rien n’est indiqué à l’utilisateur. J’utilise pourtant des barres de progression, mais ce n’est pas suffisant. Voici le résultat du compte-rendu de certification que j’avais reçu :

Requirements
The application must not become unresponsive to user input because of an operation within the application. For time consuming activities such as downloading data over network connections, the application must display a visual progress indicator. When there is a visual progress indicator, you must implement a UI element that provides the user with an option to cancel the time consuming activity.

Et voici ce qui est (était) attendu :

Expected Result
Test Process Required:
1. While thoroughly testing the application, ensure the application does not become unresponsive for one second or more.
2. If the application is processing information or downloading data, a progress indicator must be displayed.
3. The visual indicator may be a color wheel, progress bar, or text command.
4. The application must provide the user an option to cancel or end the processing or downloading.

Tous ces points étaient mis en place à l’exception du point 4 (que je n’ai toujours pas mis en place, mais “contourné”).

Résolution du problème

Pourquoi avoir contourné ce problème ?
J’utilise pour consommer des services REST l’excellent RestSharp. J’en avais déjà parlé par ailleurs dans cet article.
L’avantage en utilisant RestSharp est le suivant :

If there is a network transport error (network is down, failed DNS lookup, etc), RestResponse.Status will be set to ResponseStatus.Error, otherwise it will be ResponseStatus.Completed. If an API returns a 404, ResponseStatus will still be Completed. If you need access to the HTTP status code returned you will find it at RestResponse.StatusCode. The Status property is an indicator of completion independent of the API error handling.

Etant donné que le mécanisme de détection d’erreur quant à la requête est déjà géré, je ne voulais pas m’engager dans un processus permettant d’annuler une requête exécutée (en tout cas, pour le moment). Il fallait donc simplement que j’informe l’utilisateur quand on a un problème de requête, d’où l’utilisation d’une simple popup.

Réalisation de la popup

Généralement, dans mes applications, je créé un dossier Controls dans lequel je mets les différents controls/usercontrols que je peux créer. Concernant la Popup, un simple UserControl suffit amplement.

Créez donc un UserControl qui s’appelle par exemple : ErrorMessageControl.xaml


<UserControl x:Class="monsmartphone.Controls.ErrorMessageControl"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    d:DesignHeight="480" d:DesignWidth="480">
    
    <Grid x:Name="LayoutRoot" Background="Transparent" Width="480" Height="800">
        <Grid x:Name="LayoutChild" VerticalAlignment="Top" Background="{StaticResource PhoneChromeBrush}" Width="480" Height="200">
            <StackPanel Orientation="Vertical" Margin="5">
                <TextBlock x:Name="tbTitle" Text="Problème de connexion" Style="{StaticResource PhoneTextTitle2Style}" FontSize="30" Margin="5,0,5,5" />
                <TextBlock x:Name="tbContent" Margin="5,0,5,0" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}" />
                <Button Click="Button_Click" Content="Fermer" Width="150" Margin="-5,15,0,0" HorizontalAlignment="Left"/>
            </StackPanel>
        </Grid>
    </Grid>
</UserControl>

Quelques petites explications à propos des Grid utilisés.
Le Grid LayoutChild est celui qui affiche le message. J’identifie mes TextBlock avec des noms afin de pouvoir dynamiquement par la suite y afficher des valeurs (j’aurai pu le faire aussi avec des Binding, le choix vous appartient).
Ce même Grid a des dimensions correspondant à ce que je souhaite (c’est-à-dire que je souhaite que la popup s’affiche sur une partie de l’écran). Je ne souhaite pas que le message occupe tout l’écran, mais que l’on puisse voir ce qu’il y a derrière ce message.

C’est là qu’intervient le Grid LayoutRoot. Je lui ai donné comme dimensions les dimensions complètes de l’écran en mode portrait, c’est à dire, 480 x 800. Non seulement cela, mais aussi un background Transparent afin de pouvoir voir derrière.
Pourquoi ?
Parce que, si on ne le fait pas, il sera possible d’agir sur l’arrière plan de la fenêtre contenant le message. C’est ainsi que l’on doit s’y prendre pour réaliser une Popup “modale”.

Ensuite, le code behind :


using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace monsmartphone.Controls
{
    public partial class ErrorMessageControl : UserControl
    {
        public ErrorMessageControl(string message)
        {
            InitializeComponent();
            tbContent.Text = message;
        }

        private void ClosePopup()
        {
            Popup errorPopup = this.Parent as Popup;
            errorPopup.IsOpen = false;
        }

        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            ClosePopup();
        }
    }
}

Il faut juste noter que pour fermer une popup, il faut utiliser la propriété IsOpen = false.

Comment appeler sa popup ?
Je commence d’abord par créer une méthode qui affichera ma Popup là ou je le souhaite:


        private bool _IsErrorMessageDisplayed = false;
        /// 
        /// Displays An Error Message popup
        /// 
        private void DisplayErrorMessage()
        {
            if (!_IsErrorMessageDisplayed)
            {
                Opacity = 0.5;
                _IsErrorMessageDisplayed = true;
                Popup errorPopup = new Popup();
                errorPopup.Child = new ErrorMessageControl("Cette fonctionnalité est indisponible car votre connexion semble être inexistante ou limitée.");
                errorPopup.Closed += (s1, s2) => {
                    Opacity = 1;
                    _IsErrorMessageDisplayed = false; 
                };
                LatestNewsLoading = false;
                LatestNewsVisibility = System.Windows.Visibility.Visible;
                IsBusy = false;
                errorPopup.IsOpen = true;
            }
        }

Alors, j’ai une condition à laquelle je dois porter attention : J’affiche des flux provenant de plusieurs sources. Toutefois, si j’ai un problème de connectivité, je l’aurai pour l’ensemble des flux. Il n’est donc pas nécessaire (pas du tout recommandé) d’afficher autant de fois le message que d’erreurs de connectivité. C’est la raison de ma condition dans cette méthode.

Après, dépendant de votre manière de fonctionner et de la façon dont est construite votre application, vous pouvez choisir de diminuer l’opacité de votre contrôle principal, désactiver la barre de progression et afficher les blocs éventuellement masqués pour la récupération de données. C’est ce à quoi me servent les variables que vous voyez dans ce petit bloc de code.

Où appeler sa popup
Il faut l’appeler dans le callback de la réponse car, c’est là que nous aurons son statut.


        private void GetLatestNews(RestResponse results)
        {
            if (results.ResponseStatus == ResponseStatus.Error) {
                DisplayErrorMessage();
                return;
            }
            //Process Response Data

Conclusion

Voilà, vous savez maintenant intégrer des popup au sein de votre application Windows Phone, alors, n’hésitez pas à en utiliser (aux bons moments). Concernant RestSharp, nous aurons l’occasion d’y revenir très prochainement dans un billet qui lui sera dédié, car, il y a beaucoup de choses à en dire.
A bientôt !