Tutoriel : Créer des liaisons de donnéesTutorial: Create data bindings

Supposons que vous avez conçu et implémenté une belle interface utilisateur remplie d'espaces réservés pour les images, de texte réutilisable « lorem ipsum » et de contrôles qui ne font rien pour le moment.Suppose you've designed and implemented a nice looking UI filled with placeholder images, "lorem ipsum" boilerplate text, and controls that don't do anything yet. Vous voulez maintenant la connecter à des données réelles et transformer ce prototype de conception en une application vivante.Next, you'll want to connect it to real data and transform it from a design prototype into a living app.

Dans ce tutoriel, vous allez apprendre à remplacer votre texte réutilisable avec des liaisons de données et à créer d'autres liens directs entre votre interface utilisateur et vos données.In this tutorial, you'll learn how to replace your boilerplate with data bindings and create other direct links between your UI and your data. Vous découvrirez également comment mettre en forme ou convertir vos données pour l’affichage, et synchroniser votre interface utilisateur et vos données. Après avoir suivi ce tutoriel, vous saurez simplifier et organiser du code XAML et C#, pour le rendre plus facile à maintenir et à étendre.You'll also learn how to format or convert your data for display, and keep your UI and data in sync. When you complete this tutorial, you'll be able to improve the simplicity and organization of the XAML and C# code, making it easier to maintain and extend.

Vous allez commencer avec une version simplifiée de l’exemple PhotoLab.You'll start with a simplified version of the PhotoLab sample. Cette version de démarrage comprend la couche de données complète ainsi que les mises en page XAML de base et exclut de nombreuses fonctionnalités afin que le code soit plus facile à parcourir.This starter version includes the complete data layer plus the basic XAML page layouts, and leaves out many features to make the code easier to browse around in. Comme ce tutoriel ne génère pas l’application complète, veillez à examiner la version finale pour voir des fonctionnalités, comme les animations personnalisées et les dispositions adaptatives.This tutorial doesn't build up to the complete app, so be sure to check out the final version to see features such as custom animations and adaptive layouts. Vous la trouverez dans le dossier racine du référentiel Windows-appsample-photo-lab.You can find the final version in the root folder of the Windows-appsample-photo-lab repo.

L’exemple d’application PhotoLab comporte deux pages.The PhotoLab sample app has two pages. La page principale présente une vue de galerie de photos ainsi que des informations sur chaque fichier image.The main page displays a photo gallery view, along with some information about each image file.

Capture d’écran de la page principale du Laboratoire photo.

La page de détails affiche une seule photo une fois qu’elle a été sélectionnée.The details page displays a single photo after it has been selected. Un menu d’édition volant permet de modifier la photo, de la renommer et de l’enregistrer.A flyout editing menu allows the photo to be altered, renamed, and saved.

Capture d’écran de la page de détails du Laboratoire photo.

PrérequisPrerequisites

Partie 0 : Obtenir le code de démarrage à partir de GitHubPart 0: Get the starter code from GitHub

Dans ce tutoriel, vous allez commencer avec une version simplifiée de l’exemple PhotoLab.For this tutorial, you'll start with a simplified version of the PhotoLab sample.

  1. Accédez à la page GitHub de l’exemple : https://github.com/Microsoft/Windows-appsample-photo-lab.Go to the GitHub page for the sample: https://github.com/Microsoft/Windows-appsample-photo-lab.

  2. Vous devez ensuite cloner ou télécharger l’exemple.Next, you'll need to clone or download the sample. Sélectionnez le bouton Clone or download.Select the Clone or download button. Un sous-menu s’affiche.A sub-menu appears. Menu Clone or download dans la page GitHub de l’exemple PhotoLabThe Clone or download menu on the PhotoLab sample's GitHub page

    Si vous n’êtes pas familiarisé avec GitHub :If you're not familiar with GitHub:

    a.a. Sélectionnez Télécharger le zip, puis enregistrez le fichier localement.Select Download ZIP and save the file locally. Cela télécharge un fichier .zip contenant tous les fichiers projet dont vous avez besoin.This downloads a .zip file that contains all the project files you need.

    b.b. Extrayez le fichier.Extract the file. Utilisez l’Explorateur de fichiers pour accéder au fichier .zip que vous venez de télécharger, cliquez dessus avec le bouton droit, puis sélectionnez Extraire tout.Use File Explorer to browse to the .zip file that you just downloaded, right-click it, and select Extract All....

    c.c. Accédez à votre copie locale de l’exemple, puis au répertoire Windows-appsample-photo-lab-master\xaml-basics-starting-points\data-binding.Browse to your local copy of the sample, and go to the Windows-appsample-photo-lab-master\xaml-basics-starting-points\data-binding directory.

    Si vous maîtrisez GitHub :If you are familiar with GitHub:

    a.a. Clonez localement la branche maîtresse du dépôt.Clone the master branch of the repo locally.

    b.b. Accédez au répertoire Windows-appsample-photo-lab\xaml-basics-starting-points\data-binding.Browse to the Windows-appsample-photo-lab\xaml-basics-starting-points\data-binding directory.

  3. Double-cliquez sur Photolab.sln pour ouvrir la solution dans Visual Studio.Double-click Photolab.sln to open the solution in Visual Studio.

Partie 1 : Remplacer les espaces réservésPart 1: Replace the placeholders

Vous allez créer des liaisons à usage unique dans le modèle de données XAML pour afficher les images réelles et leurs métadonnées à la place du contenu des espaces réservés.Here, you create one-time bindings in data-template XAML to display real images and image metadata instead of placeholder content.

Les liaisons à usage unique sont destinées à des données immuables en lecture seule. Elles sont donc très performantes et faciles à créer, ce qui permet d’afficher de grands jeux de données dans des contrôles GridView et ListView.One-time bindings are for read-only, unchanging data, which means they are high performance and easy to create, letting you display large data sets in GridView and ListView controls.

