Générer vos données factices avec Blend - Le pouvoir du Lorem Ipsum

Lorem Ipsum ?

Une petite définition wikipedia pour débuter cet article :

Le faux-texte (également appelé lorem ipsum, lipsum ) est, en imprimerie, un texte sans valeur sémantique, permettant de remplir des pages lors d'une mise en forme afin d'en calibrer le contenu en l'absence du texte définitif. […]

Il circule des centaines de versions différentes du Lorem ipsum, mais ce texte aurait originellement été tiré de l'ouvrage de Cicéron, De Finibus Bonorum et Malorum (Liber Primus, 32), texte populaire à cette époque, dont l'une des premières phrases est : « Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit... » (« Il n'existe personne qui aime la souffrance pour elle-même, ni qui la recherche ni qui la veuille pour ce qu'elle est... »).

Tout un programme … mais quel est le rapport entre Cicéron et Windows Phone ?

Microsoft utilise depuis longtemps le Lorem Ipsum dans ses produits pour faciliter la mise en page avant de disposer du contenu finalisés. Vous pouvez par exemple utiliser la fonction =lorem() dans Word pour afficher un paragraphe factice (cf. https://support.microsoft.com/kb/212251).

Bien, mais toujours aucun rapport avec Windows Phone. Le but de cet article est de vous expliquer comment Blend for Visual Studio peut vous faciliter la vie en générant des données factices pour votre interface Windows Phone.

1. Le modèle de données

Toutefois, avant d’arriver à l’étape Blend, vous avez quand même un minimum de travail à fournir et notamment construire le modèle de données qui va être utilisé par l’application.

Dans notre exemple, je vais partir d’un projet presque vide (un modèle Windows Phone App classique) et ajouter le fichier SessionDataSource.cs suivant:

 using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Media;

namespace Sample.Data
{
    /// <summary>
    /// Session item data model.
    /// </summary>
    public class SessionDataItem : INotifyPropertyChanged
    {
        public SessionDataItem(string title, string speaker, string imagePath, string description)
        {
            this._title = title;
            this._speaker = speaker;
            this._imagePath = imagePath;
            this._description = description;
        }

        private string _title = string.Empty;
        public string Title
        {
            get { return this._title; }
            set { this.SetProperty(ref this._title, value); }
        }

        private string _speaker = string.Empty;
        public string Speaker
        {
            get { return this._speaker; }
            set { this.SetProperty(ref this._speaker, value); }
        }

        private string _imagePath = null;
        public string ImagePath
        {
            get { return this._imagePath; }
            set { this.SetProperty(ref this._imagePath, value); }
        }

        private string _description = string.Empty;
        public string Description
        {
            get { return this._description; }
            set { this.SetProperty(ref this._description, value); }
        }

        /// <summary>
        /// Multicast event for property change notifications.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Checks if a property already matches a desired value.  Sets the property and
        /// notifies listeners only when necessary.
        /// </summary>
        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (object.Equals(storage, value)) return false;

            storage = value;
            this.NotifyPropertyChanged(propertyName);
            return true;
        }

        /// <summary>
        /// Notifies listeners that a property value has changed.
        /// </summary>
        private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (null != handler)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

 

La classe SessionDataItem comporte simplement quatre propriétés (Title, Speaker, ImagePath et Description) ; et pour la forme j’implémente l’interface INotifyPropertyChanged . Pas forcément utile dans la démonstration suivante mais c’est un prétexte pour souligner l’arrivée du nouvel attribut [CallerMemberName] pour Windows Phone 8. Cet attribut permet d’éviter de saisir le nom de la propriété lors de l’appel à la méthode NotifyPropertyChanged, c’est le compilateur qui fait le travail pour vous ! Le but est principalement d’éviter les erreurs lorsque vous renommez une propriété.

SetProperty<T> permet de s’assurer qu’on déclenche l’évènement PorpertyChanged uniquement lorsque la valeur à été modifiée (et non à chaque appel de l’accesseur Set).  

 

2. Edition d’une nouvelle page

Pour l’objet de la démonstration, je rajoute une nouvelle page à mon projet nommée DetailsPage (de type Windows Phone Portrait Page) ; celle-ci est composée simplement d’une grille avec quatre contrôles(une Image et trois TextBlock) pour accueillir les propriétés précédentes. Rien de bien compliqué mais vous pouvez copier le code XAML suivant pour gagner du temps :

 <phone:PhoneApplicationPage
    x:Class="Sample.DetailsPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    mc:Ignorable="d"
    shell:SystemTray.IsVisible="True">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="200"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Border Grid.Row="0" Background="Blue">
            <Image Name="BackgroundImage" Stretch="UniformToFill"/>
        </Border>
        <TextBlock Name="TitleTextBlock" Grid.Row="1"/>
        <TextBlock Name="SpeakerTextBlock" Grid.Row="2"/>
        <TextBlock Name="DescriptionTextBlock" TextWrapping="Wrap" Grid.Row="3"/>
    </Grid>
</phone:PhoneApplicationPage>

Sauvegardez votre projet. ouvrez ensuite la page dans Blend for Visual Studio (depuis menu contextuel disponible sur DetailsPage.xaml). Nous obtenons le résultat ‘presque’ sensationnel suivant … bon, j’avoue rien de génial, on peut mieux faire  … et de surcroit rapidement !

image

3. Les données factices pour faciliter notre design

On rentre dans le vif du sujet; Blend propose une section Data disponible dans le coin en haut à droite de l’interface. Si celle-ci n’est pas présente, vous pouvez l’activer depuis le menu Window.

L’option Create Sample Data from Class… va nous permettre de générer un fichier de données factices (dans notre cas SessionDataItemSampleData.xaml) dont la structure est basée sur notre classe SessionDataItem. Le but n’est pas d’utiliser ces données lors de l’exécution de l’application mais simplement de faciliter notre mise en page : on peut appliquer des styles et mettre en forme la page avant d’accueillir les véritables données.

image

Je vous invite à consulter la vidéo suivante pour découvrir comment utiliser cette fonctionnalité. Pas si mal pour 3 minutes de travail ?

Quelles modifications ont été ajoutées au projet par Blend ?

- Un fichier au format .xaml (SessionDataItemSampleData.xaml) qui contient les données factices de nos différentes propriétés. Vous pouvez noter dans la démonstration que le Lorem Ipsum peut être remplacé par d’autres formats plus spécifiques si besoin (nom, adresse, numéro de téléphone, url …). Ce fichier peut également être édité à la main (par exemple dans le cas des images, pour pointer sur des éléments graphiques présents dans votre projet).

- Un DataContext de Design (d:DataContext) qui pointe sur le fichier précédent; et le DataBinding associé sur chaque contrôle. Le code de notre page .xaml donne donc :

 <phone:PhoneApplicationPage
    x:Class="Sample.DetailsPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    mc:Ignorable="d"
    shell:SystemTray.IsVisible="True">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent" 
          d:DataContext="{d:DesignData /SampleData/SessionDataItemSampleData.xaml, Instance={x:Null}}">
        <Grid.RowDefinitions>
            <RowDefinition Height="200"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Border Grid.Row="0" Background="Blue" Margin="12,0">
            <Image Name="BackgroundImage" Stretch="UniformToFill" Source="{Binding ImagePath}"/>
        </Border>
        <TextBlock Name="TitleTextBlock" Grid.Row="1" Text="{Binding Title}" 
                   Style="{StaticResource PhoneTextLargeStyle}"/>
        <TextBlock Name="SpeakerTextBlock" Grid.Row="2" Text="{Binding Speaker}"
                   Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock Name="DescriptionTextBlock" TextWrapping="Wrap" Grid.Row="3" Text="{Binding Description}" 
                   Style="{StaticResource PhoneTextSubtleStyle}" Margin="12,12,12,0"/>
    </Grid>
</phone:PhoneApplicationPage>

 

Votre application est désormais prête à accueillir de véritables données (ou presque ^^).

 

image