Partager via


Ajouter une injection de dépendances

L’injection de dépendances (DI) vous permet de gérer le cycle de vie de vos ViewModels et services. Il rend votre code plus testable et plus facile à gérer. Dans cette étape, vous configurez l’authentification unique dans votre application et mettez à jour vos modèles pour utiliser un service de fichiers pour les opérations de fichier.

Pour plus d’informations sur l’infrastructure d’injection de dépendances .NET, consultez le document sur l’injection de dépendances .NET et le didacticiel Utiliser l’injection de dépendances dans .NET.

Installer des packages Microsoft.Extensions

Ajoutez la prise en charge de la DI à vos projets.

  1. Installez Microsoft.Extensions.DependencyInjection dans les projets WinUINotes et WinUINotes.Bus :

    dotnet add WinUINotes package Microsoft.Extensions.DependencyInjection
    dotnet add WinUINotes.Bus package Microsoft.Extensions.DependencyInjection
    

Créer une interface et une implémentation de service de fichiers

  1. Dans le projet WinUINotes.Bus , créez un dossier nommé Services.

  2. Ajoutez un fichier IFileService.csd’interface :

    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Windows.Storage;
    
    namespace WinUINotes.Services
    {
        public interface IFileService
        {
            Task<IReadOnlyList<IStorageItem>> GetStorageItemsAsync();
            Task<IReadOnlyList<IStorageItem>> GetStorageItemsAsync(IStorageFolder storageFolder);
            Task<string> GetTextFromFileAsync(IStorageFile file);
            Task CreateOrUpdateFileAsync(string filename, string contents);
            Task DeleteFileAsync(string filename);
            bool FileExists(string filename);
            IStorageFolder GetLocalFolder();
        }
    }
    

    L’interface de service de fichiers définit des méthodes pour les opérations de fichier. Il extrait les détails de la gestion des fichiers à partir des ViewModels et des modèles. Les paramètres et les valeurs de retour sont tous des types ou interfaces .NET de base. Cette conception garantit que le service peut être facilement simulé ou remplacé dans les tests unitaires, en favorisant le couplage libre et la testabilité.

  3. Ajoutez le fichier WindowsFileService.csd’implémentation :

    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Windows.Storage;
    
    namespace WinUINotes.Services
    {
        public class WindowsFileService : IFileService
        {
             public StorageFolder storageFolder;
    
             public WindowsFileService(IStorageFolder storageFolder)
             {
                 this.storageFolder = (StorageFolder)storageFolder;
    
                 if (this.storageFolder is null)
                 {
                     throw new ArgumentException("storageFolder must be of type StorageFolder", nameof(storageFolder));
                 }
             }
    
             public async Task CreateOrUpdateFileAsync(string filename, string contents)
             {
                 // Save the note to a file.
                 StorageFile storageFile = (StorageFile)await storageFolder.TryGetItemAsync(filename);
                 if (storageFile is null)
                 {
                     storageFile = await storageFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
                 }
                 await FileIO.WriteTextAsync(storageFile, contents);
             }
    
         public async Task DeleteFileAsync(string filename)
         {
             // Delete the note from the file system.
             StorageFile storageFile = (StorageFile)await storageFolder.TryGetItemAsync(filename);
             if (storageFile is not null)
             {
                 await storageFile.DeleteAsync();
             }
         }
    
         public bool FileExists(string filename)
         {
             StorageFile storageFile = (StorageFile)storageFolder.TryGetItemAsync(filename).AsTask().Result;
             return storageFile is not null;
         }
    
         public IStorageFolder GetLocalFolder()
         {
             return storageFolder;
         }
    
         public async Task<IReadOnlyList<IStorageItem>> GetStorageItemsAsync()
         {
             return await storageFolder.GetItemsAsync();
         }
    
         public async Task<IReadOnlyList<IStorageItem>> GetStorageItemsAsync(IStorageFolder folder)
         {
             return await folder.GetItemsAsync();
         }
    
         public async Task<string> GetTextFromFileAsync(IStorageFile file)
         {
             return await FileIO.ReadTextAsync(file);
         }
        }
    }
    