Remplacer les espaces réservés par des liaisons à usage uniqueReplace the placeholders with one-time bindings

  1. Ouvrez le dossier xaml-basics-starting-points\data-binding et lancez le fichier PhotoLab.sln dans Visual Studio.Open the xaml-basics-starting-points\data-binding folder and launch the PhotoLab.sln file in Visual Studio.

  2. Vérifiez que votre plateforme de solution est définie sur x86 ou x64 (et non ARM), puis exécutez l’application.Make sure your Solution Platform is set to x86 or x64, not ARM, and then run the app. Cela indique l’état de l’application avec les espaces réservés de l’interface utilisateur, avant l'ajout des liaisons.This shows the state of the app with UI placeholders, before bindings have been added.

    Application en cours d’exécution avec les images et le texte des espaces réservés

  3. Ouvrez MainPage.xaml et recherchez un DataTemplate nommé ImageGridView_DefaultItemTemplate.Open MainPage.xaml and search for a DataTemplate named ImageGridView_DefaultItemTemplate. Vous devez mettre à jour ce modèle pour utiliser les liaisons de données.You'll update this template to use data bindings.

    Avant :Before:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate">
    

    La valeur x:Key est utilisée par ImageGridView afin de sélectionner ce modèle pour l'affichage des objets de données.The x:Key value is used by the ImageGridView to select this template for displaying data objects.

  4. Ajoutez une valeur x:DataType au modèle.Add an x:DataType value to the template.

    Après :After:

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
    

    x:DataType indique à quel type correspond le modèle.x:DataType indicates which type this is a template for. En l’occurrence, il s’agit d’un modèle pour la classe ImageFileInfo (où local: indique l’espace de noms local, tel que défini dans une déclaration xmlns dans la partie supérieure du fichier).In this case, it's a template for the ImageFileInfo class (where local: indicates the local namespace, as defined in an xmlns declaration near the top of the file).

    x:DataType est obligatoire lorsque des expressions x:Bind sont utilisées dans un modèle de données, comme indiqué ci-dessous.x:DataType is required when using x:Bind expressions in a data template, as described next.

  5. Dans le DataTemplate, recherchez l'élément Image nommé ItemImage et remplacez sa valeur Source comme indiqué.In the DataTemplate, find the Image element named ItemImage and replace its Source value as shown.

    Avant :Before:

    <Image x:Name="ItemImage"
           Source="/Assets/StoreLogo.png"
           Stretch="Uniform" />
    

    Après :After:

    <Image x:Name="ItemImage"
           Source="{x:Bind ImageSource}"
           Stretch="Uniform" />
    

    x:Name identifie un élément XAML pour permettre d’y faire référence ailleurs dans le code XAML et dans le code-behind.x:Name identifies a XAML element so you can refer to it elsewhere in the XAML and in the code-behind.

    Les expressions x:Bind fournissent une valeur à une propriété de l’interface utilisateur en récupérant la valeur à partir d’une propriété data-object.x:Bind expressions supply a value to a UI property by getting the value from a data-object property. Dans les modèles, la propriété indiquée est une propriété de la valeur définie pour x:DataType.In templates, the property indicated is a property of whatever the x:DataType has been set to. Dans ce cas, la source de données est la propriété ImageFileInfo.ImageSource.So in this case, the data source is the ImageFileInfo.ImageSource property.

    Notes

    La valeur x:Bind indique également à l’éditeur le type de données ; vous pouvez donc utiliser IntelliSense au lieu de taper le nom de la propriété dans une expression x:Bind.The x:Bind value also lets the editor know about the data type, so you can use IntelliSense instead of typing in the property name in an x:Bind expression. Effectuez des tests dans le code que vous venez de coller : placez le curseur juste après x:Bind et appuyez sur la barre d’espace pour afficher la liste des propriétés avec lesquelles vous pouvez établir une liaison.Try it on the code you just pasted in: place the cursor just after x:Bind and press the Spacebar to see a list of properties you can bind to.

  6. Remplacez les valeurs des autres contrôles d’interface utilisateur de la même façon.Replace the values of the other UI controls in the same way. (Essayez de le faire avec IntelliSense au lieu de copier/coller !)(Try doing this with IntelliSense instead of copy/pasting!)

    Avant :Before:

    <TextBlock Text="Placeholder" ... />
    <StackPanel ... >
        <TextBlock Text="PNG file" ... />
        <TextBlock Text="50 x 50" ... />
    </StackPanel>
    <muxc:RatingControl Value="3" ... />
    

    Après :After:

    <TextBlock Text="{x:Bind ImageTitle}" ... />
    <StackPanel ... >
        <TextBlock Text="{x:Bind ImageFileType}" ... />
        <TextBlock Text="{x:Bind ImageDimensions}" ... />
    </StackPanel>
    <muxc:RatingControl Value="{x:Bind ImageRating}" ... />
    

Exécutez l’application pour voir à quoi elle ressemble maintenant.Run the app to see how it looks so far. Il n'y a plus aucun espace réservé !No more placeholders! Nous sommes bien partis.We're off to a good start.

Application en cours d’exécution avec des images réelles et du texte à la place des espaces réservés

Notes

Si vous souhaitez aller plus loin, essayez d’ajouter un nouveau TextBlock au modèle de données et utilisez l’astuce IntelliSense x:Bind pour trouver une propriété à afficher.If you want to experiment further, try adding a new TextBlock to the data template, and use the x:Bind IntelliSense trick to find a property to display.

Vous allez maintenant créer des liaisons à usage unique dans la page XAML pour connecter l’affichage de la galerie à la collection d’images, en remplaçant le code procédural existant qui s’en charge dans le code-behind.Here, you'll create one-time bindings in page XAML to connect the gallery view to the image collection, replacing the existing procedural code that does this in code-behind. Vous allez également créer un bouton Supprimer pour voir comment l’affichage de galerie change lorsque les images sont supprimées de la collection.You'll also create a Delete button to see how the gallery view changes when images are removed from the collection. En même temps, vous allez apprendre à lier des événements aux gestionnaires d’événements pour bénéficier de davantage de souplesse qu’avec les gestionnaires d’événements classiques.At the same time, you'll learn how to bind events to event handlers for more flexibility than that provided by traditional event handlers.

Toutes les liaisons traitées jusqu’à présent se trouvent à l’intérieur de modèles de données et font référence aux propriétés de la classe indiquée par la valeur x:DataType.All the bindings covered so far are inside data templates and refer to properties of the class indicated by the x:DataType value. Qu’en est-il du reste du code XAML de la page ?What about the rest of the XAML in your page?

Les expressions x:Bind qui se trouvent en dehors des modèles de données sont toujours liées à la page elle-même.x:Bind expressions outside of data templates are always bound to the page itself. Il est donc possible de faire référence à tout ce qui est placé dans le code-behind ou déclaré dans le code XAML, y compris les propriétés personnalisées et les propriétés des autres contrôles d’interface utilisateur de la page (à condition qu’ils aient une valeur x:Name).This means you can reference anything you put in code-behind or declare in XAML, including custom properties and properties of other UI controls on the page (as long as they have an x:Name value).

Dans l’exemple PhotoLab, une utilisation possible de ce type de liaison consiste à connecter directement le contrôle GridView principal à la collection d’images, au lieu de le faire dans le code-behind.In the PhotoLab sample, one use for a binding like this is to connect the main GridView control directly to the collection of images, instead of doing it in code-behind. Vous verrez par la suite d’autres exemples.Later, you'll see other examples.

