Plan du site  
français  English
pixel
pixel

Articles - Étudiants SUPINFO

Introduction

Dans ce chapitre, nous allons découvrir les possibilités de stockage d'information local et Cloud dans une application UWP. Le système offre un espace de stockage réplicable ou non dans le Cloud Microsoft, permettant la synchronisation des informations entre les différents équipements utilisant l'application.

Si ces espaces de stockage ne sont pas prévus pour stocker de grandes quantités de données, ils peuvent être utilisés pour stocker des paramètres et des informations utilisateur, d'où leur nom : LocalSettings et RoamingSettings.

Accès aux espaces de stockage

L'accès au stockage local est permis par la classe ApplicationData et retourne un objet de type ApplicationDataContainer pour les paramètres, et StorageFolder pour les fichiers. Quelle que soit la méthode utilisée, ces valeurs et fichiers sont accessibles uniquement dans le cadre de l'application et ne sont pas accessibles en dehors de celle-ci.

Exemples d'accès :

// Accès au stockage de paramètres local
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
// Accès au stockage de paramètres Cloud
ApplicationDataContainer roamingSettings = ApplicationData.Current.RoamingSettings;

// Accès au stockage de fichiers local
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
// Accès au stockage de fichiers Cloud
StorageFolder roamingFolder = ApplicationData.Current.RoamingFolder;

LocalSettings et RoamingSettings

Un objet de classe ApplicationDataContainer obtenu permet d'accéder à la propriété Values, permettant le stockage des données aisément :

// Pour le stockage Cloud, remplacez la variable "localSettings" par "roamingSettings"
localSettings.Values["clef"] = "valeur";
localSettings.Values["age"] = 25;
localSettings.Values["booleen"] = true;
[Note]

Il n'est pas possible de stocker pour le moment un objet dans localSettings. Si vous souhaitez stocker une valeur composite, il vous faudra utiliser des méthodes de sérialisation pour stocker la valeur en tant que string.

Au sein des paramètres locaux comme Cloud, il est possible de créer des conteneurs séparant les valeurs.

// Pour le stockage Cloud, remplacez la variable "localSettings" par "roamingSettings"
ApplicationDataContainer sousConteneur = 
  localSettings.CreateContainer("sousConteneur", ApplicationDataCreateDisposition.Always);

Ici, on initialise un conteneur appelé "sousConteneur", qui sera grâce au second paramètre créé s'il n'existe pas.

[Note]

L'énumération ApplicationDataCreateDisposition autorise également la valeur "Existing", qui ne retourne le conteneur que s'il existe déjà.

Pour récupérer des valeurs, il suffit d'effectuer l'opération inverse :

// Pour le stockage Cloud, remplacez la variable "localSettings" par "roamingSettings"
var clef = localSettings.Values["clef"];
// Attention, le type de retour est nécessairement "object". Vous devrez utiliser une opération
// de cast pour accéder à vos éléments.
int? age = (int?)localSettings.Values["age"];
[Note]

Si la clef n'existe pas dans l'espace de stockage, la valeur retournée sera null. Il faut toujours vérifier la valeur de retour pour éviter les exceptions générées par l'absence d'une clef.

LocalFolder et RoamingFolder

La gestion des fichiers est légèrement plus complexe à appréhender, car c'est une opération externe, dépendant donc de méthodes asynchrones et de structures plus complexes qu'un ensemble clef/valeur.

On note les possibilités suivantes sur la gestion des fichiers:

  • Créer, Lire, Renommer et Supprimer un dossier

  • Créer, Lire, Modifier, Renommer et Supprimer un fichier.

Exemple d'ouverture de dossiers :

StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFolder monDossier = await localFolder.CreateFolderAsync("MonDossier", 
    CreationCollisionOption.OpenIfExists);
[Note]

L'énumération CreationCollisionOption permet de définir le comportement à tenir en cas de création d'un fichier ou dossier qui existe déjà. Dans notre cas, nous souhaitons obtenir ce dossier s'il existe, mais il est possible de remplacer l'existant ou de générer un nom unique. Sans précision, une exception sera levée si le fichier existe déjà.

Après obtention d'un dossier, il est possible d'itérer sur ses fichiers pour en lister les propriétés :

