Partager via


Mettre à niveau votre application avec des concepts MVVM

Ce tutoriel est conçu pour poursuivre le didacticiel Créer une application .NET MAUI , qui a créé une application de prise de notes. Dans ce tutoriel, vous apprendrez comment le faire :

  • Implémentez le modèle MVVM (Model-ViewModel).
  • Utilisez un style supplémentaire de chaîne de requête pour transmettre des données pendant la navigation.

Nous vous suggérons de suivre d’abord le didacticiel Créer une application MAUI .NET , car le code créé dans ce didacticiel est la base de ce didacticiel. Si vous avez perdu le code ou que vous souhaitez commencer à démarrer, téléchargez ce projet.

Comprendre MVVM

L’expérience du développeur .NET MAUI implique généralement la création d’une interface utilisateur en XAML, puis l’ajout de code-behind qui fonctionne sur l’interface utilisateur. Des problèmes de maintenance complexes peuvent survenir lorsque les applications sont modifiées et augmentent en taille et en étendue. Ces problèmes incluent le couplage étroit entre les contrôles de l’interface utilisateur et la logique métier, ce qui augmente le coût d’apporter des modifications à l’interface utilisateur et la difficulté de tester unitairement ce code.

Le modèle model-view-viewmodel (MVVM) permet de séparer correctement la logique métier et de présentation d’une application de son interface utilisateur (UI). La gestion d’une séparation propre entre la logique de l’application et l’interface utilisateur permet de résoudre de nombreux problèmes de développement et facilite le test, la maintenance et l’évolution d’une application. Il peut également améliorer considérablement les opportunités de réutilisation du code et permet aux développeurs et aux concepteurs d’interface utilisateur de collaborer plus facilement lors du développement de leurs parties respectives d’une application.

Modèle

Il existe trois composants principaux dans le modèle MVVM : le modèle, la vue et le modèle d’affichage. Chacun a un but distinct. Le diagramme suivant montre les relations entre les trois composants.

Diagramme illustrant les parties d’une application modélisée MVVM

En plus de comprendre les responsabilités de chaque composant, il est également important de comprendre comment ils interagissent. Au niveau conceptuel, la vue « connaît » le modèle de vue, et le modèle de vue « connaît » le modèle, mais le modèle ne connaît pas le modèle de vue, et le modèle de vue ne connaît pas la vue. Par conséquent, le modèle d’affichage isole la vue du modèle et permet au modèle d’évoluer indépendamment de la vue.

La clé de l’utilisation de MVVM consiste à comprendre comment factoriser le code d’application dans les classes correctes et comment les classes interagissent.

Afficher

La vue est chargée de définir la structure, la disposition et l’apparence de ce que l’utilisateur voit à l’écran. Dans l’idéal, chaque vue est définie en XAML, avec un code-behind limité qui ne contient pas de logique métier. Toutefois, dans certains cas, le code-behind peut contenir une logique d’interface utilisateur qui implémente un comportement visuel difficile à exprimer en XAML, comme les animations.

ViewModel

Le modèle d’affichage implémente des propriétés et des commandes auxquelles la vue peut lier des données et avertit l’affichage des modifications d’état par le biais d’événements de notification de modification. Les propriétés et les commandes fournies par le modèle d’affichage définissent les fonctionnalités à offrir par l’interface utilisateur, mais la vue détermine la façon dont cette fonctionnalité doit être affichée.

Le modèle de vue est également chargé de coordonner les interactions de la vue avec toutes les classes de modèle requises. Il existe généralement une relation un-à-plusieurs entre le modèle d’affichage et les classes de modèle.

Chaque modèle d’affichage fournit des données d’un modèle dans une forme que la vue peut facilement consommer. Pour ce faire, le modèle d’affichage effectue parfois une conversion de données. Placer cette conversion de données dans le modèle de vue est une bonne idée, car elle fournit des propriétés auxquelles la vue peut être liée. Par exemple, le modèle de vue peut combiner les valeurs de deux propriétés pour faciliter l'affichage par la vue.

Important

.NET MAUI gère les mises à jour des liaisons vers le thread de l'interface utilisateur. Lorsque vous utilisez MVVM, cela vous permet de mettre à jour les propriétés viewmodel liées aux données à partir de n’importe quel thread, avec le moteur de liaison de .NET MAUI qui apporte les mises à jour au thread d’interface utilisateur.

Modèle

Les classes de modèle sont des classes non visuelles qui encapsulent les données de l’application. Par conséquent, le modèle peut être considéré comme représentant le modèle de domaine de l’application, qui inclut généralement un modèle de données avec la logique métier et de validation.

Mettre à jour le modèle

Dans cette première partie du tutoriel, vous allez implémenter le modèle MVVM (model-view-viewmodel). Pour commencer, ouvrez la solution Notes.sln dans Visual Studio.

Nettoyer le modèle

Dans le tutoriel précédent, les types de modèles agissent à la fois comme modèle (données) et en tant que modèle de vue (préparation des données), qui a été mappé directement à une vue. Le tableau suivant décrit le modèle :

Fichier de code Descriptif
Modèles/About.cs Modèle About . Contient des champs en lecture seule qui décrivent l’application elle-même, telles que le titre et la version de l’application.
Modèles/Note.cs Modèle Note . Représente une note.
Modèles/AllNotes.cs Modèle AllNotes . Charge toutes les notes sur l’appareil dans une collection.

En pensant à l’application elle-même, il n’existe qu’un seul élément de données utilisé par l’application, le Note. Les notes sont chargées à partir de l’appareil, enregistrées sur l’appareil et modifiées via l’interface utilisateur de l’application. Il n’y a vraiment pas besoin des modèles About et AllNotes. Supprimez ces modèles du projet :

  1. Recherchez le volet Explorateur de solutions de Visual Studio.
  2. Cliquez avec le bouton droit sur le fichier Models\About.cs , puis sélectionnez Supprimer. Appuyez sur OK pour supprimer le fichier.
  3. Cliquez avec le bouton droit sur le fichier Models\AllNotes.cs , puis sélectionnez Supprimer. Appuyez sur OK pour supprimer le fichier.

Le seul fichier de modèle restant est le fichier Models\Note.cs .

Mettre à jour le modèle

Le Note modèle contient :

  • Identificateur unique, qui est le nom de fichier de la note, tel qu’il est stocké sur l’appareil.
  • Texte de la note.
  • Date à laquelle indiquer la date de création ou de dernière mise à jour de la note.