L’implémentation WindowsFileService fournit des opérations concrètes de fichier à l’aide des API de stockage Windows Runtime (WinRT) et .NET :

  • Injection de constructeur : le service accepte un IStorageFolder dans son constructeur. Cette approche vous permet de configurer l’emplacement de stockage lorsque vous instanciez le service. Cette approche rend le service flexible et testable.
  • CreateOrUpdateFileAsync(): cette méthode permet TryGetItemAsync() de vérifier si un fichier existe déjà. Si c’est le cas, la méthode met à jour le fichier existant. Sinon, il crée un fichier à l’aide de CreateFileAsync(). Cette approche gère à la fois les scénarios de création et de mise à jour dans une seule méthode.
  • DeleteFileAsync(): avant de supprimer un fichier, cette méthode vérifie que le fichier existe à l’aide TryGetItemAsync()de . Cette vérification empêche les exceptions d’être levées lors de la tentative de suppression de fichiers inexistants.
  • FileExists(): cette méthode synchrone vérifie l’existence du fichier en appelant l’async TryGetItemAsync() et le blocage avec .Result. Bien que cette approche ne soit généralement pas recommandée, elle est utilisée ici pour prendre en charge la CanDelete() méthode de validation dans ViewModel, qui doit être synchrone.
  • Méthodes d’élément de stockage : les méthodes GetStorageItemsAsync() et GetTextFromFileAsync() fournissent l’accès aux fichiers et à leur contenu à l’aide des API de stockage WinRT. Ces méthodes permettent aux modèles de charger et d’énumérer des notes.

En implémentant l’interface IFileService , vous pouvez facilement remplacer cette classe par une implémentation fictive pour les tests ou un autre fournisseur de stockage si nécessaire.

En savoir plus dans la documentation :

Configurer l’injection de dépendances dans App.xaml.cs

Avant de mettre à jour les modèles et ViewModels pour utiliser le service de fichiers, configurez l’injection de dépendances afin que le service puisse être résolu et injecté dans les constructeurs.

Mettez à jour le App.xaml.cs fichier pour configurer le conteneur d’adresses di :

using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using WinUINotes.ViewModels;

namespace WinUINotes;

public partial class App : Application
{
    private readonly IServiceProvider _serviceProvider;

    public App()
    {
        Services = ConfigureServices();
        this.InitializeComponent();
    }

    private static IServiceProvider ConfigureServices()
    {
        var services = new ServiceCollection();

        // Services
        services.AddSingleton<Services.IFileService>(x =>
            ActivatorUtilities.CreateInstance<Services.WindowsFileService>(x,
                            Windows.Storage.ApplicationData.Current.LocalFolder)
        );

        // ViewModels
        services.AddTransient<AllNotesViewModel>();
        services.AddTransient<NoteViewModel>();

        return services.BuildServiceProvider();
    }

    protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        m_window = new MainWindow();
        m_window.Activate();
    }

    public IServiceProvider Services { get; }

    private Window? m_window;

    public new static App Current => (App)Application.Current;
}

Cette configuration configure le conteneur d’injection de dépendances avec tous les services requis :

  • ConfigureServices() méthode : méthode statique qui crée et configure la collection de services. La séparation de cette méthode facilite la maintenance et la facilité de test de la configuration.
  • Services propriété : propriété d’instance qui contient le IServiceProvider. Le constructeur définit cette propriété en appelant ConfigureServices().
  • App.Current propriété statique : fournit un accès pratique à l’instance actuelle App , ce qui est utile lorsque des modèles ou d’autres classes doivent accéder au fournisseur de services.
  • IFileService enregistrement : utilise ActivatorUtilities.CreateInstance pour créer une WindowsFileService instance avec ApplicationData.Current.LocalFolder comme paramètre. Cette approche permet au paramètre de constructeur d’être injecté au moment de l’inscription. Enregistrez le service en tant que singleton, car les opérations de fichier sont sans état et une seule instance peut être partagée sur l’application.
  • Enregistrement des ViewModels : Enregistrez les deux ViewModels en tant que transitoires, ce qui signifie qu'une nouvelle instance est créée chaque fois qu'une instance est demandée. Cette approche garantit que chaque page obtient sa propre instance ViewModel avec un état propre.

Les modèles et d'autres classes peuvent accéder au fournisseur de services par le biais de App.Current.Services.GetService() pour récupérer les services inscrits si nécessaire.