IReadOnlyList<StorageFile> mesFichiers = await monDossier.GetFilesAsync();
foreach(StorageFile fichier in mesFichiers)
{
  string nom = fichier.Name;
  DateTimeOffset creation = fichier.DateCreated;
  // ...
}

Pour créer un fichier au sein d'un dossier, il faudra utiliser la méthode CreateFileAsync sur un objet StorageFolder, qu'il soit la racine (LocalFolder, RoamingFolder) ou un sous-dossier obtenu précédemment.

StorageFile nouveauFichier = await monDossier.CreateFileAsync("nouveauFichier.txt", 
                                                 CreationCollisionOption.OpenIfExists);

Il est possible d'écrire du contenu de deux manières dans un fichier : via la classe FileIO permettant une gestion simple et minimaliste, ou via des flux en ouvrant le fichier.

Méthode 1 : Utilisation de FileIO :

await FileIO.WriteTextAsync(nouveauFichier, "Ajout de contenu textuel", UnicodeEncoding.Utf8);

Le code précédent va ouvrir le fichier référencé par l'objet "nouveauFichier" et y écrire le texte "Ajout de contenu textuel" avec l'encodage UTF-8.

[Note]

Il est toujours conseillé de préciser l'encodage pour éviter les problèmes de récupération et de traitement de chaînes de caractère. Une fois un encodage défini, il est conseillé de respecter le même pour l'ensemble des fichiers gérés par l'application.

Méthode 2 : Utilisation de flux :

Pour cette méthode, il est nécessaire d'ouvrir le fichier avec la méthode OpenFileAsync. On obtient un flux vers le fichier nous permettant d'écrire du contenu. Cette méthode nécessite d'avoir des connaissances minimales en gestion de fichiers et en flux d'écriture. La méthode utilisée dans l'exemple ci-dessous ressemble particulièrement au langage Java. Pour plus d'informations, n'hésitez pas à consulter le cours 2JVA.

IRandomAccessStream flux = await nouveauFichier.OpenAsync(FileAccessMode.ReadWrite);
// Utilisez la méthode GetOutputStreamAt(0) pour écrire à partir du début du fichier.
// La propriété flux.Size permet de positionner le curseur à la fin du fichier.
using (IOutputStream fluxEcriture = flux.GetOutputStreamAt(flux.Size))
{
  // L'objet DataWriter est une instance facilitant l'écriture de données dans un fichier.
  using (DataWriter d = new DataWriter(fluxEcriture))
  {
    // Il est conseillé de définir l'encodage utilisé et de s'y tenir dans toute l'application,
    // pour éviter les erreurs d'encodage entre les différents fichiers.
    d.UnicodeEncoding = UnicodeEncoding.Utf8;
    // On écrit la chaîne de caractères. Il est possible d'utiliser d'autres méthodes
    // d'instance de classe DataWriter, comme WriteBytes pour un contenu binaire.
    d.WriteString("Ajout de contenu textuel avec un flux.");
    // On vide le contenu de DataWriter dans le flux fluxEcriture
    await d.StoreAsync();
    // On écrit le contenu dans le fichier avec FlushAsync
    await fluxEcriture.FlushAsync();
  }
}
// Pour terminer, on ferme le fichier pour libérer l'accès à celui-ci
flux.Dispose();

Pour lire le contenu d'un fichier, il suffit d'appeler la méthode ReadFileAsync de la classe FileIO.

string contenu = await FileIO.ReadTextAsync(nouveauFichier, UnicodeEncoding.Utf8);

Etape 5 : Page de paramètres

Exercice corrigé : Paramètres local et Cloud

1.1.

Nous allons créer une page Paramètres pour notre application. Tout d'abord, la navigation vers cette page devra être placée en bas de notre NavPane. Pour cela, englobez l'ensemble de votre affichage dans NavPane par un élément Grid, la première ligne ayant la taille "*" et la seconde la taille "auto".

Au sein de cette deuxième colonne, répliquez l'affichage des autres éléments de NavPane (taille de police, dimensions, ...). La couleur de fond doit être "Gray", et l'icône affichée doit être "&#xE713;".

Figure 1.1. Rendu de NavPane avec l'élément "Paramètres"

Rendu de NavPane avec l'élément "Paramètres"