Actuellement, le chargement et l’enregistrement du modèle ont été effectués via les vues, et dans certains cas, par les autres types de modèles que vous venez de supprimer. Le code dont vous disposez pour le Note type doit être le suivant :

namespace Notes.Models;

internal class Note
{
    public string Filename { get; set; }
    public string Text { get; set; }
    public DateTime Date { get; set; }
}

Le Note modèle va être développé pour gérer le chargement, l’enregistrement et la suppression de notes.

  1. Dans le volet Explorateur de solutions de Visual Studio, double-cliquez sur Models\Note.cs.

  2. Dans l’éditeur de code, ajoutez les deux méthodes suivantes à la Note classe. Ces méthodes sont basées sur des instances et gèrent l’enregistrement ou la suppression de la note actuelle vers ou depuis l’appareil, respectivement :

    public void Save() =>
    File.WriteAllText(System.IO.Path.Combine(FileSystem.AppDataDirectory, Filename), Text);
    
    public void Delete() =>
        File.Delete(System.IO.Path.Combine(FileSystem.AppDataDirectory, Filename));
    
  3. L’application doit charger des notes de deux façons, charger une note individuelle à partir d’un fichier et charger toutes les notes sur l’appareil. Le code de gestion du chargement peut être static membres, ne nécessitant pas l'exécution d'une instance de classe.

    Ajoutez le code suivant à la classe pour charger une note par nom de fichier :

    public static Note Load(string filename)
    {
        filename = System.IO.Path.Combine(FileSystem.AppDataDirectory, filename);
    
        if (!File.Exists(filename))
            throw new FileNotFoundException("Unable to find file on local storage.", filename);
    
        return
            new()
            {
                Filename = Path.GetFileName(filename),
                Text = File.ReadAllText(filename),
                Date = File.GetLastWriteTime(filename)
            };
    }
    

    Ce code prend le nom de fichier en tant que paramètre, génère le chemin d’accès à l’emplacement où les notes sont stockées sur l’appareil et tente de charger le fichier s’il existe.

  4. La deuxième façon de charger des notes consiste à énumérer toutes les notes sur l’appareil et à les charger dans un regroupement.

    Ajoutez le code suivant à la classe :

    public static IEnumerable<Note> LoadAll()
    {
        // Get the folder where the notes are stored.
        string appDataPath = FileSystem.AppDataDirectory;
    
        // Use Linq extensions to load the *.notes.txt files.
        return Directory
    
                // Select the file names from the directory
                .EnumerateFiles(appDataPath, "*.notes.txt")
    
                // Each file name is used to load a note
                .Select(filename => Note.Load(Path.GetFileName(filename)))
    
                // With the final collection of notes, order them by date
                .OrderByDescending(note => note.Date);
    }
    

    Ce code retourne une collection énumérable de types de Note modèles en récupérant les fichiers sur l’appareil qui correspondent au modèle de fichier de notes : *.notes.txt. Chaque nom de fichier est passé à la méthode Load, chargeant une note individuelle. Enfin, la collection de notes est triée selon la date de chaque note et retournée à l’appelant.

  5. Enfin, ajoutez un constructeur à la classe qui définit les valeurs par défaut pour les propriétés, y compris un nom de fichier aléatoire :

    public Note()
    {
        Filename = $"{Path.GetRandomFileName()}.notes.txt";
        Date = DateTime.Now;
        Text = "";
    }
    

Le Note code de classe doit ressembler à ce qui suit :

namespace Notes.Models;

internal class Note
{
    public string Filename { get; set; }
    public string Text { get; set; }
    public DateTime Date { get; set; }

    public Note()
    {
        Filename = $"{Path.GetRandomFileName()}.notes.txt";
        Date = DateTime.Now;
        Text = "";
    }

    public void Save() =>
    File.WriteAllText(System.IO.Path.Combine(FileSystem.AppDataDirectory, Filename), Text);

    public void Delete() =>
        File.Delete(System.IO.Path.Combine(FileSystem.AppDataDirectory, Filename));

    public static Note Load(string filename)
    {
        filename = System.IO.Path.Combine(FileSystem.AppDataDirectory, filename);

        if (!File.Exists(filename))
            throw new FileNotFoundException("Unable to find file on local storage.", filename);

        return
            new()
            {
                Filename = Path.GetFileName(filename),
                Text = File.ReadAllText(filename),
                Date = File.GetLastWriteTime(filename)
            };
    }

    public static IEnumerable<Note> LoadAll()
    {
        // Get the folder where the notes are stored.
        string appDataPath = FileSystem.AppDataDirectory;

        // Use Linq extensions to load the *.notes.txt files.
        return Directory

                // Select the file names from the directory
                .EnumerateFiles(appDataPath, "*.notes.txt")

                // Each file name is used to load a note
                .Select(filename => Note.Load(Path.GetFileName(filename)))

                // With the final collection of notes, order them by date
                .OrderByDescending(note => note.Date);
    }
}

Maintenant que le Note modèle est terminé, les modèles d’affichage peuvent être créés.

Créer le modèle de vue de la section 'À propos'

Avant d’ajouter des modèles d’affichage au projet, ajoutez une référence au kit de ressources de la communauté MVVM. Cette bibliothèque est disponible sur NuGet et fournit des types et des systèmes qui aident à implémenter le modèle MVVM.

  1. Dans le volet Explorateur de solutions de Visual Studio, cliquez avec le bouton droit sur le projet >Gérer les packages NuGet.

  2. Sélectionnez l’onglet Parcourir.

  3. Recherchez communitytoolkit mvvm et sélectionnez le CommunityToolkit.Mvvm package, qui doit être le premier résultat.

  4. Vérifiez qu’au moins la version 8 est sélectionnée. Ce tutoriel a été écrit à l’aide de la version 8.0.0.

  5. Ensuite, sélectionnez Installer et accepter les invites affichées.

    Recherchez le package CommunityToolkit.Mvvm dans NuGet.

Vous êtes maintenant prêt à commencer à mettre à jour le projet en ajoutant des modèles d’affichage.

Dissocier avec des modèles d’affichage

La relation view-to-viewmodel repose fortement sur le système de liaison fourni par l’interface utilisateur de l’application multiplateforme .NET (.NET MAUI). L’application utilise déjà la liaison de données dans les affichages pour afficher une liste de notes et présenter le texte et la date d’une seule note. La logique d’application est actuellement fournie par le « code-behind » de la vue et y est directement liée. Par exemple, lorsqu’un utilisateur modifie une note et appuie sur le bouton Enregistrer , l’événement Clicked du bouton est déclenché. Ensuite, le code-behind du gestionnaire d’événements enregistre le texte de note dans un fichier et accède à l’écran précédent.