En savoir plus dans la documentation :

Mettre à jour des modèles pour utiliser le service de fichiers

Maintenant que le service de fichiers est disponible via l’injection de dépendances, mettez à jour les classes de modèle pour l’utiliser. Les modèles reçoivent le service de fichiers et l’utilisent pour toutes les opérations de fichier.

Mettre à jour le modèle note

Mettez à jour la Note classe pour accepter le service de fichiers et l’utiliser pour les opérations d’enregistrement, de suppression et d’existence de fichier :

using System;
using System.Threading.Tasks;
using WinUINotes.Services;

namespace WinUINotes.Models;

public class Note
{
    private IFileService fileService;
    public string Filename { get; set; } = string.Empty;
    public string Text { get; set; } = string.Empty;
    public DateTime Date { get; set; } = DateTime.Now;

    public Note(IFileService fileService)
    {
        Filename = "notes" + DateTime.Now.ToBinary().ToString() + ".txt";
        this.fileService = fileService;
    }

    public async Task SaveAsync()
    {
        await fileService.CreateOrUpdateFileAsync(Filename, Text);
    }

    public async Task DeleteAsync()
    {
        await fileService.DeleteFileAsync(Filename);
    }

    public bool NoteFileExists()
    {
        return fileService.FileExists(Filename);
    }
}

Le modèle Note reçoit désormais le service de fichier par injection de constructeur :

  • Constructeur : accepte un IFileService paramètre, rendant la dépendance explicite et requise. Cette conception favorise la testabilité et garantit que le modèle a toujours accès au service de fichiers dont il a besoin.
  • Génération de nom de fichier : le constructeur génère automatiquement un nom de fichier unique à l’aide de l’horodatage actuel, ce qui garantit que chaque note a un nom de fichier distinct.
  • Opérations de fichier : Les méthodes SaveAsync(), DeleteAsync() et NoteFileExists() délèguent toutes au service de fichiers injecté, en gardant le modèle concentré sur la coordination des opérations plutôt que sur l'implémentation des détails des E/S de fichiers.

Cette approche élimine la nécessité pour le modèle d’utiliser le modèle de localisateur de services (accès App.Services directement), ce qui améliore la testabilité et rend les dépendances claires.

Mettre à jour le modèle AllNotes

Mettez à jour la AllNotes classe pour charger des notes à partir du stockage à l’aide du service de fichiers :

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Windows.Storage;
using WinUINotes.Services;

namespace WinUINotes.Models;

public class AllNotes
{
        private IFileService fileService;
        public ObservableCollection<Note> Notes { get; set; } = [];

        public AllNotes(IFileService fileService)
        {
            this.fileService = fileService;
        }

        public async Task LoadNotes()
        {
            Notes.Clear();
            await GetFilesInFolderAsync(fileService.GetLocalFolder());
        }

        private async Task GetFilesInFolderAsync(IStorageFolder folder)
        {
            // Each StorageItem can be either a folder or a file.
            IReadOnlyList<IStorageItem> storageItems =
                                        await fileService.GetStorageItemsAsync(folder);
            foreach (IStorageItem item in storageItems)
            {
                if (item.IsOfType(StorageItemTypes.Folder))
                {
                    // Recursively get items from subfolders.
                    await GetFilesInFolderAsync((IStorageFolder)item);
                }
                else if (item.IsOfType(StorageItemTypes.File))
                {
                    IStorageFile file = (IStorageFile)item;
                    Note note = new(fileService)
                    {
                        Filename = file.Name,
                        Text = await fileService.GetTextFromFileAsync(file),
                        Date = file.DateCreated.DateTime
                    };
                    Notes.Add(note);
                }
            }
        }
}

Le AllNotes modèle reçoit le service de fichiers par injection de constructeur, comme le Note modèle. Étant donné que cette classe se trouve dans le WinUINotes.Bus projet, elle ne peut pas accéder App.Current.Services à partir du WinUINotes projet (en raison de contraintes de référence de projet).