Lier le contrôle GridView principal à la collection ImagesBind the main GridView control to the Images collection

  1. Dans MainPage.xaml.cs, recherchez la méthode GetItemsAsync et supprimez le code qui définit ItemsSource.In MainPage.xaml.cs, find the GetItemsAsync method and remove the code that sets ItemsSource.

    Avant :Before:

    ImageGridView.ItemsSource = Images;
    

    Après :After:

    // Replaced with XAML binding:
    // ImageGridView.ItemsSource = Images;
    
  2. Dans MainPage.xaml, recherchez le GridView nommé ImageGridView et ajoutez un attribut ItemsSource.In MainPage.xaml, find the GridView named ImageGridView and add an ItemsSource attribute. Pour la valeur, utilisez une expression x:Bind qui fait référence à la propriété Images implémentée dans le code-behind.For the value, use an x:Bind expression that refers to the Images property implemented in code-behind.

    Avant :Before:

    <GridView x:Name="ImageGridView"
    

    Après :After:

    <GridView x:Name="ImageGridView"
              ItemsSource="{x:Bind Images}"
    

    La propriété Images étant de type ObservableCollection<ImageFileInfo>, les éléments individuels affichés dans le GridView sont de type ImageFileInfo.The Images property is of type ObservableCollection<ImageFileInfo>, so the individual items displayed in the GridView are of type ImageFileInfo. ce qui correspond à la valeur x:DataType décrite dans la partie 1.This matches the x:DataType value described in Part 1.

Toutes les liaisons que nous avons examinées jusqu'à présent sont des liaisons à usage unique en lecture seule, soit le comportement par défaut des expressions x:Bind simples.All the bindings we've looked at so far are one-time, read-only bindings, which is the default behavior for plain x:Bind expressions. Les données ne sont chargées qu’à l’initialisation, ce qui donne des liaisons hautes performances, idéales pour prendre en charge plusieurs vues complexes de jeux de données volumineux.The data is loaded only at initialization, which makes for high-performance bindings - perfect for supporting multiple, complex views of large data sets.

Même la liaison ItemsSource que vous venez d'ajouter est une liaison à usage unique en lecture seule avec une valeur de propriété immuable, à une différence importante près.Even the ItemsSource binding you just added is a one-time, read-only binding to an unchanging property value, but there's an important distinction to make here. La valeur immuable de la propriété Images est une instance unique et spécifique d’une collection, initialisée une fois, comme on le voit ici.The unchanging value of the Images property is a single, specific instance of a collection, initialized once as shown here.

private ObservableCollection<ImageFileInfo> Images { get; }
    = new ObservableCollection<ImageFileInfo>();

La valeur de la propriété Images ne change jamais mais, comme la propriété est de type ObservableCollection<T>, le contenu de la collection peut changer, auquel cas la liaison remarque automatiquement les modifications et met à jour l’interface utilisateur.The Images property value never changes, but because the property is of type ObservableCollection<T>, the contents of the collection can change, and the binding will automatically notice the changes and update the UI.

Pour tester ce comportement, nous allons ajouter temporairement un bouton qui supprime l’image sélectionnée.To test this, we're going to temporarily add a button that deletes the currently-selected image. Ce bouton ne se trouve pas dans la version finale, car le fait de sélectionner une image a pour effet de rediriger vers une page de détails.This button isn't in the final version because selecting an image will take you to a detail page. Toutefois, le comportement d’ObservableCollection<T> reste important dans l’exemple PhotoLab final, car le code XAML est initialisé dans le constructeur de la page (par le biais de l’appel de méthode InitializeComponent), mais la collection Images est remplie ultérieurement dans la méthode GetItemsAsync.However, the behavior of ObservableCollection<T> is still important in the final PhotoLab sample because the XAML is initialized in the page constructor (through the InitializeComponent method call), but the Images collection is populated later in the GetItemsAsync method.

Ajouter un bouton SupprimerAdd a delete button

  1. Dans MainPage.xaml, recherchez la CommandBar nommée MainCommandBar et ajoutez un nouveau bouton avant le bouton de zoom.In MainPage.xaml, find the CommandBar named MainCommandBar and add a new button before the zoom button. (Les contrôles de zoom ne fonctionnent pas encore.(The zoom controls don't work yet. Vous allez les associer dans la partie suivante de ce tutoriel.)You'll hook those up in the next part of the tutorial.)

    <AppBarButton Icon="Delete"
                  Label="Delete selected image"
                  Click="{x:Bind DeleteSelectedImage}" />
    

    Si vous connaissez déjà le langage XAML, cette valeur Click peut vous paraître inhabituelle.If you're already familiar with XAML, this Click value might look unusual. Dans les versions précédentes de XAML, vous deviez lui affecter une méthode avec une signature de gestionnaire d’événements spécifique, comprenant généralement des paramètres d’expéditeur de l’événement et un objet d'arguments propres à l’événement.In previous versions of XAML, you had to set this to a method with a specific event-handler signature, typically including parameters for the event sender and an event-specific arguments object. Vous pouvez toujours utiliser cette technique si vous avez besoin des arguments de l’événement, mais vous avez également la possibilité de vous connecter à d’autres méthodes avec x:Bind.You can still use this technique when you need the event arguments, but with x:Bind, you can connect to other methods, too. Par exemple, si les données de l’événement ne vous sont pas utiles, vous pouvez vous connecter à des méthodes dépourvues de paramètres, comme ici.For example, if you don't need the event data, you can connect to methods that have no parameters, like we do here.

  2. Dans MainPage.xaml.cs, ajoutez la méthode DeleteSelectedImage.In MainPage.xaml.cs, add the DeleteSelectedImage method.

    private void DeleteSelectedImage() =>
        Images.Remove(ImageGridView.SelectedItem as ImageFileInfo);
    

    Cette méthode supprime simplement l’image sélectionnée de la collection Images.This method simply deletes the selected image from the Images collection.

Maintenant, exécutez l’application et utilisez le bouton pour supprimer quelques images.Now run the app and use the button to delete a few images. Comme vous pouvez le constater, l’interface utilisateur est automatiquement mise à jour, grâce à la liaison de données et au type ObservableCollection<T>.As you can see, the UI is updated automatically, thanks to data binding and the ObservableCollection<T> type.

Notes

Ce code supprime uniquement l’instance de ImageFileInfo de la collection Images dans l’application en cours d’exécution.This code only deletes the ImageFileInfo instance from the Images collection in the running app. Il ne supprime pas le fichier image de l’ordinateur.It does not delete the image file from the computer.

Partie 3 : Configurer le curseur de zoomPart 3: Set up the zoom slider

Dans cette partie, vous allez créer des liaisons unidirectionnelles d’un contrôle situé dans le modèle de données au curseur de zoom, qui se trouve en dehors du modèle.In this part, you'll create one-way bindings from a control in the data template to the zoom slider, which is outside the template. Vous découvrirez également que vous pouvez utiliser une liaison de données avec de nombreuses propriétés de contrôle, et pas seulement les plus évidentes comme TextBlock.Text et Image.Source.You'll also learn that you can use data binding with many control properties, not just the most obvious ones like TextBlock.Text and Image.Source.

Lier le modèle de données d’image au curseur de zoomBind the image data template to the zoom slider

  • Recherchez le DataTemplate nommé ImageGridView_DefaultItemTemplate et remplacez les valeurs **Height** et Width du contrôle Grid en haut du modèle.Find the DataTemplate named ImageGridView_DefaultItemTemplate and replace the **Height** and Width values of the Grid control at the top of the template.

    AvantBefore

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="200"
              Width="200"
              Margin="{StaticResource LargeItemMargin}">
    

    AprèsAfter

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding Value, ElementName=ZoomSlider}"
              Width="{Binding Value, ElementName=ZoomSlider}"
              Margin="{StaticResource LargeItemMargin}">
    