L’utilisation de la logique d’application dans le « code-behind » d’une vue peut poser problème lorsque la vue change. Par exemple, si le bouton est remplacé par un autre contrôle d’entrée ou que le nom d’un contrôle est modifié, les gestionnaires d’événements peuvent devenir non valides. Quelle que soit la façon dont la vue est conçue, l’objectif de la vue est d’appeler une sorte de logique d’application et de présenter des informations à l’utilisateur. Pour cette application, le Save bouton enregistre la note, puis revient à l’écran précédent.

Le viewmodel donne à l’application un emplacement spécifique pour placer la logique de l’application, quelle que soit la façon dont l’interface utilisateur est conçue ou la façon dont les données sont chargées ou enregistrées. Le viewmodel est la colle qui représente et interagit avec le modèle de données pour le compte de la vue.

Les modèles d’affichage sont stockés dans un dossier ViewModels .

  1. Recherchez le volet Explorateur de solutions de Visual Studio.
  2. Cliquez avec le bouton droit sur le projet Notes , puis sélectionnez Ajouter>un nouveau dossier. Nommez le dossier ViewModels.
  3. Cliquez avec le bouton droit sur le dossier >Ajouter> uneclasse et nommez-la AboutViewModel.cs.
  4. Répétez l’étape précédente et créez deux modèles d’affichage supplémentaires :
    • NoteViewModel.cs
    • NotesViewModel.cs

Votre structure de projet doit ressembler à l’image suivante :

Explorateur de solutions affichant les dossiers MVVM.

À propos de viewmodel et à propos de l’affichage

L’affichage À propos affiche certaines données à l’écran et accède éventuellement à un site web avec plus d’informations. Étant donné que cette vue n’a pas de données à modifier, comme avec un contrôle d’entrée de texte ou la sélection d’éléments dans une liste, il est judicieux d’illustrer l’ajout d’un viewmodel. Pour le About viewmodel, il n’y a pas de modèle de support.

Créez le About viewmodel:

  1. Dans le volet Explorateur de solutions de Visual Studio, double-cliquez sur ViewModels\AboutViewModel.cs.

  2. Collez le code suivant :

    using CommunityToolkit.Mvvm.Input;
    using System.Windows.Input;
    
    namespace Notes.ViewModels;
    
    internal class AboutViewModel
    {
        public string Title => AppInfo.Name;
        public string Version => AppInfo.VersionString;
        public string MoreInfoUrl => "https://aka.ms/maui";
        public string Message => "This app is written in XAML and C# with .NET MAUI.";
        public ICommand ShowMoreInfoCommand { get; }
    
        public AboutViewModel()
        {
            ShowMoreInfoCommand = new AsyncRelayCommand(ShowMoreInfo);
        }
    
        async Task ShowMoreInfo() =>
            await Launcher.Default.OpenAsync(MoreInfoUrl);
    }
    

L’extrait de code précédent contient certaines propriétés qui représentent des informations sur l’application, telles que le nom et la version. Cet extrait est exactement identique au modèle À propos que vous avez supprimé précédemment. Toutefois, ce viewmodel contient un nouveau concept, la ShowMoreInfoCommand propriété de commande.

Les commandes sont des actions pouvant être liées qui appellent du code et constituent un excellent endroit pour mettre la logique de l’application. Dans cet exemple, le ShowMoreInfoCommand pointe vers la méthode ShowMoreInfo, ouvrant le navigateur web sur une page spécifique. Vous en apprendrez davantage sur le système de commandes dans la section suivante.

À propos de l’affichage

La vue About doit être légèrement modifiée pour la raccorder au viewmodel créé dans la section précédente. Dans le fichier Views\AboutPage.xaml , appliquez les modifications suivantes :

  • Mettez à jour l'espace xmlns:models de noms XML pour xmlns:viewModels et définissez l'espace Notes.ViewModels de noms .NET comme cible.
  • Remplacez la ContentPage.BindingContext propriété par une nouvelle instance du About viewmodel.
  • Supprimez le gestionnaire d’événements du Clicked bouton et utilisez la Command propriété.

Mettez à jour l’affichage À propos de :

  1. Dans le volet Explorateur de solutions de Visual Studio, double-cliquez sur Views\AboutPage.xaml.

  2. Collez le code suivant :

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:viewModels="clr-namespace:Notes.ViewModels"
                 x:Class="Notes.Views.AboutPage"
                 x:DataType="viewModels:AboutViewModel">
        <ContentPage.BindingContext>
            <viewModels:AboutViewModel />
        </ContentPage.BindingContext>
        <VerticalStackLayout Spacing="10" Margin="10">
            <HorizontalStackLayout Spacing="10">
                <Image Source="dotnet_bot.png"
                       SemanticProperties.Description="The dot net bot waving hello!"
                       HeightRequest="64" />
                <Label FontSize="22" FontAttributes="Bold" Text="{Binding Title}" VerticalOptions="End" />
                <Label FontSize="22" Text="{Binding Version}" VerticalOptions="End" />
            </HorizontalStackLayout>
    
            <Label Text="{Binding Message}" />
            <Button Text="Learn more..." Command="{Binding ShowMoreInfoCommand}" />
        </VerticalStackLayout>
    
    </ContentPage>
    

    L’extrait de code précédent met en évidence les lignes qui ont changé dans cette version de la vue.

Notez que le bouton utilise la Command propriété. De nombreux contrôles ont une Command propriété appelée lorsque l’utilisateur interagit avec le contrôle. Lorsqu’il est utilisé avec un bouton, la commande est appelée lorsqu’un utilisateur appuie sur le bouton, similaire à la façon dont le Clicked gestionnaire d’événements est appelé, sauf que vous pouvez lier Command à une propriété dans le viewmodel.

Dans cette vue, lorsque l’utilisateur appuie sur le bouton, Command est appelé. La Command propriété est liée à la ShowMoreInfoCommand propriété dans le viewmodel, et lorsqu’elle est appelée, exécute le code dans la ShowMoreInfo méthode, ce qui ouvre le navigateur web à une page spécifique.

Nettoyer le code-behind de la section À propos