La LoadNotes() méthode appelle la méthode privée GetFilesInFolderAsync() pour énumérer de manière récursive tous les fichiers dans le dossier de stockage local et ses sous-dossiers. Pour chaque élément de stockage :

  1. S’il s’agit d’un dossier, la méthode s’appelle de manière récursive pour traiter le contenu du dossier
  2. S’il s’agit d’un fichier, il crée une Note instance avec le service de fichiers injecté
  3. La note Filename est définie sur le nom du fichier
  4. La note Text est remplie en lisant le contenu du fichier à l’aide de GetTextFromFileAsync()
  5. La balise Date de la note est réglée à la date de création du fichier
  6. La note est ajoutée à la Notes collection observable

Cette approche garantit que toutes les notes chargées à partir du stockage ont accès au service de fichiers dont ils ont besoin pour les opérations d’enregistrement et de suppression ultérieures.

Mettre à jour ViewModels pour utiliser le service de fichiers

Avec les modèles maintenant utilisant le service de fichiers, vous devez mettre à jour les ViewModels. Toutefois, étant donné que les modèles gèrent directement les opérations de fichier, les ViewModels se concentrent principalement sur l’orchestration des modèles et la gestion des propriétés observables.

Mettre à jour AllNotesViewModel

Mettez à jour le AllNotesViewModel pour qu’il fonctionne avec le modèle mis à jour AllNotes.

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using WinUINotes.Models;
using WinUINotes.Services;

namespace WinUINotes.ViewModels
{
    public partial class AllNotesViewModel : ObservableObject
    {
        private readonly AllNotes allNotes;

        [ObservableProperty]
        private ObservableCollection<Note> notes;

        public AllNotesViewModel(IFileService fileService)
        {
            allNotes = new AllNotes(fileService);
            notes = new ObservableCollection<Note>();
        }

        [RelayCommand]
        public async Task LoadAsync()
        {
            await allNotes.LoadNotes();
            Notes.Clear();
            foreach (var note in allNotes.Notes)
            {
                Notes.Add(note);
            }
        }
    }
}

Qu’est-ce qui a changé depuis l’étape 2 ?

Le changement principal est l’ajout du paramètre IFileService au constructeur. À l’étape 2, le ViewModel a instancié AllNotes avec un constructeur sans paramètre (allNotes = new AllNotes()). Maintenant que le modèle nécessite que le AllNotes service de fichiers effectue ses opérations, le ViewModel reçoit l’injection de constructeur du IFileService et le transmet au modèle.

Cette modification maintient un flux de dépendance approprié : le service de fichiers est injecté au niveau supérieur (ViewModel) et descend vers le modèle. ViewModel continue de se concentrer sur la coordination du processus de chargement et la synchronisation de la collection observable Notes avec les données du modèle, sans avoir à connaître les détails de l’implémentation du chargement des fichiers.

Mettre à jour NoteViewModel

Mettez à jour le NoteViewModel pour injecter le service de fichiers et utiliser le système de messagerie du Kit d'Outils MVVM :

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using System;
using System.Threading.Tasks;
using WinUINotes.Models;
using WinUINotes.Services;

namespace WinUINotes.ViewModels
{
    public partial class NoteViewModel : ObservableObject
    {
        private Note note;
        private IFileService fileService;

        [ObservableProperty]
        [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
        [NotifyCanExecuteChangedFor(nameof(DeleteCommand))]
        private string filename = string.Empty;

        [ObservableProperty]
        [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
        private string text = string.Empty;

        [ObservableProperty]
        private DateTime date = DateTime.Now;

        public NoteViewModel(IFileService fileService)
        {
            this.fileService = fileService;
            this.note = new Note(fileService);
            this.Filename = note.Filename;
        }

        public void InitializeForExistingNote(Note note)
        {
            this.note = note;
            this.Filename = note.Filename;
            this.Text = note.Text;
            this.Date = note.Date;
        }

        [RelayCommand(CanExecute = nameof(CanSave))]
        private async Task Save()
        {
            note.Filename = this.Filename;
            note.Text = this.Text;
            note.Date = this.Date;
            await note.SaveAsync();

            // Check if the DeleteCommand can now execute
            // (it can if the file now exists)
            DeleteCommand.NotifyCanExecuteChanged();
        }

        private bool CanSave()
        {
            return note is not null
                && !string.IsNullOrWhiteSpace(this.Text)
                && !string.IsNullOrWhiteSpace(this.Filename);
        }

        [RelayCommand(CanExecute = nameof(CanDelete))]
        private async Task Delete()
        {
            await note.DeleteAsync();
            note = new Note(fileService);
            // Send a message from some other module
            WeakReferenceMessenger.Default.Send(new NoteDeletedMessage(note));
        }

        private bool CanDelete()
        {
            // Note: This is to illustrate how commands can be
            // enabled or disabled.
            // In a real application, you shouldn't perform
            // file operations in your CanExecute logic.
            return note is not null
                && !string.IsNullOrWhiteSpace(this.Filename)
                && this.note.NoteFileExists();
        }
    }
}

Qu’est-ce qui a changé depuis l’étape 2 ?

Plusieurs modifications importantes prennent en charge l’injection de dépendances et la communication entre ViewModel :