Avez-vous remarqué que ce sont des expressions Binding et non des expressions x:Bind ?Did you notice that these are Binding expressions, and not x:Bind expressions? Il s’agit de l’ancienne méthode pour effectuer des liaisons de données, en grande partie obsolète.This is the old way of doing data bindings, and it's mostly obsolete. x:Bind fait presque tout ce que Binding fait et plus encore.x:Bind does nearly everything that Binding does, and more. Toutefois, dans un modèle de données, x:Bind se lie au type déclaré dans la valeur x:DataType.However, when you use x:Bind in a data template, it binds to the type declared in the x:DataType value. Comment lier un élément du modèle à un élément du code XAML de la page ou du code-behind ?So how do you bind something in the template to something in the page XAML or in code-behind? Il faut utiliser une expression Binding à l’ancienne.You must use an old-style Binding expression.

Les expressions Binding ne reconnaissent pas la valeur x:DataType, mais elles ont des valeurs Binding qui fonctionnent presque de la même manière.Binding expressions don't recognize the x:DataType value, but these Binding expressions have ElementName values that work almost the same way. Ces valeurs indiquent au moteur de liaison que Binding Value est une liaison avec la propriété Value de l’élément spécifié sur la page (autrement dit, l’élément possédant cette valeur x:Name).These tell the binding engine that Binding Value is a binding to the Value property of the specified element on the page (that is, the element with that x:Name value). Une liaison avec une propriété du code-behind ressemblerait à {Binding MyCodeBehindProperty, ElementName=page}, où page fait référence à la valeur x:Name définie dans l'élément Page dans le code XAML.If you want to bind to a property in code-behind, it would look something like {Binding MyCodeBehindProperty, ElementName=page} where page refers to the x:Name value set in the Page element in XAML.

Notes

Par défaut, les expressions Binding sont unidirectionnelles, ce qui signifie qu’elles mettent automatiquement à jour l’interface utilisateur lorsque la valeur de la propriété liée change.By default, Binding expressions are one-way, meaning that they will automatically update the UI when the bound property value changes.

En revanche, x:Bind est par défaut à usage unique, ce qui signifie que toutes les modifications apportées à la propriété liée sont ignorées.In contrast, the default for x:Bind is one-time, meaning that any changes to the bound property are ignored. Il s’agit de la valeur par défaut, car c'est l’option qui offre les plus hautes performances et la plupart des liaisons sont établies avec des données statiques en lecture seule.This is the default because it's the most high-performance option, and most bindings are to static, read-only data.

La leçon qu'il faut en tirer est que, si l’on utilise x:Bind avec des propriétés dont la valeur peut changer, il faut ajouter Mode=OneWay ou Mode=TwoWay.The lesson here is that if you use x:Bind with properties that can change their values, be sure to add Mode=OneWay or Mode=TwoWay. Vous trouverez des exemples dans la section suivante.You'll see examples of this in the next section.

Exécutez l’application et utilisez le curseur pour modifier les dimensions du modèle d’image.Run the app and use the slider to change the image-template dimensions. Comme vous pouvez le constater, l’effet est assez puissant avec peu de code.As you can see, the effect is pretty powerful without needing much code.

Application en cours d’exécution avec affichage du curseur de zoom

Notes

Petit défi : essayez de lier d’autres propriétés de l’interface utilisateur à la propriété Value du curseur de zoom, ou à d’autres curseurs que vous ajoutez après le curseur de zoom.For a challenge, try binding other UI properties to the zoom slider Value property, or to other sliders that you add after the zoom slider. Par exemple, vous pouvez lier la propriété FontSize du TitleTextBlock à un nouveau curseur avec la valeur par défaut 24.For example, you could bind the FontSize property of the TitleTextBlock to a new slider with a default value of 24. Veillez à définir des valeurs minimales et maximales raisonnables.Be sure to set reasonable minimum and maximum values.

Partie 4 : Améliorer l’expérience de zoomPart 4: Improve the zoom experience

Dans cette partie, vous allez ajouter une propriété ItemSize personnalisée au code-behind et créer des liaisons unidirectionnelles du modèle d'image à la nouvelle propriété.In this part, you'll add a custom ItemSize property to code-behind and create one-way bindings from the image template to the new property. La valeur ItemSize sera mise à jour par le curseur de zoom, ainsi que par d’autres facteurs comme le bouton bascule Ajuster à l’écran et la taille de la fenêtre, pour offrir une expérience plus précise.The ItemSize value will be updated by the zoom slider and other factors such as the Fit to screen toggle and the window size, making for a more refined experience.

Contrairement aux propriétés de contrôle intégrées, les propriétés personnalisées ne mettent pas automatiquement à jour l’interface utilisateur, même avec des liaisons unidirectionnelles et bidirectionnelles.Unlike built-in control properties, your custom properties do not automatically update the UI, even with one-way and two-way bindings. Elles fonctionnent bien avec des liaisons à usage unique, mais certaines opérations sont nécessaires pour que les modifications apportées aux propriétés apparaissent réellement dans l’interface utilisateur.They work fine with one-time bindings, but if you want your property changes to actually show up in your UI, you need to do some work.