Implémentez l'événement au clic (en fonction de votre implémentation, Click ou PointerPressed) sur l'élément "Paramètres", et faites naviguer l'utilisateur vers la page SettingsPage que nous allons créer juste après. Pour cela, ajoutez une propriété événement NavigationCalled et invoquez-le comme vu au chapitre précédent.

<!-- Fichier NavPane.xaml -->
<UserControl> <!-- Attributs omis pour faciliter la lecture -->
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="*" />
      <RowDefinition Height="auto" />
    </Grid.RowDefinitions>
    <!-- Reste de NavPane -->
    <StackPanel Grid.Row="1" Orientation="Horizontal" x:Name="Settings" 
                PointerPressed="Settings_Click" Background="Gray">
      <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="30" Text="&#xE713;" 
                 Width="50" Height="50" Padding="10" />
      <TextBlock Text="Paramètres" FontSize="18" VerticalAlignment="Center" />
    </StackPanel>
  </Grid>
</UserControl>
// Classe NavPane.xaml.cs. Reste de la classe omise
private void Settings_Click(object sender, RoutedEventArgs e)
{
  NavigationCalled?.Invoke(this, new NavigationCalledEventArgs()
  {
    Type = typeof(SettingsPage)
  });
}
// Classe MainPage.xaml.cs. Reste de la classe omise.
public MainPage()
{
  // Reste du constructeur omis
  // Factorisation des événements de navigation générés par
  // ArticlesList et NavPane au sein d'une unique méthode
  ArticlesList.NavigationCalled += OnNavigationCalled;
  NavPane.NavigationCalled += OnNavigationCalled;
}

private void OnNavigationCalled(object sender, NavigationCalledEventArgs e)
{
  Frame.Navigate(e.Type, e.Parameter);
}

1.2.

Créer une nouvelle page appelée "SettingsPage.xaml". Il faudra reproduire l'affichage suivant :

Figure 1.2. Rendu de la page SettingsPage.xaml

Rendu de la page SettingsPage.xaml

Il est conseillé d'utiliser la propriété Header de ToggleSwitch pour définir le texte associé. De même, les textes à droite des boutons radio sont préférablement définis par leur attribut Content.

Le texte "Paramètres" doit appliquer le style "SubheaderTextBlockStyle". Le bouton radio "Sombre" doit posséder l'attribut "Tag" défini à "Dark" tandis que le bouton radio "Clair" doit posséder l'attribut "Tag" défini à "Light".

Les élements "Publication des étudiants de SUPINFO" et "Site institutionnel SUPINFO" doivent être des contrôles HyperlinkButton.

<!-- Page SettingsPage.xaml -->
<Page> <!-- Attributs omis pour faciliter la lecture -->
  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel Orientation="Vertical" Margin="10,10,0,0">
      <TextBlock Text="Paramètres" Style="{StaticResource HeaderTextStyle}" />
      <ToggleSwitch Header="Afficher les icônes d'articles" x:Name="ShowIcons" />
      
      <TextBlock Text="Utilisation du thème (nécessite un redémarrage de l'application)" 
                 TextWrapping="Wrap"/>
      <RadioButton Content="Sombre" Tag="Dark" x:Name="Theme_Dark"/>
      <RadioButton Content="Clair" Tag="Light" x:Name="Theme_Light"/>

      <HyperlinkButton Margin="0,10,0,0" Content="Publications des étudiants de SUPINFO" />
      <HyperlinkButton Content="Site institutionnel SUPINFO" />
    </StackPanel>
  </Grid>
</Page>

1.3.

Nous allons stocker la valeur du paramètre "Afficher les icônes d'articles" dans le stockage Cloud (Roaming Settings), avec la clef "ShowIcons". La valeur du paramètre "Utilisation du thème" sera stockée dans ce même stockage avec la clef "RequestedTheme".

Dans le code-behind, ajoutez en instance de classe SettingsPage l'objet roamingSettings, de type ApplicationDataContainer, et initialisez cette propriété avec le conteneur de stockage Cloud comme vu précédemment. Au constructeur de SettingsPage, tentez de récupérer les deux champs, ShowIcons et RequestedTheme. Si les clefs n'existent pas, initialisez de la façon suivante :

  • RequestedTheme : "Light" (string)

  • ShowIcons : true (booléen)