  1. Injection de service de fichiers : le constructeur accepte IFileService désormais en tant que paramètre et le stocke dans un champ. Ce service est passé au modèle Note lors de la création de nouvelles instances, ce qui garantit que toutes les notes peuvent effectuer des opérations de fichier.

  2. WeakReferenceMessenger : La Delete() méthode utilise désormais le Toolkit MVVM pour diffuser un NoteDeletedMessage après la suppression d’une note. Cette approche permet un couplage libre entre ViewModels : d’autres parties de l’application (comme NotePage) peuvent écouter ce message et répondre de manière appropriée (par exemple, en revenant à la liste des notes, qui a été actualisée) sans NoteViewModel avoir besoin d’une référence directe à ces derniers.

WeakReferenceMessenger est une fonctionnalité clé de l'outil MVVM qui empêche les fuites de mémoire grâce à l’utilisation de références faibles. Les composants peuvent s’abonner aux messages sans créer de références fortes qui empêcheraient le ramasse-miettes.

En savoir plus dans la documentation :

Créer la classe NoteDeletedMessage

La WeakReferenceMessenger a besoin d'une classe de message pour envoyer entre les composants. Créez une classe pour représenter l’événement de suppression de note :

  1. Dans le projet WinUINotes.Bus , ajoutez un nouveau fichier de NoteDeletedMessage.csclasse :

    using CommunityToolkit.Mvvm.Messaging.Messages;
    using WinUINotes.Models;
    
    namespace WinUINotes
    {
        public class NoteDeletedMessage : ValueChangedMessage<Note>
        {
            public NoteDeletedMessage(Note note) : base(note)
            {
            }
        }
    }
    

Cette classe de message hérite de ValueChangedMessage<Note>, qui est un type de message spécialisé fourni par le kit de ressources MVVM pour transporter des notifications de modification de valeur. Le constructeur accepte a Note et le transmet à la classe de base, le rendant disponible pour les destinataires du message via la Value propriété. Lorsque NoteViewModel envoie ce message, tout composant qui s’abonne à NoteDeletedMessage le reçoit et peut accéder à la note supprimée par le biais de la propriété Value.

Fonctionnement de la messagerie dans le kit de ressources MVVM :

  1. Expéditeur : la NoteViewModel.Delete() méthode envoie le message à l’aide de WeakReferenceMessenger.Default.Send(new NoteDeletedMessage(note)).
  2. Récepteur : les pages (comme NotePage) peuvent s’inscrire pour recevoir des messages en implémentant IRecipient<NoteDeletedMessage> et en inscrivant auprès du messager. Lorsque le message est reçu, la page peut revenir à la liste de toutes les notes.
  3. Couplage libre : l’expéditeur n’a pas besoin de savoir qui (le cas échéant) écoute. Le destinataire n’a pas besoin d’une référence directe à l’expéditeur. Cette configuration conserve vos composants indépendants et testables.

L'approche de référence faible permet que si un composant est collecté par le ramasse-miettes, son abonnement aux messages soit automatiquement nettoyé sans provoquer de fuites de mémoire.

Mettre à jour des pages pour utiliser l’injection de dépendances

Mettez à jour vos constructeurs de page pour recevoir les ViewModels par le biais d'une DI.

Mettre à jour AllNotesPage.xaml.cs

using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using WinUINotes.ViewModels;

namespace WinUINotes.Views
{
    public sealed partial class AllNotesPage : Page
    {
        private AllNotesViewModel? viewModel;