Le ShowMoreInfo bouton n’utilise pas le gestionnaire d’événements. Le LearnMore_Clicked code doit donc être supprimé du fichier Views\AboutPage.xaml.cs . Supprimez ce code, la classe ne doit contenir que le constructeur :

  1. Dans le volet Explorateur de solutions de Visual Studio, double-cliquez sur Views\AboutPage.xaml.cs.

    Conseil / Astuce

    Vous devrez peut-être développer Views\AboutPage.xaml pour afficher le fichier.

  2. Remplacez le code par l’extrait de code suivant :

    namespace Notes.Views;
    
    public partial class AboutPage : ContentPage
    {
        public AboutPage()
        {
            InitializeComponent();
        }
    }
    

Créer le modèle de vue Note

L’objectif de la mise à jour de la vue Note est de déplacer autant de fonctionnalités que possible hors du code-behind XAML et de le placer dans le modèle de vue Note.

Remarque modèle de vue

En fonction de ce que nécessite l’affichage Note, le modèle de vue Note doit fournir les éléments suivants :

  • Texte de la note.
  • Date/heure de création ou de dernière mise à jour de la note.
  • Commande qui enregistre la note.
  • Commande qui supprime la note.

Créez le ViewModel Note :

  1. Dans le volet Explorateur de solutions de Visual Studio, double-cliquez sur ViewModels\NoteViewModel.cs.

  2. Remplacez le code dans ce fichier par l’extrait de code suivant :

    using CommunityToolkit.Mvvm.Input;
    using CommunityToolkit.Mvvm.ComponentModel;
    using System.Windows.Input;
    
    namespace Notes.ViewModels;
    
    internal class NoteViewModel : ObservableObject, IQueryAttributable
    {
        private Models.Note _note;
    
    }
    

    Ce code correspond au viewmodel Note vide auquel vous allez ajouter des propriétés et des commandes pour prendre en charge la vue Note. Notez que l'espace de noms CommunityToolkit.Mvvm.ComponentModel est importé. Cet espace de noms fournit le ObservableObject utilisé comme classe de base. Vous apprendrez davantage sur ObservableObject à l’étape suivante. L’espace CommunityToolkit.Mvvm.Input de noms est également importé. Cet espace de noms fournit certains types de commandes qui appellent des méthodes de manière asynchrone.

    Le Models.Note modèle est stocké en tant que champ privé. Les propriétés et méthodes de cette classe utilisent ce champ.

  3. Ajoutez les propriétés suivantes à la classe :

    public string Text
    {
        get => _note.Text;
        set
        {
            if (_note.Text != value)
            {
                _note.Text = value;
                OnPropertyChanged();
            }
        }
    }
    
    public DateTime Date => _note.Date;
    
    public string Identifier => _note.Filename;
    

    Les propriétés Date et Identifier sont des propriétés simples qui récupèrent simplement les valeurs correspondantes du modèle.

    Conseil / Astuce

    Pour les propriétés, la => syntaxe crée une propriété get uniquement où l’instruction à droite de => doit évaluer une valeur à retourner.

    La Text propriété vérifie d’abord si la valeur définie est une valeur différente. Si la valeur est différente, cette valeur est transmise à la propriété du modèle et la OnPropertyChanged méthode est appelée.

    La OnPropertyChanged méthode est fournie par la classe de ObservableObject base. Cette méthode utilise le nom du code appelant, dans ce cas, le nom de la propriété Text et déclenche l’événement ObservableObject.PropertyChanged . Cet événement fournit le nom de la propriété à tous les abonnés aux événements. Le système de liaison fourni par .NET MAUI reconnaît cet événement et met à jour toutes les liaisons associées dans l’interface utilisateur. Pour le viewmodel note, lorsque la Text propriété change, l’événement est déclenché et tout élément d’interface utilisateur lié à la Text propriété est averti que la propriété a changé.

  4. Ajoutez les propriétés de commande suivantes à la classe, qui sont les commandes auxquelles la vue peut être liée :

    public ICommand SaveCommand { get; private set; }
    public ICommand DeleteCommand { get; private set; }
    
  5. Ajoutez les constructeurs suivants à la classe :

    public NoteViewModel()
    {
        _note = new Models.Note();
        SaveCommand = new AsyncRelayCommand(Save);
        DeleteCommand = new AsyncRelayCommand(Delete);
    }
    
    public NoteViewModel(Models.Note note)
    {
        _note = note;
        SaveCommand = new AsyncRelayCommand(Save);
        DeleteCommand = new AsyncRelayCommand(Delete);
    }
    

    Ces deux constructeurs sont utilisés pour créer le modèle de vue avec un nouveau modèle de support, qui est une note vide, ou pour créer un modèle de vue qui utilise l'instance de modèle spécifiée.

    Les constructeurs configurent également les commandes pour le viewmodel. Ensuite, ajoutez le code pour ces commandes.

  6. Ajoutez les méthodes Save et Delete.

    private async Task Save()
    {
        _note.Date = DateTime.Now;
        _note.Save();
        await Shell.Current.GoToAsync($"..?saved={_note.Filename}");
    }
    
    private async Task Delete()
    {
        _note.Delete();
        await Shell.Current.GoToAsync($"..?deleted={_note.Filename}");
    }
    

    Ces méthodes sont appelées par les commandes associées. Ils effectuent les actions associées sur le modèle et permettent à l’application d’accéder à la page précédente. Un paramètre de chaîne de requête est ajouté au .. chemin de navigation, indiquant quelle action a été effectuée et l’identificateur unique de la note.

  7. Ensuite, ajoutez la ApplyQueryAttributes méthode à la classe, qui répond aux exigences de l’interface IQueryAttributable :

    void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query)
    {
        if (query.ContainsKey("load"))
        {
            _note = Models.Note.Load(query["load"].ToString());
            RefreshProperties();
        }
    }
    

    Lorsqu’une page ou le contexte de liaison d’une page implémente cette interface, les paramètres de chaîne de requête utilisés dans la navigation sont passés à la ApplyQueryAttributes méthode. Ce viewmodel est utilisé comme contexte de liaison pour la vue Remarque. Lorsque le mode Note est consulté, le contexte de liaison de ce modèle de vue est transmis aux paramètres de la chaîne de requête utilisés pendant la navigation.

    Ce code vérifie si la load clé a été fournie dans le query dictionnaire. Si cette clé est trouvée, la valeur doit être l’identificateur (nom de fichier) de la note à charger. Cette note est chargée et définie comme l'objet modèle sous-jacent de cette instance de vue modèle.

  8. Enfin, ajoutez ces deux méthodes d’assistance à la classe :

    public void Reload()
    {
        _note = Models.Note.Load(_note.Filename);
        RefreshProperties();
    }
    
    private void RefreshProperties()
    {
        OnPropertyChanged(nameof(Text));
        OnPropertyChanged(nameof(Date));
    }
    

    La méthode Reload est une méthode d'assistance qui actualise l'objet du modèle sous-jacent, en le rechargeant à partir du stockage de l'appareil.

    La méthode RefreshProperties est une méthode d’assistance supplémentaire pour s’assurer que tous les abonnés liés à cet objet sont avertis que les propriétés Text et Date ont changé. Étant donné que le modèle sous-jacent (le champ _note) est modifié lorsque la note est chargée pendant la navigation, les propriétés Text et Date ne reçoivent pas réellement de nouvelles valeurs. Étant donné que ces propriétés ne sont pas directement définies, les liaisons attachées à ces propriétés ne seraient pas informées, car OnPropertyChanged n'est pas appelé pour chaque propriété. RefreshProperties garantit que les liaisons à ces propriétés sont actualisées.