Appliquez aux contrôles présents sur la page la valeur de leurs paramètres respectifs. Ainsi, si le paramètre ShowIcons vaut true, définissez la propriété IsOn du ToggleSwitch à true et inversement. De la même manière, si le paramètre RequestedTheme vaut "Light", définissez la propriété IsChecked du bouton radio Light à true et la propriété IsChecked du bouton radio Dark à false, et inversement.

public sealed partial class SettingsPage : Page
{
  ApplicationDataContainer roamingSettings = ApplicationData.Current.RoamingSettings;
  public SettingsPage()
  {
    this.InitializeComponent();
    // Initialisation de la clef ShowIcons
    if(roamingSettings.Values["ShowIcons"] == null)
    {
      roamingSettings.Values["ShowIcons"] = true;
    }
    bool showIcons = (bool)roamingSettings.Values["ShowIcons"];

    // Initialisation de la clef RequestedTheme
    if(roamingSettings.Values["RequestedTheme"] == null)
    {
      roamingSettings.Values["RequestedTheme"] = "Light";
    }
    string requestedTheme = roamingSettings.Values["RequestedTheme"].ToString();

    // Définition de la valeur du ToggleSwitch
    ShowIcons.IsOn = showIcons;
    // Définition de la valeur des boutons radio
    bool isDark = requestedTheme.ToString() != "Light";
    Theme_Dark.IsChecked = isDark;
    Theme_Light.IsChecked = !isDark;
  }
}

1.4.

Implémentez des événements sur les différents contrôles :

  • Au changement d'état du contrôle ToggleSwitch (Toggled) : Définissez pour la clef ShowIcons de l'objet roamingSettings la valeur de la propriété IsOn du contrôle.

  • Au changement de sélection des boutons radio (Checked, un seul événement pour les deux contrôles) : Définissez pour la clef RequestedTheme de l'objet roamingSettings la valeur de la propriété Tag du contrôle.

  • Au clic sur l'hyperlien "Publication des étudiants de SUPINFO" : Utilisez la méthode _3WIN_HttpHelper.WebLaunch et redirigez l'utilisateur vers l'URI : "https://www.supinfo.com/articles"

  • Au clic sur l'hyperlien "Site institutionnel SUPINFO" : Utilisez la méthode _3WIN_HttpHelper.WebLaunch et redirigez l'utilisateur vers l'URI : "https://www.supinfo.com/"

<TextBlock Text="Paramètres" Style="{StaticResource SubheaderTextBlockStyle}" />
<ToggleSwitch Header="Afficher les icônes d'articles" x:Name="ShowIcons" 
              Toggled="ShowIcons_Toggled"/>

<TextBlock Text="Utilisation du thème (nécessite un redémarrage de l'application)" 
           TextWrapping="Wrap"/>
<RadioButton Content="Sombre" Tag="Dark" x:Name="Theme_Dark" 
             Checked="RequestedTheme_Checked"/>
<RadioButton Content="Clair" Tag="Light" x:Name="Theme_Light" 
             Checked="RequestedTheme_Checked"/>

<HyperlinkButton Margin="0,10,0,0" Content="Publications des étudiants de SUPINFO" 
                 Click="ArticlesLink_Click" />
<HyperlinkButton Content="Site institutionnel SUPINFO" Click="IndexLink_Click" />
// Fichier SettingsPage.xaml.cs. Reste de la classe omise
private void ShowIcons_Toggled(object sender, RoutedEventArgs e)
{
  var ts = (ToggleSwitch)sender;
  bool showIcons = ts.IsOn;
  roamingSettings.Values["ShowIcons"] = showIcons;
}

private void RequestedTheme_Checked(object sender, RoutedEventArgs e)
{
  RadioButton rb = sender as RadioButton;
  if (rb != null)
  {
    roamingSettings.Values["RequestedTheme"] = rb.Tag.ToString();
  }
}

private void ArticlesLink_Click(object sender, RoutedEventArgs e)
{
  _3WIN_HttpHelper.WebLaunch(new Uri("https://www.supinfo.com/articles"));
}
private void IndexLink_Click(object sender, RoutedEventArgs e)
{
  _3WIN_HttpHelper.WebLaunch(new Uri("https://www.supinfo.com"));
}

1.5.

Implémentons l'option "Utilisation du thème". Pour ce faire, ajoutez avant toute autre chose, dans le contructeur de votre application (fichier App.xaml.cs) le code suivant :