Créer la propriété ItemSize afin qu’elle mette à jour l’interface utilisateurCreate the ItemSize property so that it updates the UI

  1. Dans MainPage.xaml.cs, modifiez la signature de la classe MainPage de sorte qu’elle implémente l'interface INotifyPropertyChanged.In MainPage.xaml.cs, change the signature of the MainPage class so that it implements the INotifyPropertyChanged interface.

    Avant :Before:

    public sealed partial class MainPage : Page
    

    Après :After:

    public sealed partial class MainPage : Page, INotifyPropertyChanged
    

    Le système de liaison est ainsi informé du fait que MainPage a un événement PropertyChanged (ajouté ensuite) que les liaisons peuvent détecter pour mettre à jour l’interface utilisateur.This informs the binding system that MainPage has a PropertyChanged event (added next) that bindings can listen for in order to update the UI.

  2. Ajoutez un événement PropertyChanged à la classe MainPage.Add a PropertyChanged event to the MainPage class.

    public event PropertyChangedEventHandler PropertyChanged;
    

    Cet événement fournit l’implémentation complète requise par l'interface INotifyPropertyChanged.This event provides the complete implementation required by the INotifyPropertyChanged interface. Toutefois, il est nécessaire de déclencher explicitement l’événement dans les propriétés personnalisées pour qu’il fasse effet.However, for it to have any effect, you must explicitly raise the event in your custom properties.

  3. Ajoutez une propriété ItemSize et déclenchez l'événement PropertyChanged dans sa méthode setter.Add an ItemSize property and raise the PropertyChanged event in its setter.

    public double ItemSize
    {
        get => _itemSize;
        set
        {
            if (_itemSize != value)
            {
                _itemSize = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemSize)));
            }
        }
    }
    private double _itemSize;
    

    La propriété ItemSize expose la valeur d’un champ _itemSize privé.The ItemSize property exposes the value of a private _itemSize field. Un champ de stockage comme celui-ci permet à la propriété de vérifier si la nouvelle valeur est identique à l’ancienne avant de déclencher un événement PropertyChanged potentiellement inutile.Using a backing field like this enables the property to check whether a new value is the same as the old value before it raises a potentially unnecessary PropertyChanged event.

    L’événement lui-même est déclenché par la méthode Invoke.The event itself is raised by the Invoke method. Le point d’interrogation vérifie si l'événement PropertyChanged est Null, c'est-à-dire si des gestionnaires d’événements ont déjà été ajoutés.The question mark checks whether the PropertyChanged event is null - that is, whether any event handlers have been added yet. Chaque liaison unidirectionnelle ou bidirectionnelle ajoute un gestionnaire d’événements en arrière-plan ; cependant, si aucun n’est à l’écoute, il ne se produit rien de plus.Every one-way or two-way binding adds an event handler behind the scenes, but if no one is listening, nothing more would happen here. Si en revanche PropertyChanged n’est pas Null, la méthode Invoke est appelée avec une référence à la source d’événement (la page elle-même, représentée par le mot clé this) et un objet event-args qui indique le nom de la propriété.If PropertyChanged isn't null, however, then Invoke is called with a reference to the event source (the page itself, represented by the this keyword) and an event-args object that indicates the name of the property. Grâce à ces informations, toutes les liaisons unidirectionnelles et bidirectionnelles avec la propriété ItemSize sont informées des modifications pour pouvoir mettre à jour l’interface utilisateur liée.With this info, any one-way or two-way bindings to the ItemSize property will be informed of any changes so they can update the bound UI.

  4. Dans MainPage.xaml, recherchez le DataTemplate nommé ImageGridView_DefaultItemTemplate et remplacez les valeurs Height et Width du contrôle Grid en haut du modèle.In MainPage.xaml, find the DataTemplate named ImageGridView_DefaultItemTemplate and replace the Height and Width values of the Grid control at the top of the template. (Si vous avez effectué la liaison de contrôle à contrôle dans la partie précédente de ce tutoriel, les seules modifications à apporter consistent à remplacer Value par ItemSize et ZoomSlider par page.(If you did the control-to-control binding in the previous part of this tutorial, the only changes are to replace Value with ItemSize and ZoomSlider with page. N’oubliez pas d’effectuer cette opération à la fois pour Height et pour Width !)Be sure to do this for both Height and Width!)

    AvantBefore

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding Value, ElementName=ZoomSlider}"
            Width="{Binding Value, ElementName=ZoomSlider}"
            Margin="{StaticResource LargeItemMargin}">
    

    AprèsAfter

    <DataTemplate x:Key="ImageGridView_DefaultItemTemplate"
                  x:DataType="local:ImageFileInfo">
        <Grid Height="{Binding ItemSize, ElementName=page}"
              Width="{Binding ItemSize, ElementName=page}"
              Margin="{StaticResource LargeItemMargin}">
    

Maintenant que l’interface utilisateur peut répondre aux modifications de ItemSize, vous devez effectuer quelques changements.Now that the UI can respond to ItemSize changes, you need to actually make some changes. Comme nous l’avons vu, la valeur ItemSize est calculée à partir de l’état actuel de plusieurs contrôles d’interface utilisateur, mais le calcul doit être effectué à chaque fois que ces contrôles changent d’état.As mentioned previously, the ItemSize value is calculated from the current state of various UI controls, but the calculation must be performed whenever those controls change state. Vous allez donc utiliser une liaison d’événement pour que certaines modifications de l’interface utilisateur appellent une méthode auxiliaire qui mette à jour ItemSize.To do this, you'll use event binding so that certain UI changes will call a helper method that updates ItemSize.

Mettre à jour la valeur de la propriété ItemSizeUpdate the ItemSize property value

  1. Ajoutez la méthode DetermineItemSize à MainPage.xaml.cs.Add the DetermineItemSize method to MainPage.xaml.cs.

    private void DetermineItemSize()
    {
        if (FitScreenToggle != null
            && FitScreenToggle.IsOn == true
            && ImageGridView != null
            && ZoomSlider != null)
        {
            // The 'margins' value represents the total of the margins around the
            // image in the grid item. 8 from the ItemTemplate root grid + 8 from
            // the ItemContainerStyle * (Right + Left). If those values change,
            // this value needs to be updated to match.
            int margins = (int)this.Resources["LargeItemMarginValue"] * 4;
            double gridWidth = ImageGridView.ActualWidth -
                (int)this.Resources["DefaultWindowSidePaddingValue"];
            double ItemWidth = ZoomSlider.Value + margins;
            // We need at least 1 column.
            int columns = (int)Math.Max(gridWidth / ItemWidth, 1);
    
            // Adjust the available grid width to account for margins around each item.
            double adjustedGridWidth = gridWidth - (columns * margins);
    
            ItemSize = (adjustedGridWidth / columns);
        }
        else
        {
            ItemSize = ZoomSlider.Value;
        }
    }
    
  2. Dans MainPage.xaml, naviguez vers le haut du fichier et ajoutez une liaison d’événement SizeChanged à l'élément Page.In MainPage.xaml, navigate to the top of the file and add a SizeChanged event binding to the Page element.

    Avant :Before:

    <Page x:Name="page"
    

    Après :After:

    <Page x:Name="page"
          SizeChanged="{x:Bind DetermineItemSize}"
    
  3. Recherchez le Slider nommé ZoomSlider (dans la section Page.Resources) et ajoutez une liaison d’événement ValueChanged.Find the Slider named ZoomSlider (in the Page.Resources section) and add a ValueChanged event binding.

    Avant :Before:

    <Slider x:Name="ZoomSlider"
    

    Après :After:

    <Slider x:Name="ZoomSlider"
            ValueChanged="{x:Bind DetermineItemSize}"
    
  4. Recherchez le ToggleSwitch nommé FitScreenToggle et ajoutez une liaison d’événement Toggled.Find the ToggleSwitch named FitScreenToggle and add a Toggled event binding.

    Avant :Before:

    <ToggleSwitch x:Name="FitScreenToggle"
    

    Après :After:

    <ToggleSwitch x:Name="FitScreenToggle"
                  Toggled="{x:Bind DetermineItemSize}"
    

