Delen via


Afhankelijkheidsinjectie toevoegen

Met afhankelijkheidsinjectie (DI) kunt u de levenscyclus van uw ViewModels en services beheren. Het maakt uw code testbaar en eenvoudiger te onderhouden. In deze stap configureert u DI in uw app en werkt u uw modellen bij voor het gebruik van een bestandsservice voor bestandsbewerkingen.

Zie .NET-afhankelijkheidsinjectie en de zelfstudie Afhankelijkheidsinjectie gebruiken in .NET voor meer achtergrondinformatie over het .NET-afhankelijkheidsinjectieframework .

Microsoft.Extensions-pakketten installeren

Voeg DI-ondersteuning toe aan uw projecten.

  1. Installeer Microsoft.Extensions.DependencyInjection zowel in de WinUINotes als WinUINotes.Bus-projecten:

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

Een bestandsservice-interface en -implementatie maken

  1. Maak in het WinUINotes.Bus-project een nieuwe map met de naam Services.

  2. Voeg een interfacebestand IFileService.cstoe:

    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();
        }
    }
    

    De bestandsserviceinterface definieert methoden voor bestandsbewerkingen. Het abstraheert de details van bestandsafhandeling van de ViewModels en Modellen. De parameters en retourwaarden zijn allemaal eenvoudige .NET-typen of interfaces. Dit ontwerp zorgt ervoor dat de service eenvoudig kan worden gesimuleerd of vervangen in eenheidstests, waardoor losse koppeling en testbaarheid wordt bevorderd.

  3. Voeg het implementatiebestand WindowsFileService.cstoe:

    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);
         }
        }
    }
    

De WindowsFileService implementatie biedt concrete bestandsbewerkingen met behulp van de Windows Runtime (WinRT) en .NET-opslag-API's:

  • Constructorinjectie: De service accepteert een IStorageFolder in de constructor. Met deze methode kunt u de opslaglocatie configureren wanneer u de service instantiëren. Deze aanpak maakt de service flexibel en testbaar.
  • CreateOrUpdateFileAsync(): Deze methode gebruikt TryGetItemAsync() om te controleren of er al een bestand bestaat. Als dit het geval is, werkt de methode het bestaande bestand bij. Anders wordt er een nieuw bestand gemaakt met behulp van CreateFileAsync(). Deze aanpak verwerkt zowel de scenario's voor het creëren als het bijwerken in één methode.
  • DeleteFileAsync(): Voordat u een bestand verwijdert, controleert deze methode of het bestand bestaat met behulp van TryGetItemAsync(). Met deze controle voorkomt u dat er uitzonderingen worden gegenereerd bij het verwijderen van niet-bestaande bestanden.
  • FileExists(): Deze synchrone methode controleert op het bestaan van bestanden door de asynchrone TryGetItemAsync() aan te roepen en te blokkeren met .Result. Hoewel deze benadering over het algemeen niet wordt aanbevolen, wordt deze hier gebruikt ter ondersteuning van de CanDelete() validatiemethode in het ViewModel, die synchroon moet zijn.
  • Methoden voor opslagitems: de GetStorageItemsAsync() en GetTextFromFileAsync() methoden bieden toegang tot bestanden en hun inhoud met behulp van WinRT-opslag-API's. Met deze methoden kunnen de modellen notities laden en inventariseren.

Door de IFileService interface te implementeren, kunt u deze klasse eenvoudig vervangen door een mock-implementatie voor testen of een andere opslagprovider, indien nodig.

Meer informatie in de documenten:

Afhankelijkheidsinjectie configureren in App.xaml.cs

Voordat u de modellen en ViewModels bijwerkt om de bestandsservice te gebruiken, configureert u dependency injection zodat de service geresolved kan worden en in de constructors geïnjecteerd kan worden.

Werk het App.xaml.cs bestand bij om de DI-container in te stellen:

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;
}