ApplicationDataContainer roamingSettings = ApplicationData.Current.RoamingSettings;
var requestedTheme = roamingSettings.Values["RequestedTheme"];
if (requestedTheme == null || requestedTheme.ToString() != "Light") {
  this.RequestedTheme = ApplicationTheme.Dark;
} else {
  this.RequestedTheme = ApplicationTheme.Light;
}

Dans le fichier App.xaml, supprimez l'attribut RequestedTheme.

L'implémentation de la deuxième option sera réalisée à la fin de la section suivante, car il nous faudra pour cela utiliser un nouveau contrôle : VisualState.

Changement d'affichage dynamique avec VisualState

Sur un tout autre registre et pour implémenter la deuxième option de l'application ArticlesApp permettant de changer l'affichage des articles, il nous faudra découvrir un autre contrôle, VisualState.

Ce contrôle permet de définir des états visuels et de basculer programmatiquement l'affichage d'un état à un autre, par le biais d'un élément VisualStateManager. Ce changement d'état peut utiliser des déclencheurs (triggers) en fonction de la taille de la fenêtre pour rendre l'affichage adaptatif et respectueux des différentes résolutions.

<VisualStateManager.VisualStateGroups>
  <VisualStateGroup>
    <VisualState x:Name="Large">
      <VisualState.StateTriggers>
        <AdaptiveTrigger MinWindowHeight="500" MinWindowWidth="500"/>
      </VisualState.StateTriggers>
      <VisualState.Setters>
        <Setter Target="RootGrid.Background" Value="Red" />
      </VisualState.Setters>
    </VisualState>
  </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

On a défini dans l'exemple précédent :

  • Un VisualStateManager ou élément permettant d'indiquer à UWP que notre page a des schémas de comportement différents.

  • Un VisualState (nécessairement englobé dans un élément VisualStateGroup) définissant un comportement

Au sein de cet élément VisualState, deux parties distinctes :

  • VisualState.StateTriggers (optionnel) : Définit les conditions d'activation du comportement. Dans l'exemple précédent, celui-ci s'enclenchera automatiquement si la taille de la fenêtre est supérieure à 500x500.

  • VisualState.Setters : Définit les propriétés à changer au sein de la page. Pour ce faire, chaque balise Setter définit un attribut "Target" qui ciblera une propriété d'un élément. Dans cet exemple, l'élément ciblé est celui ayant l'attribut x:Name défini à "RootGrid", et sa propriété ciblée est Background. La valeur définie est ici Red.

De manière plus concise, si la fenêtre a des dimensions inférieures à 500x500, alors la couleur de fond de l'élément RootGrid sera rouge.

Figure 1.3. Plus d'informations sur les StateTriggers (Anglais)


Les comportements peuvent également être définis sans AdaptiveTriggers et enclenchés programmatiquement dans le code-behind. C'est cette option que nous utiliserons pour la prochaine étape de réalisation de notre application.

<!-- PageTest.xaml -->
<Page> <!-- Attributs omis pour faciliter la lecture -->
  <Grid Background="White" x:Name="RootGrid">
    <VisualStateManager.VisualStateGroups>
      <VisualStateGroup>
        <VisualState x:Name="Situation1">
          <VisualState.Setters>
            <Setter Target="RootGrid.Background" Value="Red" />
          </VisualState.Setters>
        </VisualState>
        <VisualState x:Name="Situation2">
          <VisualState.Setters>
            <Setter Target="RootGrid.Background" Value="Blue" />
          </VisualState.Setters>
        </VisualState>
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    <StackPanel Orientation="Horizontal">
      <Button Content="Situation 1" Click="Button1_Click" />
      <Button Content="Situation 2" Click="Button2_Click" />
    </StackPanel>
  </Grid>
</Page>

Dans cet exemple, il n'y a pas de déclencheurs. Deux situations sont présentes, une définissant la couleur de fond de la grille principale à rouge, et l'autre à bleu. Puisqu'aucun déclencheur n'est présent, nous utiliserons le code-behind pour changer l'affichage, grâce aux deux boutons.

public sealed partial class PageTest : Page
{
  public PageTest()
  {
    this.InitializeComponent();
  }
  