        public AllNotesPage()
        {
            this.InitializeComponent();
            viewModel = App.Current.Services.GetService<AllNotesViewModel>();
        }

        private void NewNoteButton_Click(object sender, RoutedEventArgs e)
        {
            Frame.Navigate(typeof(NotePage));
        }

        private void ItemsView_ItemInvoked(ItemsView sender, ItemsViewItemInvokedEventArgs args)
        {
            Frame.Navigate(typeof(NotePage), args.InvokedItem);
        }

        protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            if (viewModel is not null)
            {
                await viewModel.LoadAsync();
            }
        }
    }
}

Qu’est-ce qui a changé depuis l’étape 2 ?

L’application obtient désormais le AllNotesViewModel à partir du conteneur d’injection de dépendances App.Current.Services.GetService<AllNotesViewModel>() au lieu de le créer directement avec new AllNotesViewModel(). Cette approche présente plusieurs avantages :

  1. Résolution automatique des dépendances : le conteneur DI fournit automatiquement la dépendance que AllNotesViewModel requiert dans son constructeur.
  2. Gestion du cycle de vie : le conteneur DI gère le cycle de vie du ViewModel en fonction de la façon dont il a été enregistré (en tant que transitoire dans ce cas, fournissant une nouvelle instance).
  3. Testabilité : ce modèle facilite l’échange d’implémentations ou de dépendances fictifs dans les tests.
  4. Facilité de maintenance : si les dépendances de ViewModel changent à l’avenir, vous devez uniquement mettre à jour la configuration de l’authentification unique, et non à chaque emplacement où ViewModel est créé.

Le reste du code reste le même. La OnNavigatedTo() méthode appelle LoadAsync() toujours pour actualiser la liste des notes lorsque l’utilisateur accède à cette page.

Mettre à jour NotePage.xaml.cs

using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using WinUINotes.Models;
using WinUINotes.ViewModels;

namespace WinUINotes.Views
{
    public sealed partial class NotePage : Page
    {
        private NoteViewModel? noteVm;

        public NotePage()
        {
            this.InitializeComponent();
        }

        public void RegisterForDeleteMessages()
        {
            WeakReferenceMessenger.Default.Register<NoteDeletedMessage>(this, (r, m) =>
            {
                if (Frame.CanGoBack)
                {
                    Frame.GoBack();
                }
            });
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            noteVm = App.Current.Services.GetService<NoteViewModel>();
            RegisterForDeleteMessages();

            if (e.Parameter is Note note && noteVm is not null)
            {
                noteVm.InitializeForExistingNote(note);
            }
        }
    }
}

Qu’est-ce qui a changé depuis l’étape 2 ?

Plusieurs modifications importantes intègrent les fonctionnalités d’injection de dépendances et de messagerie :

  1. ViewModel à partir d’un conteneur DI : le NoteViewModel est désormais obtenu depuis le conteneur d’injection de dépendances en utilisant App.Current.Services.GetService<NoteViewModel>() dans la méthode OnNavigatedTo() au lieu d’être instancié directement. Cette approche garantit que ViewModel reçoit automatiquement sa dépendance requise IFileService .
  2. Inscription de message : la nouvelle méthode s’abonne à NoteDeletedMessage avec l’aide du WeakReferenceMessenger. Lorsqu'une note est supprimée (de la NoteViewModel.Delete() méthode), ce message est reçu par cette page et elle revient à la liste de toutes les notes à l'aide de Frame.GoBack().
  3. Modèle de messagerie : ce modèle illustre le couplage libre activé par le système de messagerie du kit de ressources MVVM. Le NoteViewModel n’a pas besoin de savoir sur la navigation ni sur la structure de la page - il envoie simplement un message lorsqu’une note est supprimée, et la page traite la réponse de navigation de manière autonome.
  4. Minutage du cycle de vie : le ViewModel est instancié et l'inscription des messages se produit dans OnNavigatedTo(), ce qui garantit que tout est correctement initialisé lorsque la page devient active.

Ce modèle sépare efficacement les préoccupations : ViewModel se concentre sur la logique métier et les opérations de données, tandis que la page gère des problèmes spécifiques à l’interface utilisateur comme la navigation.