Le code de la classe doit ressembler à l’extrait de code suivant :

using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Windows.Input;

namespace Notes.ViewModels;

internal class NoteViewModel : ObservableObject, IQueryAttributable
{
    private Models.Note _note;

    public string Text
    {
        get => _note.Text;
        set
        {
            if (_note.Text != value)
            {
                _note.Text = value;
                OnPropertyChanged();
            }
        }
    }

    public DateTime Date => _note.Date;

    public string Identifier => _note.Filename;

    public ICommand SaveCommand { get; private set; }
    public ICommand DeleteCommand { get; private set; }

    public NoteViewModel()
    {
        _note = new Models.Note();
        SaveCommand = new AsyncRelayCommand(Save);
        DeleteCommand = new AsyncRelayCommand(Delete);
    }

    public NoteViewModel(Models.Note note)
    {
        _note = note;
        SaveCommand = new AsyncRelayCommand(Save);
        DeleteCommand = new AsyncRelayCommand(Delete);
    }

    private async Task Save()
    {
        _note.Date = DateTime.Now;
        _note.Save();
        await Shell.Current.GoToAsync($"..?saved={_note.Filename}");
    }

    private async Task Delete()
    {
        _note.Delete();
        await Shell.Current.GoToAsync($"..?deleted={_note.Filename}");
    }

    void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query)
    {
        if (query.ContainsKey("load"))
        {
            _note = Models.Note.Load(query["load"].ToString());
            RefreshProperties();
        }
    }

    public void Reload()
    {
        _note = Models.Note.Load(_note.Filename);
        RefreshProperties();
    }

    private void RefreshProperties()
    {
        OnPropertyChanged(nameof(Text));
        OnPropertyChanged(nameof(Date));
    }
}

Affichage de la note

Maintenant que le modèle-vue a été créé, mettez à jour la vue Note. Dans le fichier Views\NotePage.xaml , appliquez les modifications suivantes :

  • Ajoutez l’espace xmlns:viewModels de noms XML qui cible l’espace Notes.ViewModels de noms .NET.
  • Ajoutez un BindingContext à la page.
  • Supprimez les gestionnaires d’événements des boutons Clicked de suppression et d’enregistrement et remplacez-les par des commandes.

Mettez à jour la vue des notes :

  1. Dans le volet Explorateur de solutions de Visual Studio, double-cliquez sur Views\NotePage.xaml pour ouvrir l’éditeur XAML.

  2. Collez le code suivant :

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:viewModels="clr-namespace:Notes.ViewModels"
                 x:Class="Notes.Views.NotePage"
                 Title="Note"
                 x:DataType="viewModels:NoteViewModel">
        <ContentPage.BindingContext>
            <viewModels:NoteViewModel />
        </ContentPage.BindingContext>
        <VerticalStackLayout Spacing="10" Margin="5">
            <Editor x:Name="TextEditor"
                    Placeholder="Enter your note"
                    Text="{Binding Text}"
                    HeightRequest="100" />
    
            <Grid ColumnDefinitions="*,*" ColumnSpacing="4">
                <Button Text="Save"
                        Command="{Binding SaveCommand}"/>
    
                <Button Grid.Column="1"
                        Text="Delete"
                        Command="{Binding DeleteCommand}"/>
    
            </Grid>
        </VerticalStackLayout>
    </ContentPage>
    