  private void Button1_Click(object sender, RoutedEventArgs e)
  {
    VisualStateManager.GoToState(this, "Situation1", false);
  }

  private void Button2_Click(object sender, RoutedEventArgs e)
  {
    VisualStateManager.GoToState(this, "Situation2", false);
  }
}

La méthode VisualStateManager.GoToState prend trois paramètres :

  • Le contrôle concerné. Ici, la page elle-même.

  • Le nom du comportement. Selon notre bouton, la situation utilisée ne sera pas la même.

  • Si l'on souhaite ou non utiliser les transitions. Dans cet exemple, nous n'utiliserons pas de transition.

Figure 1.4. Rendu avec VisualState - Etat initial, Situation 1, Situation 2

Rendu avec VisualState - Etat initial, Situation 1, Situation 2

[Warning]

L'utilisation de VisualState au sein d'un contrôle ItemsControl (ListViews, GridView, ...) peut être utilisée dans le cas d'une modification des éléments visuels d'une liste de manière programmatique. Cette implémentation est plus complexe et nécessite une structure précise. La voici :

<ListView x:Name="ListView">
  <ListView.ItemTemplate>
    <DataTemplate>
      <UserControl> <!-- Element obligatoire -->
        <Grid> <!-- Peut être n'importe quel conteneur (StackPanel, Grid, ...), 
                    mais doit être présent -->
          <VisualStateManager.VisualStateGroups>
            <!-- .... -->
          </VisualStateManager.VisualStateGroups>
        </Grid>
      </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

L'actionnement du VisualState devient également plus complexe, car elle nécessite d'activer le changement d'état pour chaque élément de la liste.

// Récupération du conteneur d'éléments de notre ListView
ItemsStackPanel itemsStackPanel = ListView.ItemsPanelRoot;
// Itération sur chacun de ses éléments, les contrôles de type ListViewItem
foreach (ListViewItem item in itemsStackPanel.Children)
{
    // Récupération du contrôle UserControl défini plus haut
    UserControl uc = item.ContentTemplateRoot as UserControl;
    // Définition du comportement (ici "Situation1") pour l'élément UserControl
    VisualStateManager.GoToState(uc, "Situation1", false);
}

Etape 6 : Implémentation de l'option ShowIcons

Exercice corrigé : Implémentation de l'option ShowIcons

1.1.

Le but de cet exercice va être d'offrir à l'utilisateur le choix entre les deux affichages suivants (avec le thème sombre) :

Figure 1.5. ShowIcons : En haut, l'affichage actuel. En bas, l'affichage souhaité

ShowIcons : En haut, l'affichage actuel. En bas, l'affichage souhaité

Ajoutez un élément de style dans votre dictionnaire de ressources appelé BigDateLabelStyle basé sur ArticleTextStyle et ayant une taille de police de 16 et en gras.

Ajoutez un élément TextBlock dans ArticlesList aux côtés de l'icône du groupe et du label de date déjà existant.

L'affichage actuel de la partie de gauche devrait ressembler à ceci :

Figure 1.6. ShowIcons : Etape intermédiaire

ShowIcons : Etape intermédiaire

<!-- ArticleStyles.xaml. Reste du fichier omis -->
<Style x:Name="BigDateLabelStyle" TargetType="TextBlock" 
       BasedOn="{StaticResource ArticleTextStyle}">
  <Setter Property="FontSize" Value="16" />
  <Setter Property="FontWeight" Value="Bold" />
</Style>
<!-- ArticlesList.xaml. Reste du fichier omis -->
<!-- Les deux premiers TextBlocks ont été ajoutés dans une étape précédente -->
<TextBlock x:Name="DateLabel" Text="{Binding DateLabel}"
           Style="{StaticResource DateLabelStyle}" />
<TextBlock x:Name="IconLabel" Text="{Binding Category.Group.Icon}"
           Style="{StaticResource IconLabelStyle}"/>
<TextBlock x:Name="BigDateLabel" Text="{Binding DateLabel}"
           Style="{StaticResource BigDateLabelStyle}"/>

1.2.

Au sein du DataTemplate d'ArticlesList, entourez l'ensemble par un élément Grid, entouré lui-même d'un élément UserControl, comme présenté dans l'encart disponible plus haut.