Met deze configuratie stelt u de container voor afhankelijkheidsinjectie in met alle vereiste services:

  • ConfigureServices() methode: Een statische methode waarmee de serviceverzameling wordt gemaakt en geconfigureerd. Door deze methode te scheiden, is de configuratie beter onderhoudbaar en eenvoudiger te testen.
  • Services eigenschap: Een exemplaareigenschap die de IServiceProvidereigenschap bevat. De constructor stelt deze eigenschap in door aan te roepen ConfigureServices().
  • App.Current statische eigenschap: biedt handige toegang tot het huidige App exemplaar, wat handig is wanneer modellen of andere klassen toegang nodig hebben tot de serviceprovider.
  • IFileService registratie: Hiermee wordt met ActivatorUtilities.CreateInstance een WindowsFileService instantie gemaakt, met ApplicationData.Current.LocalFolder als parameter. Met deze methode kan de constructorparameter tijdens de registratie worden geïnjecteerd. Registreer de service als een singleton omdat bestandsbewerkingen staatloos zijn en één exemplaar kan worden gedeeld in de toepassing.
  • ViewModels-registratie: Registreer beide ViewModels als tijdelijk, wat betekent dat er telkens een nieuw exemplaar wordt gemaakt wanneer er een wordt aangevraagd. Deze aanpak zorgt ervoor dat elke pagina een eigen ViewModel-exemplaar krijgt met een schone status.

Modellen en andere klassen hebben via App.Current.Services.GetService() toegang tot de serviceprovider om geregistreerde services op te halen wanneer dat nodig is.

Meer informatie in de documenten:

Modellen bijwerken om de bestandsservice te gebruiken

Nu de bestandsservice beschikbaar is via afhankelijkheidsinjectie, werkt u de modelklassen bij om deze te gebruiken. De modellen ontvangen de bestandsservice en gebruiken deze voor alle bestandsbewerkingen.

Het notitiemodel bijwerken

Werk de Note klasse bij om de bestandendienst te accepteren en te gebruiken voor het opslaan, verwijderen en controleren van het bestaan van bestanden.

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);
    }
}

Het Note model ontvangt nu de bestandsservice via constructorinjectie:

  • Constructor: Accepteert een IFileService parameter, waardoor de afhankelijkheid expliciet en vereist is. Dit ontwerp bevordert testbaarheid en zorgt ervoor dat het model altijd toegang heeft tot de bestandsservice die het nodig heeft.
  • Generatie van bestandsnaam: De constructor genereert automatisch een unieke bestandsnaam met behulp van de huidige tijdstempel, zodat elke notitie een afzonderlijke bestandsnaam heeft.
  • Bestandsbewerkingen: de SaveAsync(), DeleteAsync()en NoteFileExists() methoden die alle delegeren aan de geïnjecteerde bestandsservice, waarbij het model is gericht op het coördineren van bewerkingen in plaats van de I/O-gegevens van bestanden te implementeren.

Deze aanpak elimineert de noodzaak voor het model om het patroon van de servicezoeker te gebruiken (rechtstreeks toegankelijk App.Services ), waardoor de testbaarheid wordt verbeterd en afhankelijkheden duidelijk worden.

Het AllNotes-model bijwerken

Werk de AllNotes klasse bij om notities uit de opslag te laden met behulp van de bestandsservice:

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);
                }
            }
        }
}

Het AllNotes model ontvangt de bestandsservice via constructorinjectie, net als het Note model. Omdat deze klasse zich in het WinUINotes.Bus project bevindt, kan deze geen toegang krijgen App.Current.Services vanuit het WinUINotes project (vanwege beperkingen voor projectreferenties).

Met LoadNotes() de methode wordt de privémethode GetFilesInFolderAsync() aangeroepen om alle bestanden in de lokale opslagmap en de bijbehorende submappen recursief op te sommen. Voor elk opslagitem:

  1. Als het een map is, roept de methode zichzelf recursief aan om de inhoud van de map te verwerken
  2. Als het een bestand is, wordt er een Note-exemplaar nieuw gemaakt met de geïnjecteerde bestandsservice.
  3. De notitie Filename is ingesteld op de naam van het bestand
  4. De notitie Text wordt ingevuld door de inhoud van het bestand te lezen met behulp van GetTextFromFileAsync()
  5. De notitie Date is ingesteld op de aanmaakdatum van het bestand
  6. De notitie wordt toegevoegd aan de Notes waarneembare verzameling