Auparavant, cette vue n'avait pas déclaré de contexte de liaison, car il était fourni par le code sous-jacent de la page elle-même. La définition du contexte de liaison directement dans le code XAML fournit deux éléments :

  • Au moment de l’exécution, lorsque la page est accédée, elle affiche une note vide. Cela est dû au fait que le constructeur sans paramètre du contexte de liaison, viewmodel, est appelé. Si vous vous en souvenez bien, le constructeur sans paramètre pour le modèle de vue de la note crée une note vide.

  • IntelliSense dans l’éditeur XAML affiche les propriétés disponibles dès que vous commencez à taper la syntaxe {Binding. La syntaxe est également validée et vous avertit d’une valeur non valide. Essayez de modifier la syntaxe de liaison de SaveCommand à Save123Command. Si vous placez le curseur de la souris sur le texte, vous remarquerez qu’une info-bulle s’affiche pour vous informer que Save123Command n’est pas trouvé. Cette notification n’est pas considérée comme une erreur, car les liaisons sont dynamiques, c’est vraiment un petit avertissement qui peut vous aider à remarquer lorsque vous avez tapé la propriété incorrecte.

    Si vous avez modifié SaveCommand en une autre valeur, restaurez-le maintenant.

Nettoyer le code-behind de la Note

Maintenant que l’interaction avec la vue a changé de gestionnaires d’événements en commandes, ouvrez le fichier Views\NotePage.xaml.cs et remplacez tout le code par une classe qui contient uniquement le constructeur :

  1. Dans le volet Explorateur de solutions de Visual Studio, double-cliquez sur Views\NotePage.xaml.cs.

    Conseil / Astuce

    Vous devrez peut-être développer Views\NotePage.xaml pour afficher le fichier.

  2. Remplacez le code par l’extrait de code suivant :

    namespace Notes.Views;
    
    public partial class NotePage : ContentPage
    {
        public NotePage()
        {
            InitializeComponent();
        }
    }
    

Créer le modèle de vue Notes

La paire viewmodel-view finale est la vue Notes viewmodel et AllNotes. Actuellement, la vue est directement liée au modèle, qui a été supprimé au début de ce didacticiel. L’objectif de la mise à jour de la vue AllNotes consiste à déplacer autant de fonctionnalités que possible hors du code-behind XAML et à le placer dans le viewmodel. Là encore, l'avantage est que la vue peut changer son design avec peu d'impact sur votre code.

Notes du modèle de vue

En fonction de ce que l’affichage AllNotes va afficher et quelles interactions l’utilisateur effectuera, le mode d’affichage Notes doit fournir les éléments suivants :

  • Collection de notes.
  • Commande permettant de gérer la navigation vers une note.
  • Commande permettant de créer une note.
  • Mettez à jour la liste des notes lorsqu’une note est créée, supprimée ou modifiée.

Créez le viewmodel Notes :

  1. Dans le volet Explorateur de solutions de Visual Studio, double-cliquez sur ViewModels\NotesViewModel.cs.

  2. Remplacez le code dans ce fichier par le code suivant :

    using CommunityToolkit.Mvvm.Input;
    using System.Collections.ObjectModel;
    using System.Windows.Input;
    
    namespace Notes.ViewModels;
    
    internal class NotesViewModel: IQueryAttributable
    {
    }
    

    Ce code est vide NotesViewModel dans lequel vous allez ajouter des propriétés et des commandes pour prendre en charge l’affichage AllNotes .

  3. Dans le code de NotesViewModel classe, ajoutez les propriétés suivantes :

    public ObservableCollection<ViewModels.NoteViewModel> AllNotes { get; }
    public ICommand NewCommand { get; }
    public ICommand SelectNoteCommand { get; }
    

    La AllNotes propriété est une ObservableCollection propriété qui stocke toutes les notes chargées à partir de l’appareil. Les deux commandes seront utilisées par la vue pour déclencher les actions de création d’une note ou de sélection d’une note existante.

  4. Ajoutez un constructeur sans paramètre à la classe, qui initialise les commandes et charge les notes du modèle :

    public NotesViewModel()
    {
        AllNotes = new ObservableCollection<ViewModels.NoteViewModel>(Models.Note.LoadAll().Select(n => new NoteViewModel(n)));
        NewCommand = new AsyncRelayCommand(NewNoteAsync);
        SelectNoteCommand = new AsyncRelayCommand<ViewModels.NoteViewModel>(SelectNoteAsync);
    }
    

    Notez que la AllNotes collection utilise la Models.Note.LoadAll méthode pour remplir la collection observable avec des notes. La LoadAll méthode retourne les notes comme type Models.Note , mais la collection observable est une collection de ViewModels.NoteViewModel types. Le code utilise l’extension Select Linq pour créer des instances viewmodel à partir des modèles de note retournés par LoadAll.

  5. Créez les méthodes ciblées par les commandes :

    private async Task NewNoteAsync()
    {
        await Shell.Current.GoToAsync(nameof(Views.NotePage));
    }
    
    private async Task SelectNoteAsync(ViewModels.NoteViewModel note)
    {
        if (note != null)
            await Shell.Current.GoToAsync($"{nameof(Views.NotePage)}?load={note.Identifier}");
    }
    

    Notez que la NewNoteAsync méthode ne prend pas de paramètre pendant la SelectNoteAsync procédure. Les commandes peuvent éventuellement avoir un paramètre unique fourni lorsque la commande est appelée. Pour la SelectNoteAsync méthode, le paramètre représente la note sélectionnée.

  6. Enfin, implémentez la IQueryAttributable.ApplyQueryAttributes méthode :

    void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query)
    {
        if (query.ContainsKey("deleted"))
        {
            string noteId = query["deleted"].ToString();
            NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault();
    
            // If note exists, delete it
            if (matchedNote != null)
                AllNotes.Remove(matchedNote);
        }
        else if (query.ContainsKey("saved"))
        {
            string noteId = query["saved"].ToString();
            NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault();
    
            // If note is found, update it
            if (matchedNote != null)
                matchedNote.Reload();
    
            // If note isn't found, it's new; add it.
            else
                AllNotes.Add(new NoteViewModel(Note.Load(noteId)));
        }
    }
    

    Le viewmodel note créé à l’étape précédente du didacticiel, a utilisé la navigation lorsque la note a été enregistrée ou supprimée. Le viewmodel retournait à la vue AllNotes à laquelle il est associé. Ce code détecte si la chaîne de requête contient soit la clé deleted soit la clé saved. La valeur de la clé est l’identificateur unique de la note.

    Si la note a été supprimée, cette note est associée à la collection par l'identificateur fourni, et ensuite supprimée.

    Il existe deux raisons possibles pour lesquelles une note est enregistrée. La note a été soit tout juste créée, soit une note existante a été modifiée. Si la note se trouve déjà dans la AllNotes collection, il s’agit d’une note mise à jour. Dans ce cas, l’instance de note de la collection doit simplement être actualisée. Si la note est manquante dans la collection, il s’agit d’une nouvelle note et doit être ajoutée à la collection.

Le code de la classe doit ressembler à l’extrait de code suivant :

using CommunityToolkit.Mvvm.Input;
using Notes.Models;
using System.Collections.ObjectModel;
using System.Windows.Input;

namespace Notes.ViewModels;

internal class NotesViewModel : IQueryAttributable
{
    public ObservableCollection<ViewModels.NoteViewModel> AllNotes { get; }
    public ICommand NewCommand { get; }
    public ICommand SelectNoteCommand { get; }

    public NotesViewModel()
    {
        AllNotes = new ObservableCollection<ViewModels.NoteViewModel>(Models.Note.LoadAll().Select(n => new NoteViewModel(n)));
        NewCommand = new AsyncRelayCommand(NewNoteAsync);
        SelectNoteCommand = new AsyncRelayCommand<ViewModels.NoteViewModel>(SelectNoteAsync);
    }

    private async Task NewNoteAsync()
    {
        await Shell.Current.GoToAsync(nameof(Views.NotePage));
    }

    private async Task SelectNoteAsync(ViewModels.NoteViewModel note)
    {
        if (note != null)
            await Shell.Current.GoToAsync($"{nameof(Views.NotePage)}?load={note.Identifier}");
    }

    void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query)
    {
        if (query.ContainsKey("deleted"))
        {
            string noteId = query["deleted"].ToString();
            NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault();

            // If note exists, delete it
            if (matchedNote != null)
                AllNotes.Remove(matchedNote);
        }
        else if (query.ContainsKey("saved"))
        {
            string noteId = query["saved"].ToString();
            NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault();

            // If note is found, update it
            if (matchedNote != null)
                matchedNote.Reload();

            // If note isn't found, it's new; add it.
            else
                AllNotes.Add(new NoteViewModel(Note.Load(noteId)));
        }
    }
}