<!-- ArticlesList.xaml. Reste du fichier omis -->
<DataTemplate>
  <UserControl>
    <Grid>
      <!-- Contenu du DataTemplate omis -->
    </Grid>
  </UserControl>
</DataTemplate>

1.3.

Dans l'élément Grid récemment ajouté, introduisez un contrôle VisualStateManager.VisualStateGroups.

Définissez deux scénarios :

  • Default : Dans ce scénario, les contrôles DateLabel et IconLabel doivent avoir leur attribut Visibility défini à Collapsed.

  • ShowIcons : Dans ce scénario, le contrôle BigDateLabel doit avoir son attribut Visibility défini à Collapsed.

<!-- ArticlesList.xaml. Reste du fichier omis -->
<VisualStateManager.VisualStateGroups>
  <VisualStateGroup>
    <VisualState x:Name="ShowIcons">
      <VisualState.Setters>
        <Setter Value="Collapsed" Target="BigDateLabel.Visibility" />
      </VisualState.Setters>
    </VisualState>
    <VisualState x:Name="Default">
      <VisualState.Setters>
        <Setter Value="Collapsed" Target="IconLabel.Visibility" />
        <Setter Value="Collapsed" Target="DateLabel.Visibility" />
      </VisualState.Setters>
    </VisualState>
  </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

1.4.

Dans le fichier ArticlesList.xaml.cs, au dessus de la classe ArticlesItem, définissez une énumération nommée ArticleItemDisplayMode. Cette énumération possèdera deux possibilités, Default et ShowIcons.

public enum ArticleItemDisplayMode
{
  Default, ShowIcons
}

1.5.

Dans la classe ArticlesList, ajoutez une propriété DisplayMode de type ArticleItemDisplayMode. Ajoutez également en propriété une référence au stockage Cloud (RoamingSettings).

Dans le constructeur d'ArticlesList, récupérez la valeur de la clef ShowIcons, et attribuez à DisplayMode la possibilité adaptée.

// Fichier ArticlesList.xaml.cs. Reste de la classe omise.
private ApplicationDataContainer roamingSettings = ApplicationData.Current.RoamingSettings;
public ArticleItemDisplayMode DisplayMode;

public ArticleList()
{
  this.InitializeComponent();
  if((bool?)roamingSettings.Values["ShowIcons"] == true) {
    DisplayMode = ArticleItemDisplayMode.ShowIcons;
  } else {
    DisplayMode = ArticleItemDisplayMode.Default;
  }
}

1.6.

Ajoutez l'événement LayoutUpdated sur la ListView d'ArticlesList. Nous utiliserons cet événement pour choisir de comportement de notre VisualStateManager. Utilisez la méthode vue dans l'encart précédent pour appeller la méthode GoToState pour obtenir le résultat voulu.

[Note]

Pour obtenir la représentation textuelle (string) d'une possibilité d'énumération, utilisez la syntaxe suivante :

DisplayMode.ToString("g")
<!-- Fichier ArticlesList.xaml. Reste du fichier omis -->
<ListView x:Name="RootListView" ItemsSource="{Binding Articles}" IsItemClickEnabled="True" 
        ItemClick="ListView_ItemClick" Grid.Row="1" LayoutUpdated="ListView_LayoutUpdated">
// Fichier ArticlesList.xaml.cs. Reste de la classe omise.
private void RootListView_LayoutUpdated(object sender, object e)
{
  if (RootListView.ItemsPanelRoot.Children.Count == 0) return;
  foreach (ListViewItem lvi in RootListView.ItemsPanelRoot.Children)
  {
    UserControl uc = lvi.ContentTemplateRoot as UserControl;
    VisualStateManager.GoToState(uc, DisplayMode.ToString("g"), false);
  }
}
About SUPINFO | Contacts & addresses | Teachers | Press | INVESTOR | Conditions of Use & Copyright | Respect of Privacy
Logo de la société Cisco Logo de la société IBM Logo de la société Sun-Oracle Logo de la société Apple Logo de la société Sybase Logo de la société Novell Logo de la société Intel Logo de la société Accenture Logo de la société SAP Logo de la société Prometric Logo du IT Academy Program par Microsoft

SUPINFO International University is globally operated by EDUCINVEST Belgium - Avenue Louise, 534 - 1050 Brussels
and is accredited in France by Association Ecole Supérieure d'Informatique de Paris (ESI SUPINFO)