Deze aanpak zorgt ervoor dat alle notities die vanuit de opslag zijn geladen, toegang hebben tot de bestandsservice die ze nodig hebben voor toekomstige opslag- en verwijderbewerkingen.

ViewModels bijwerken om de bestandsservice te gebruiken

Nu de modellen de bestandsservice gebruiken, moet u de ViewModels bijwerken. Omdat de bestandsbewerkingen echter rechtstreeks worden verwerkt, richten de ViewModels zich voornamelijk op het organiseren van de modellen en het beheren van waarneembare eigenschappen.

AllNotesViewModel bijwerken

Werk het AllNotesViewModel bij om te werken met het bijgewerkte AllNotes model:

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);
            }
        }
    }
}

Wat is er veranderd sinds stap 2?

De sleutelwijziging is de toevoeging van de IFileService parameter aan de constructor. In stap 2 is het ViewModel geïnstantieerd AllNotes met een parameterloze constructor (allNotes = new AllNotes()). Nu het AllNotes model vereist dat de bestandsservice de bewerkingen uitvoert, ontvangt het ViewModel de IFileService via constructorinjectie en geeft het door aan het model.

Deze wijziging onderhoudt de juiste afhankelijkheidsstroom: de bestandsservice wordt geïnjecteerd op het hoogste niveau (ViewModel) en loopt omlaag naar het model. Het ViewModel blijft zich richten op het coördineren van het laadproces en het houden van de waarneembare Notes verzameling gesynchroniseerd met de gegevens van het model, zonder dat u de implementatiedetails hoeft te kennen van de wijze waarop bestanden worden geladen.

NoteViewModel bijwerken

Werk het NoteViewModel bestand bij om de bestandsservice te injecteren en het berichtensysteem van de MVVM Toolkit te gebruiken:

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();
        }
    }
}

Wat is er veranderd sinds stap 2?

Verschillende belangrijke wijzigingen ondersteunen afhankelijkheidsinjectie en inter-ViewModel-communicatie:

  1. Bestandsservice-injectie: de constructor accepteert IFileService nu als een parameter en slaat deze op in een veld. Deze service wordt doorgegeven aan het model bij het maken van nieuwe Note exemplaren, zodat alle notities bestandsbewerkingen kunnen uitvoeren.

  2. WeakReferenceMessenger: De Delete() methode maakt nu gebruik van de MVVM Toolkit om WeakReferenceMessenger.Default.Send() een NoteDeletedMessage uit te zenden nadat een notitie is verwijderd. Met deze methode kunt u losjes koppelen tussen ViewModels: andere onderdelen van de toepassing (zoals NotePage) kunnen naar dit bericht luisteren en op de juiste manier reageren (bijvoorbeeld door terug te gaan naar de lijst met notities die zijn vernieuwd) zonder NoteViewModel dat u hiervoor een directe verwijzing nodig hebt.

Het WeakReferenceMessenger is een belangrijke functie van de MVVM Toolkit die geheugenlekken voorkomt met behulp van zwakke verwijzingen. Onderdelen kunnen zich abonneren op berichten zonder sterke verwijzingen te maken die het ophalen van ongebruikte objecten zouden voorkomen.

Meer informatie in de documenten:

De klasse NoteDeletedMessage maken

Er is een berichtklasse nodig voor WeakReferenceMessenger om te verzenden tussen componenten. Maak een nieuwe klasse om de gebeurtenis voor het verwijderen van notities weer te geven:

  1. Voeg in het Project WinUINotes.Bus een nieuw klassebestand NoteDeletedMessage.cstoe:

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

Deze berichtklasse erft van ValueChangedMessage<Note>, een gespecialiseerd berichttype dat door de MVVM Toolkit wordt geleverd voor meldingen van wijzigingen in waarden. De constructor accepteert een Note en geeft deze door aan de basisklasse, zodat deze beschikbaar is voor geadresseerden van berichten via de Value eigenschap. Wanneer NoteViewModel dit bericht verzendt, ontvangt elk onderdeel dat zich abonneert op NoteDeletedMessage het en heeft het toegang tot de verwijderde notitie via de Value eigenschap.