Vue AllNotes

Maintenant que le viewmodel a été créé, mettez à jour la vue AllNotes pour qu’elle pointe vers les propriétés viewmodel. Dans le fichier Views\AllNotesPage.xaml , appliquez les modifications suivantes :

  • Ajoutez l’espace xmlns:viewModels de noms XML qui cible l’espace Notes.ViewModels de noms .NET.
  • Ajoutez un BindingContext à la page.
  • Supprimez l’événement du bouton de la barre d’outils Clicked et utilisez la propriété Command.
  • Changez le CollectionView pour lier son ItemSource à AllNotes.
  • Modifiez la CollectionView commande à utiliser pour réagir lorsque l’élément sélectionné change.

Mettez à jour la vue AllNotes :

  1. Dans le volet Explorateur de solutions de Visual Studio, double-cliquez sur Views\AllNotesPage.xaml.

  2. Collez le code suivant :

    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:viewModels="clr-namespace:Notes.ViewModels"
                 x:Class="Notes.Views.AllNotesPage"
                 Title="Your Notes"
                 x:DataType="viewModels:NotesViewModel">
        <ContentPage.BindingContext>
            <viewModels:NotesViewModel />
        </ContentPage.BindingContext>
    
        <!-- Add an item to the toolbar -->
        <ContentPage.ToolbarItems>
            <ToolbarItem Text="Add" Command="{Binding NewCommand}" IconImageSource="{FontImageSource Glyph='+', Color=Black, Size=22}" />
        </ContentPage.ToolbarItems>
    
        <!-- Display notes in a list -->
        <CollectionView x:Name="notesCollection"
                        ItemsSource="{Binding AllNotes}"
                        Margin="20"
                        SelectionMode="Single"
                        SelectionChangedCommand="{Binding SelectNoteCommand}"
                        SelectionChangedCommandParameter="{Binding x:DataType='CollectionView', Source={RelativeSource Self}, Path=SelectedItem}">
            <!-- Designate how the collection of items are laid out -->
            <CollectionView.ItemsLayout>
                <LinearItemsLayout Orientation="Vertical" ItemSpacing="10" />
            </CollectionView.ItemsLayout>
    
            <!-- Define the appearance of each item in the list -->
            <CollectionView.ItemTemplate>
                <DataTemplate x:DataType="viewModels:NoteViewModel">
                    <StackLayout>
                        <Label Text="{Binding Text}" FontSize="22"/>
                        <Label Text="{Binding Date}" FontSize="14" TextColor="Silver"/>
                    </StackLayout>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </ContentPage>
    

La barre d’outils n’utilise plus l’événement Clicked et utilise plutôt une commande.

Le CollectionView prend en charge la commande avec les propriétés SelectionChangedCommand et SelectionChangedCommandParameter. Dans le code XAML mis à jour, la SelectionChangedCommand propriété est liée aux viewmodel SelectNoteCommand, ce qui signifie que la commande est appelée lorsque l’élément sélectionné change. Lorsque la commande est appelée, la valeur de propriété SelectionChangedCommandParameter est passée à la commande.

Regardez la liaison utilisée pour le CollectionView.

<CollectionView x:Name="notesCollection"
                ItemsSource="{Binding AllNotes}"
                Margin="20"
                SelectionMode="Single"
                SelectionChangedCommand="{Binding SelectNoteCommand}"
                SelectionChangedCommandParameter="{Binding x:DataType='CollectionView', Source={RelativeSource Self}, Path=SelectedItem}">

La propriété SelectionChangedCommandParameter utilise la Source={RelativeSource Self} liaison. L'élément Self fait référence à l'objet actuel, qui est le CollectionView. Par conséquent, x:DataType spécifie CollectionView le type de la liaison compilée. Notez que le chemin de liaison est la SelectedItem propriété. Lorsque la commande est appelée en modifiant l’élément sélectionné, la SelectNoteCommand commande est appelée et l’élément sélectionné est passé à la commande en tant que paramètre.

Pour que l’expression de liaison définie dans la SelectionChangedCommandParameter propriété soit compilée, il est nécessaire de demander au projet d’activer les liaisons compilées dans les expressions qui spécifient la Source propriété. Pour ce faire, modifiez le fichier projet de votre solution et ajoutez-le <MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation> dans l’élément <PropertyGroup> :

<PropertyGroup>
  <MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation>
</PropertyGroup>

Nettoyer le code sous-jacent d'AllNotes

Maintenant que l’interaction avec la vue a changé de gestionnaires d’événements en commandes, ouvrez le fichier Views\AllNotesPage.xaml.cs et remplacez tout le code par une classe qui contient uniquement le constructeur :

  1. Dans le volet Explorateur de solutions de Visual Studio, double-cliquez sur Views\AllNotesPage.xaml.cs.

    Conseil / Astuce

    Vous devrez peut-être développer Views\AllNotesPage.xaml pour afficher le fichier.

  2. Remplacez le code par l’extrait de code suivant :

    namespace Notes.Views;
    
    public partial class AllNotesPage : ContentPage
    {
        public AllNotesPage()
        {
            InitializeComponent();
        }
    }
    

Exécuter l’application

Vous pouvez maintenant exécuter l’application et tout fonctionne. Toutefois, il existe deux problèmes avec le comportement de l’application :

  • Si vous sélectionnez une note, qui ouvre l’éditeur, appuyez sur Enregistrer, puis essayez de sélectionner la même note, elle ne fonctionne pas.
  • Chaque fois qu’une note est modifiée ou ajoutée, la liste des notes n’est pas réorganisées pour afficher les dernières notes en haut.

Ces deux problèmes sont résolus à l’étape suivante du didacticiel.

Corriger le comportement de l’application

Maintenant que le code de l’application peut être compilé et exécuté, vous avez probablement remarqué qu’il existe deux défauts avec le comportement de l’application. L’application ne vous permet pas de réélectionner une note déjà sélectionnée, et la liste des notes n’est pas réorganisées après la création ou la modification d’une note.

Mettez les remarques en haut de la liste