Exécutez l’application et utilisez le curseur de zoom et le bouton bascule Ajuster à l’écran pour modifier les dimensions du modèle d’image.Run the app and use the zoom slider and Fit to screen toggle to change the image-template dimensions. Comme vous pouvez le constater, les dernières modifications offrent une expérience de zoom/redimensionnement plus précise sans désorganiser le code.As you can see, the latest changes enable a more refined zoom/resize experience while keeping the code well organized.

Application en cours d’exécution avec la fonction Ajuster à l’écran activée

Notes

Petit défi : essayez d’ajouter un TextBlock après le ZoomSlider et de lier la propriété Text à la propriété ItemSize.For a challenge, try adding a TextBlock after the ZoomSlider and binding the Text property to the ItemSize property. Dans la mesure où il ne se trouve pas dans un modèle de données, vous pouvez utiliser x:Bind au lieu de Binding comme dans les liaisons ItemSize précédentes.Because it's not in a data template, you can use x:Bind instead of Binding like in the previous ItemSize bindings.

Partie 5 : Autoriser les modifications par l’utilisateurPart 5: Enable user edits

Vous allez maintenant créer des liaisons bidirectionnelles pour permettre aux utilisateurs de mettre à jour les valeurs, notamment le titre de l’image, l'évaluation et divers effets visuels.Here, you'll create two-way bindings to enable users to update values, including the image title, rating, and various visual effects.

Pour cela, vous devez mettre à jour la DetailPage existante, qui fournit une visionneuse d’image unique, un contrôle de zoom et une interface utilisateur d'édition.To achieve this, you'll update the existing DetailPage, which provides a single-image viewer, zoom control, and editing UI.

Toutefois, il faut d’abord lier la DetailPage pour que l’application y accède lorsque l’utilisateur clique sur une image dans l'affichage de la galerie.First, however, you need to attach the DetailPage so that the app navigates to it when the user clicks an image in the gallery view.