Hoe messaging werkt in de MVVM Toolkit:

  1. Afzender: De NoteViewModel.Delete() methode verzendt het bericht met behulp van WeakReferenceMessenger.Default.Send(new NoteDeletedMessage(note)).
  2. Ontvanger: Pagina's (zoals NotePage) kunnen zich registreren om berichten te ontvangen door ze te implementeren IRecipient<NoteDeletedMessage> en te registreren bij de messenger. Wanneer het bericht wordt ontvangen, kan de pagina teruggaan naar de lijst met alle notities.
  3. Losse koppeling: de afzender hoeft niet te weten wie (als iemand) luistert. De ontvanger heeft geen directe verwijzing naar de afzender nodig. Met deze installatie blijven uw onderdelen onafhankelijk en testbaar.

De zwakke referentiebenadering betekent dat als een component garbage-gecollecteerd wordt, het berichtenabonnement automatisch wordt opgeruimd om geheugenlekken te vermijden.

Pagina's bijwerken om afhankelijkheidsinjectie te gebruiken

Werk de paginaconstructors bij om de ViewModels via DI te ontvangen.

AllNotesPage.xaml.cs bijwerken

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();
            }
        }
    }
}

Wat is er veranderd sinds stap 2?

De app krijgt nu de AllNotesViewModel uit de afhankelijkheidsinjectiecontainer door App.Current.Services.GetService<AllNotesViewModel>() te gebruiken in plaats van deze rechtstreeks met new AllNotesViewModel() te maken. Deze aanpak heeft verschillende voordelen:

  1. Automatische afhankelijkheidsresolutie: de DI-container verstrekt automatisch de afhankelijkheid die IFileService in de AllNotesViewModel constructor nodig heeft.
  2. Levenscyclusbeheer: De DI-container beheert de levenscyclus van het ViewModel op basis van hoe deze is geregistreerd (als tijdelijk in dit geval, waarbij een nieuw exemplaar wordt geboden).
  3. Testbaarheid: Dit patroon maakt het eenvoudiger om implementaties of mock-afhankelijkheden in tests te wisselen.
  4. Onderhoudbaarheid: als de afhankelijkheden van ViewModel in de toekomst veranderen, hoeft u alleen de DI-configuratie bij te werken, niet elke locatie waar het ViewModel wordt gemaakt.

De rest van de code blijft hetzelfde. De OnNavigatedTo() methode roept LoadAsync() nog steeds aan om de lijst met notities te vernieuwen wanneer de gebruiker naar deze pagina navigeert.

NotePage.xaml.cs bijwerken

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);
            }
        }
    }
}

Wat is er veranderd sinds stap 2?

Verschillende belangrijke wijzigingen integreren functies voor afhankelijkheidsinjectie en berichten:

  1. ViewModel uit de DI-container: De NoteViewModel wordt nu opgehaald uit de afhankelijkheidsinjectiecontainer met behulp van App.Current.Services.GetService<NoteViewModel>() in de OnNavigatedTo()-methode in plaats van direct te worden geïnstantieerd. Deze aanpak zorgt ervoor dat het ViewModel automatisch de vereiste IFileService afhankelijkheid ontvangt.
  2. Berichtregistratie: De nieuwe RegisterForDeleteMessages() methode abonneert zich op NoteDeletedMessage met behulp van de WeakReferenceMessenger. Wanneer een notitie wordt verwijderd (uit de NoteViewModel.Delete() methode), ontvangt deze pagina het bericht en gaat u terug naar de lijst met alle notities met behulp van Frame.GoBack().
  3. Berichtpatroon: Dit patroon demonstreert de losse koppeling die is ingeschakeld door het berichtensysteem van de MVVM Toolkit. Het NoteViewModel hoeft niet te weten over navigatie of de paginastructuur. Het verzendt gewoon een bericht wanneer een notitie wordt verwijderd en de pagina verwerkt het navigatieantwoord onafhankelijk.
  4. Levenscyclustijd: Het ViewModel wordt geïnstantieerd en de berichtregistratie vindt plaats OnNavigatedTo(), zodat alles correct wordt geïnitialiseerd wanneer de pagina actief wordt.

Dit patroon scheidt problemen effectief: het ViewModel richt zich op bedrijfslogica en gegevensbewerkingen, terwijl de pagina ui-specifieke problemen, zoals navigatie, afhandelt.