Tout d’abord, corrigez le problème de réorganisation avec la liste des notes. Dans le fichier ViewModels\NotesViewModel.cs , la AllNotes collection contient toutes les notes à présenter à l’utilisateur. Malheureusement, l’inconvénient de l’utilisation d’un ObservableCollection est qu’il doit être trié manuellement. Pour obtenir les éléments nouveaux ou mis à jour en haut de la liste, procédez comme suit :

  1. Dans le volet Explorateur de solutions de Visual Studio, double-cliquez sur ViewModels\NotesViewModel.cs.

  2. Dans la ApplyQueryAttributes méthode, examinez la logique de la clé de chaîne de requête sauvegardée.

  3. Quand matchedNote n’est pas null, la note est mise à jour. Utilisez la méthode AllNotes.Move pour déplacer matchedNote vers l'index 0, qui est le haut de la liste.

    string noteId = query["saved"].ToString();
    NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault();
    
    // If note is found, update it
    if (matchedNote != null)
    {
        matchedNote.Reload();
        AllNotes.Move(AllNotes.IndexOf(matchedNote), 0);
    }
    

    La AllNotes.Move méthode prend deux paramètres pour déplacer la position d’un objet dans la collection. Le premier paramètre est l’index de l’objet à déplacer, et le deuxième paramètre est l’index de l’emplacement où déplacer l’objet. La AllNotes.IndexOf méthode récupère l’index de la note.

  4. Lorsque la matchedNote valeur est null, la note est nouvelle et est ajoutée à la liste. Au lieu de l’ajouter, qui ajoute la note à la fin de la liste, insérez la note à l’index 0, qui est la partie supérieure de la liste. Remplacez la méthode AllNotes.Addpar AllNotes.Insert .

    string noteId = query["saved"].ToString();
    NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault();
    
    // If note is found, update it
    if (matchedNote != null)
    {
        matchedNote.Reload();
        AllNotes.Move(AllNotes.IndexOf(matchedNote), 0);
    }
    // If note isn't found, it's new; add it.
    else
        AllNotes.Insert(0, new NoteViewModel(Models.Note.Load(noteId)));
    

La ApplyQueryAttributes méthode doit ressembler à l’extrait de code suivant :

void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query)
{
    if (query.ContainsKey("deleted"))
    {
        string noteId = query["deleted"].ToString();
        NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault();

        // If note exists, delete it
        if (matchedNote != null)
            AllNotes.Remove(matchedNote);
    }
    else if (query.ContainsKey("saved"))
    {
        string noteId = query["saved"].ToString();
        NoteViewModel matchedNote = AllNotes.Where((n) => n.Identifier == noteId).FirstOrDefault();

        // If note is found, update it
        if (matchedNote != null)
        {
            matchedNote.Reload();
            AllNotes.Move(AllNotes.IndexOf(matchedNote), 0);
        }
        // If note isn't found, it's new; add it.
        else
            AllNotes.Insert(0, new NoteViewModel(Models.Note.Load(noteId)));
    }
}

Autoriser la sélection d’une note deux fois

Dans la vue AllNotes, la CollectionView liste toutes les notes, mais ne vous permet pas de sélectionner la même note deux fois. Il existe deux façons dont l’élément reste sélectionné : lorsque l’utilisateur modifie une note existante et lorsque l’utilisateur navigue de force vers l’arrière. Le cas où l’utilisateur enregistre une note est résolu avec le changement de code dans la section précédente qui utilise AllNotes.Move, de sorte que vous n’avez pas à vous soucier de ce cas.

Le problème que vous devez résoudre est lié à la navigation. Quelle que soit la façon dont la vue Allnotes est accédée, l’événement NavigatedTo est déclenché pour la page. Cet événement est un endroit idéal pour désélectionner de force l’élément sélectionné dans le CollectionView.

Toutefois, avec le modèle MVVM appliqué ici, le viewmodel ne peut pas déclencher quelque chose directement sur l’affichage, comme l’effacement de l’élément sélectionné après l’enregistrement de la note. Alors comment faire pour que cela se produise ? Une bonne implémentation du modèle MVVM minimise le "code-behind" dans la vue. Il existe plusieurs façons de résoudre ce problème pour prendre en charge le modèle de séparation MVVM. Toutefois, il est tout à fait acceptable de mettre du code dans le code-behind de la vue, surtout lorsqu'il est directement lié à la vue. MVVM a de nombreuses conceptions et concepts exceptionnels qui vous aident à compartimenter votre application, à améliorer la facilité de maintenance et à faciliter l’ajout de nouvelles fonctionnalités. Toutefois, dans certains cas, MVVM peut encourager la sur-ingénierie.

Ne surutilisons pas une solution pour ce problème et utilisez simplement l’événement NavigatedTo pour effacer l’élément sélectionné à partir du CollectionView.

  1. Dans le volet Explorateur de solutions de Visual Studio, double-cliquez sur Views\AllNotesPage.xaml.

  2. Dans le code XAML pour le <ContentPage>, ajoutez l’événement NavigatedTo :

    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:viewModels="clr-namespace:Notes.ViewModels"
                 x:Class="Notes.Views.AllNotesPage"
                 Title="Your Notes"
                 NavigatedTo="ContentPage_NavigatedTo"
                 x:DataType="viewModels:NotesViewModel">
        <ContentPage.BindingContext>
            <viewModels:NotesViewModel />
    
  3. Vous pouvez ajouter un gestionnaire d’événements par défaut en cliquant avec le bouton droit sur le nom de la méthode d’événement, ContentPage_NavigatedTopuis en sélectionnant Atteindre la définition. Cette action ouvre les vues\AllNotesPage.xaml.cs dans l’éditeur de code.

  4. Remplacez le code du gestionnaire d’événements par l’extrait de code suivant :

    private void ContentPage_NavigatedTo(object sender, NavigatedToEventArgs e)
    {
        notesCollection.SelectedItem = null;
    }
    

    Dans le XAML, le CollectionView a reçu le nom de notesCollection. Ce code utilise ce nom pour accéder au CollectionView, et définit SelectedItem sur null. L’élément sélectionné est effacé chaque fois que la page est redirigée.

À présent, exécutez votre application. Essayez d’accéder à une note, appuyez sur le bouton Précédent, puis sélectionnez la même note une deuxième fois. Le comportement de l’application est résolu !

Explorez le code. Explorez le code de ce tutoriel.. Si vous voulez télécharger une copie du projet terminé pour y comparer votre code, téléchargez ce projet.

Votre application utilise désormais des modèles MVVM !

Étapes suivantes

Les liens suivants fournissent plus d’informations sur certains des concepts que vous avez appris dans ce tutoriel :