Lier la DetailPageAttach the DetailPage

  1. Dans MainPage.xaml, recherchez le GridView nommé ImageGridView.In MainPage.xaml, find the GridView named ImageGridView. Pour rendre les éléments interactifs, définissez IsItemClickEnabled sur True, puis ajoutez un gestionnaire d’événements ItemClick.To make the items clickable, set IsItemClickEnabled to True and add an ItemClick event handler.

    Conseil

    Si vous tapez la modification ci-dessous au lieu de la copier/coller, vous verrez s’afficher une fenêtre contextuelle IntelliSense indiquant « <New Event Handler> ».If you type in the change below instead of copy/pasting, you'll see an IntelliSense pop-up that says "<New Event Handler>". Si vous appuyez sur la touche Tab, un nom de gestionnaire de méthode par défaut sera choisi comme valeur et la méthode présentée à l’étape suivante sera automatiquement remplacée.If you press the Tab key, it will fill in the value with a default method handler name, and automatically stub out the method shown in the next step. Vous pourrez ensuite appuyer sur F12 pour accéder à la méthode dans le code-behind.You can then press F12 to navigate to the method in the code-behind.

    Avant :Before:

    <GridView x:Name="ImageGridView">
    

    Après :After:

    <GridView x:Name="ImageGridView"
              IsItemClickEnabled="True"
              ItemClick="ImageGridView_ItemClick">
    

    Notes

    Nous utilisons ici un gestionnaire d’événements classique au lieu d’une expression x:Bind.We're using a conventional event handler here instead of an x:Bind expression. En effet, nous avons besoin de voir les données d’événement, comme indiqué ci-dessous.This is because we need to see the event data, as shown next.

  2. Dans MainPage.xaml.cs, ajoutez le gestionnaire d’événements (ou renseignez-le, si vous avez appliqué l'astuce de l’étape précédente).In MainPage.xaml.cs, add the event handler (or fill it in, if you used the tip in the last step).

    private void ImageGridView_ItemClick(object sender, ItemClickEventArgs e)
    {
        this.Frame.Navigate(typeof(DetailPage), e.ClickedItem);
    }
    

    Cette méthode accède simplement à la page de détails, en transmettant l’élément cliqué, qui est un objet ImageFileInfo utilisé par DetailPage.OnNavigatedTo pour l'initialisation de la page.This method simply navigates to the detail page, passing in the clicked item, which is an ImageFileInfo object used by DetailPage.OnNavigatedTo for initializing the page. Vous n’aurez pas à implémenter cette méthode dans ce tutoriel, mais vous pouvez y jeter un œil pour voir ce qu’elle fait.You won't have to implement that method in this tutorial, but you can take a look to see what it does.

  3. (Facultatif) Supprimez ou mettez en commentaires les contrôles ajoutés dans les précédents points de lecture qui fonctionnent avec l’image sélectionnée.(Optional) Delete or comment out any controls you added in previous play-points that work with the currently selected image. Les conserver ne pose pas de problème, mais il est maintenant beaucoup plus difficile de sélectionner une image sans accéder à la page de détails.Keeping them around won't hurt anything, but it's now a lot harder to select an image without navigating to the detail page.

Maintenant que vous avez connecté les deux pages, exécutez l’application et observez le résultat.Now that you've connected the two pages, run the app and take a look around. Tout fonctionne à l’exception des contrôles sur le volet d’édition, qui ne répondent pas lorsque vous essayez de modifier les valeurs.Everything works except the controls on the editing pane, which don't respond when you try to change the values.

Comme vous pouvez le constater, la zone de texte du titre affiche le titre et vous permet de taper des modifications.As you can see, the title text box does display the title and lets you type in changes. Vous devez déplacer le focus sur un autre contrôle pour valider les modifications, mais le titre qui se trouve en haut à gauche de l’écran ne se met pas encore à jour.You have to change focus to another control to commit the changes, but the title in the upper-left corner of the screen doesn't update yet.

Tous les contrôles sont déjà liés à l’aide des expressions x:Bind simples que nous avons présentées dans la partie 1.All the controls are already bound using the plain x:Bind expressions we covered in Part 1. Pour rappel, cela signifie que ce sont toutes des liaisons à usage unique, ce qui explique pourquoi les modifications apportées aux valeurs ne sont pas enregistrées.If you recall, this means they are all one-time bindings, which explains why changes to the values aren't registered. Pour résoudre ce problème, il suffit de les transformer en liaisons bidirectionnelles.To fix this, all we have to do is turn them into two-way bindings.

Rendre les contrôles d’édition interactifsMake the editing controls interactive

  1. Dans DetailPage.xaml, recherchez le TextBlock nommé TitleTextBlock et le contrôle RatingControl qui se trouve après, et mettez à jour leurs expressions x:Bind de façon à indiquer Mode=TwoWay.In DetailPage.xaml, find the TextBlock named TitleTextBlock and the RatingControl control after it, and update their x:Bind expressions to include Mode=TwoWay.

    Avant :Before:

    <TextBlock x:Name="TitleTextBlock"
               Text="{x:Bind item.ImageTitle}"
               ... >
    <muxc:RatingControl Value="{x:Bind item.ImageRating}"
                            ... >
    

    Après :After:

    <TextBlock x:Name="TitleTextBlock"
               Text="{x:Bind item.ImageTitle, Mode=TwoWay}"
               ... >
    <muxc:RatingControl Value="{x:Bind item.ImageRating, Mode=TwoWay}"
                            ... >
    
  2. Faites de même pour tous les curseurs d'effet qui suivent le contrôle d’évaluation.Do the same thing for all the effect sliders that come after the rating control.

    <Slider Header="Exposure"    ... Value="{x:Bind item.Exposure, Mode=TwoWay}" ...
    <Slider Header="Temperature" ... Value="{x:Bind item.Temperature, Mode=TwoWay}" ...
    <Slider Header="Tint"        ... Value="{x:Bind item.Tint, Mode=TwoWay}" ...
    <Slider Header="Contrast"    ... Value="{x:Bind item.Contrast, Mode=TwoWay}" ...
    <Slider Header="Saturation"  ... Value="{x:Bind item.Saturation, Mode=TwoWay}" ...
    <Slider Header="Blur"        ... Value="{x:Bind item.Blur, Mode=TwoWay}" ...
    

Le mode bidirectionnel signifie, comme on pourrait s’y attendre, que les données se déplacent dans les deux sens chaque fois que des modifications sont apportées de part et d'autre.The two-way mode, as you might expect, means that the data moves in both directions whenever there are changes on either side.

Comme les liaisons unidirectionnelles abordées précédemment, ces liaisons bidirectionnelles mettent à présent à jour l’interface utilisateur chaque fois que les propriétés liées changent, grâce à l'implémentation de INotifyPropertyChanged dans la classe ImageFileInfo.Like the one-way bindings covered earlier, these two-way bindings will now update the UI whenever the bound properties change, thanks to the INotifyPropertyChanged implementation in the ImageFileInfo class. Toutefois, les valeurs se déplacent également de l’interface utilisateur vers les propriétés liées à chaque fois que l’utilisateur interagit avec le contrôle.With two-way binding, however, the values will also move from the UI to the bound properties whenever the user interacts with the control. Rien d'autre n'est nécessaire du côté du code XAML.Nothing more is needed on the XAML side.

Exécutez l’application et essayez les contrôles d’édition.Run the app and try the editing controls. Comme vous pouvez le constater, les modifications effectuées affectent à présent les valeurs des images et sont conservées lorsque vous revenez à la page principale.As you can see, when you make a change, it now affects the image values, and those changes persist when you navigate back to the main page.

Partie 6 : Mettre en forme les valeurs par le biais d'une liaison de fonctionPart 6: Format values through function binding

Il reste un dernier problème à résoudre.One last problem remains. Lorsque vous déplacez les curseurs d'effet, les étiquettes qui se trouvent à côté ne changent toujours pas.When you move the effect sliders, the labels next to them still don't change.

Curseurs d'effet avec les valeurs d'étiquette par défaut

La dernière partie de ce tutoriel consiste à ajouter des liaisons qui mettent en forme la valeur des curseurs pour l’affichage.The final part in this tutorial is to add bindings that format the slider values for display.

Lier les étiquettes des curseurs d’effet et mettre en forme les valeurs pour l'affichageBind the effect-slider labels and format the values for display

  1. Recherchez le TextBlock après le curseur Exposure et remplacez la valeur Text par l’expression de liaison indiquée ici.Find the TextBlock after the Exposure slider and replace the Text value with the binding expression shown here.

    Avant :Before:

    <Slider Header="Exposure" ... />
    <TextBlock ... Text="0.00" />
    

    Après :After:

    <Slider Header="Exposure" ... />
    <TextBlock ... Text="{x:Bind item.Exposure.ToString('N', culture), Mode=OneWay}" />
    

    Ce type de liaison est appelé liaison de fonction, car il consiste à établir une liaison avec la valeur de retour d’une méthode.This is called a function binding because you are binding to the return value of a method. La méthode doit être accessible par le biais du code-behind de la page ou du type x:DataType si vous vous trouvez dans un modèle de données.The method must be accessible through the page's code-behind or the x:DataType type if you are in a data template. Dans ce cas, il faut utiliser la méthode .NET ToString bien connue, accessible au moyen de la propriété d’élément de la page, puis de la propriété Exposure de l’élément.In this case, the method is the familiar .NET ToString method, which is accessed through the item property of the page, and then through the Exposure property of the item. (Cet exemple montre comment établir des liaisons avec des méthodes et des propriétés profondément imbriquées dans une chaîne de connexions.)(This illustrates how you can bind to methods and properties that are deeply nested in a chain of connections.)

    La liaison de fonction est un moyen idéal de mettre en forme des valeurs pour l’affichage, car il est possible de transmettre d’autres sources de liaison comme arguments de méthode ; l’expression de liaison détectera les modifications apportées à ces valeurs comme avec le mode unidirectionnel.Function binding is an ideal way to format values for display because you can pass in other binding sources as method arguments, and the binding expression will listen for changes to those values as expected with the one-way mode. Dans cet exemple, l'argument culture est une référence à un champ immuable implémenté dans le code-behind, mais il aurait pu tout aussi bien s'agir d'une propriété qui déclenche des événements PropertyChanged.In this example, the culture argument is a reference to an unchanging field implemented in code-behind, but it could just as easily have been a property that raises PropertyChanged events. Dans ce cas, toute modification apportée à la valeur de propriété conduit l'expression x:Bind à appeler ToString avec la nouvelle valeur, puis à mettre à jour l’interface utilisateur avec le résultat.In that case, any changes to the property value would cause the x:Bind expression to call ToString with the new value and then update the UI with the result.

  2. Faites de même pour les TextBlock correspondant aux étiquettes des autres curseurs d’effet.Do the same thing for the TextBlocks that label the other effect sliders.

    <Slider Header="Temperature" ... />
    <TextBlock ... Text="{x:Bind item.Temperature.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Tint" ... />
    <TextBlock ... Text="{x:Bind item.Tint.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Contrast" ... />
    <TextBlock ... Text="{x:Bind item.Contrast.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Saturation" ... />
    <TextBlock ... Text="{x:Bind item.Saturation.ToString('N', culture), Mode=OneWay}" />
    
    <Slider Header="Blur" ... />
    <TextBlock ... Text="{x:Bind item.Blur.ToString('N', culture), Mode=OneWay}" />
    

À présent, lorsque vous exécutez l’application, tout fonctionne, y compris les étiquettes de curseur.Now when you run the app, everything works, including the slider labels.

Curseurs d'effet avec des étiquettes fonctionnelles

ConclusionConclusion

Ce tutoriel vous a donné un aperçu de la liaison de données et vous a montré quelques-unes des fonctionnalités disponibles.This tutorial has given you a taste of data binding and shown you some of the functionality available. Un mot d’avertissement avant de conclure : toutes les liaisons ne sont pas possibles et il peut arriver que des valeurs soient incompatibles avec certaines propriétés.One word of caution before we wrap up: not everything is bindable, and sometimes the values you try to connect to are incompatible with the properties you are trying to bind. La liaison offre une grande souplesse, mais elle ne fonctionne pas dans tous les cas.There is a lot of flexibility in binding, but it won't work in every situation.

Un exemple de problème non résolu par une liaison est le cas où un contrôle ne possède aucune propriété adaptée à la liaison, comme avec la fonctionnalité de zoom de la page de détails.One example of a problem not addressed by binding is when a control has no suitable properties to bind to, as with the detail page zoom feature. Ce curseur de zoom doit interagir avec le ScrollViewer qui affiche l’image, mais ScrollViewer ne peut être mis à jour que par le biais de sa méthode ChangeView.This zoom slider needs to interact with the ScrollViewer that displays the image, but ScrollViewer can only be updated through its ChangeView method. Ici, nous utilisons des gestionnaires d’événements conventionnels pour maintenir la synchronisation entre ScrollViewer et le curseur de zoom. Pour plus d’informations, consultez les méthodes ZoomSlider_ValueChanged et MainImageScroll_ViewChanged dans DetailPage.In this case, we use conventional event handlers to keep the ScrollViewer and the zoom slider in sync; see the ZoomSlider_ValueChanged and MainImageScroll_ViewChanged methods in DetailPage for details.

Néanmoins, la liaison est un moyen puissant et souple de simplifier le code et d'établir une distinction entre la logique de l’interface utilisateur et celle des données.Nonetheless, binding is a powerful and flexible way to simplify your code and keep your UI logic separate from your data logic. Cela facilite grandement les réglages de part et d’autre de cette division, tout en limitant les risques d’introduire des bogues de l’autre côté.This will make it much easier for you to adjust either side of this divide while reducing the risk of introducing bugs on the other side.

Un exemple de séparation des données et de l’interface utilisateur est l'utilisation de la propriété ImageFileInfo.ImageTitle.One example of UI and data separation is with the ImageFileInfo.ImageTitle property. Cette propriété (ainsi que la propriété ImageRating) est légèrement différente de la propriété ItemSize que vous avez créée dans la partie 4, car la valeur est stockée dans les métadonnées du fichier (exposées par le biais du type ImageProperties) plutôt que dans un champ.This property (and the ImageRating property) is slightly different than the ItemSize property you created in Part 4 because the value is stored in the file metadata (exposed through the ImageProperties type) instead of in a field. En outre, ImageTitle retourne la valeur ImageName (définie sur le nom de fichier) s’il n’existe aucun titre dans les métadonnées du fichier.Additionally, ImageTitle returns the ImageName value (set to the file name) if there is no title in the file metadata.

public string ImageTitle
{
    get => String.IsNullOrEmpty(ImageProperties.Title) ? ImageName : ImageProperties.Title;
    set
    {
        if (ImageProperties.Title != value)
        {
            ImageProperties.Title = value;
            var ignoreResult = ImageProperties.SavePropertiesAsync();
            OnPropertyChanged();
        }
    }
}

Comme vous pouvez le constater, la méthode setter met à jour la propriété ImageProperties.Title, puis appelle SavePropertiesAsync pour écrire la nouvelle valeur dans le fichier.As you can see, the setter updates the ImageProperties.Title property and then calls SavePropertiesAsync to write the new value to the file. (Il s’agit d’une méthode asynchrone, mais il n’est pas possible d’utiliser le mot clé await dans une propriété, ce qui n'est de toute façon pas souhaitable puisque les méthodes getter et setter de propriétés doivent se terminer immédiatement.(This is an async method, but we can't use the await keyword in a property - and you wouldn't want to because property getters and setters should complete immediately. Appelez plutôt la méthode et ignorez l’objet Task qu’elle retourne.)So instead, you would call the method and ignore the Task object it returns.)

Aller plus loinGoing further

Maintenant que vous avez suivi ce labo, vous avez suffisamment de connaissances en liaisons pour résoudre les problèmes par vous-même.Now that you've completed this lab, you have enough binding knowledge tackle a problem on your own.

Comme vous l'avez peut-être remarqué, si vous modifiez le niveau de zoom sur la page de détails, il se réinitialise automatiquement lorsque vous revenez en arrière, puis sélectionnez de nouveau la même image.As you might have noticed, if you change the zoom level on the details page, it resets automatically when you navigate backward then select the same image again. Saurez-vous trouver un moyen de conserver et de restaurer le niveau de zoom de chaque image individuellement ?Can you figure out how to preserve and restore the zoom level for each image individually? Bonne chance !Good luck!

Vous disposez normalement de toutes les informations nécessaires dans ce tutoriel, mais, si vous avez besoin d’une aide supplémentaire, il suffit d'un clic pour accéder à la documentation sur la liaison de données.You should have all the info you need in this tutorial, but if you need more guidance, the data binding docs are only a click away. Commencez ici :Start here: