Udostępnij przez


Dodaj wstrzykiwanie zależności

Wstrzykiwanie zależności (DI) ułatwia zarządzanie cyklem życia modelu ViewModels i usług. Dzięki temu kod jest bardziej testowalny i łatwiejszy w obsłudze. W tym kroku skonfigurujesz di w aplikacji i zaktualizujesz modele tak, aby korzystały z usługi plików na potrzeby operacji na plikach.

Aby uzyskać więcej informacji na temat struktury wstrzykiwania zależności platformy .NET, zobacz Iniekcja zależności platformy .NET i samouczek Używanie wstrzykiwania zależności na platformie .NET .

Instalowanie pakietów Microsoft.Extensions

Dodaj obsługę di do projektów.

  1. Zainstaluj Microsoft.Extensions.DependencyInjection zarówno w projektach WinUINotes, jak i WinUINotes.Bus.

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

Tworzenie interfejsu usługi plików i implementacji

  1. W projekcie WinUINotes.Bus utwórz nowy folder o nazwie Services.

  2. Dodaj plik IFileService.csinterfejsu :

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

    Interfejs usługi plików definiuje metody operacji na plikach. Abstrahuje ona szczegóły obsługi plików od ViewModels i Models. Parametry i wartości zwracane są podstawowymi typami .NET lub interfejsami. Ten projekt gwarantuje, że usługa może być łatwo wyśmiewany lub zastępowany w testach jednostkowych, promując luźne sprzężenie i możliwość testowania.

  3. Dodaj plik WindowsFileService.csimplementacji :

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

Implementacja WindowsFileService zapewnia konkretne operacje na plikach przy użyciu interfejsów API środowiska uruchomieniowego systemu Windows (WinRT) i magazynu platformy .NET:

  • Wstrzykiwanie konstruktora: usługa akceptuje IStorageFolder parametr w konstruktorze. Takie podejście umożliwia skonfigurowanie lokalizacji przechowywania podczas instancjonowania usługi. Takie podejście sprawia, że usługa jest elastyczna i testowalna.
  • CreateOrUpdateFileAsync(): Ta metoda służy TryGetItemAsync() do sprawdzania, czy plik już istnieje. Jeśli tak, metoda aktualizuje istniejący plik. W przeciwnym razie tworzy nowy plik przy użyciu polecenia CreateFileAsync(). Takie podejście obsługuje zarówno scenariusze tworzenia, jak i aktualizowania w jednej metodzie.
  • DeleteFileAsync(): Przed usunięciem pliku ta metoda sprawdza, czy plik istnieje przy użyciu polecenia TryGetItemAsync(). Ta kontrola uniemożliwia zgłaszanie wyjątków podczas próby usunięcia nieistniejących plików.
  • FileExists(): Ta synchroniczna metoda sprawdza istnienie pliku przez wywołanie asynchronicznego TryGetItemAsync() i zablokowanie za pomocą polecenia .Result. Chociaż takie podejście nie jest zwykle zalecane, jest ono używane tutaj do wsparcia metody weryfikacji CanDelete() w ViewModel, który musi być synchronizowany.
  • Metody elementów przechowywania: Metody GetStorageItemsAsync() i GetTextFromFileAsync() zapewniają dostęp do plików i ich zawartości przy użyciu interfejsów API przechowywania WinRT. Te metody umożliwiają modelom ładowanie i wyliczanie notatek.

Implementując IFileService interfejs, można łatwo zastąpić tę klasę pozorną implementacją do testów lub innym dostawcą przechowywania danych w razie potrzeby.

Dowiedz się więcej w dokumentacji:

Konfigurowanie wstrzykiwania zależności w App.xaml.cs

Przed zaktualizowaniem modeli i modeli widoków do korzystania z usługi plików, skonfiguruj iniekcję zależności, aby usługa mogła zostać rozpoznana i wstrzyknięta do konstruktorów.

Zaktualizuj plik, App.xaml.cs aby skonfigurować kontener 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;
}

Ta konfiguracja ustanawia kontener wstrzykiwania zależności z wszystkimi wymaganymi usługami:

  • ConfigureServices() metoda: metoda statyczna, która tworzy i konfiguruje kolekcję usług. Oddzielenie tej metody sprawia, że konfiguracja jest bardziej konserwowalna i łatwiejsza do przetestowania.
  • Services właściwość: właściwość instancji, która zawiera IServiceProvider. Konstruktor ustawia tę właściwość, wywołując metodę ConfigureServices().
  • App.Current właściwość statyczna: zapewnia wygodny dostęp do bieżącego App wystąpienia, co jest przydatne, gdy modele lub inne klasy muszą uzyskiwać dostęp do dostawcy usług.
  • IFileService rejestracja: używa ActivatorUtilities.CreateInstance aby utworzyć instancję WindowsFileService z ApplicationData.Current.LocalFolder jako parametr. Takie podejście umożliwia wstrzyknięcie parametru konstruktora w czasie rejestracji. Zarejestruj usługę jako singleton, ponieważ operacje na plikach są bezstanowe, a pojedyncze wystąpienie może być współdzielone w całej aplikacji.
  • Rejestracja modelu ViewModels: zarejestruj obie modele ViewModel jako przejściowe, co oznacza, że nowe wystąpienie jest tworzone za każdym razem, gdy jest wymagane jedno. Takie podejście zapewnia, że każda strona otrzymuje własne wystąpienie modelu ViewModel z czystym stanem.

Modele i inne klasy mogą uzyskiwać dostęp do dostawcy usług poprzez App.Current.Services.GetService() w celu pobrania zarejestrowanych usług, gdy jest to potrzebne.

Dowiedz się więcej w dokumentacji:

Aktualizowanie modeli w celu korzystania z usługi plików

Teraz, gdy usługa plików jest dostępna za pośrednictwem wstrzykiwania zależności, zaktualizuj klasy modelu, aby z niej korzystać. Modele otrzymują usługę plików i używają jej dla wszystkich operacji na plikach.

Aktualizowanie modelu Notatek

Zaktualizuj klasę, Note aby akceptowała usługę plików i używała jej do operacji zapisywania, usuwania i istnienia pliku:

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

Note Model odbiera teraz usługę plików za pomocą iniekcji konstruktora:

  • Konstruktor: akceptuje IFileService parametr, co sprawia, że zależność jest jawna i wymagana. Ten projekt promuje możliwość testowania i zapewnia, że model zawsze ma dostęp do wymaganej usługi plików.
  • Generowanie nazwy pliku: konstruktor automatycznie generuje unikatową nazwę pliku przy użyciu bieżącego znacznika czasu, zapewniając, że każda notatka ma odrębną nazwę pliku.
  • Operacje na plikach: Metody SaveAsync(), DeleteAsync() i NoteFileExists() delegują zadania do wprowadzonej usługi plików, skupiając model na koordynacji operacji, a nie na implementacji szczegółów we/wy pliku.

Takie podejście eliminuje konieczność używania wzorca lokalizatora usług (bezpośredni dostęp do App.Services), co poprawia testowalność i wyraźnie określa zależności.

Aktualizowanie modelu AllNotes

Zaktualizuj klasę AllNotes w celu załadowania notatek z magazynu przy użyciu serwisu plików.

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

Model AllNotes odbiera usługę plików za pomocą iniekcji konstruktora, podobnie jak model Note. Ponieważ ta klasa znajduje się w projekcie WinUINotes.Bus , nie może uzyskać dostępu App.Current.Services z WinUINotes projektu (ze względu na ograniczenia odwołania do projektu).

Metoda LoadNotes() wywołuje metodę prywatną GetFilesInFolderAsync() , aby rekursywnie wyliczać wszystkie pliki w folderze magazynu lokalnego i jego podfolderach. Dla każdego elementu magazynu:

  1. Jeśli jest to folder, metoda rekursywnie wywołuje się w celu przetworzenia zawartości folderu
  2. Jeśli jest to plik, tworzy nowe Note wystąpienie z implementacją usługi plików
  3. Nazwa notatki Filename jest ustawiona na nazwę pliku
  4. Notatka Text jest wypełniana przez odczytanie zawartości pliku przy użyciu polecenia GetTextFromFileAsync()
  5. Notatka Date jest ustawiona na datę utworzenia pliku
  6. Notatka jest dodawana do obserwowalnej Notes kolekcji

Takie podejście gwarantuje, że wszystkie notatki załadowane z magazynu mają dostęp do usługi plików, której potrzebują do przyszłych operacji zapisywania i usuwania.

Aktualizacja modeli ViewModel do korzystania z usługi plików

W przypadku modeli korzystających teraz z usługi plików należy zaktualizować modele ViewModels. Jednak ponieważ modele obsługują operacje na plikach bezpośrednio, model ViewModels koncentruje się przede wszystkim na organizowaniu modeli i zarządzaniu widocznymi właściwościami.

Aktualizowanie modelu AllNotesViewModel

** Zaktualizuj AllNotesViewModel do współpracy z modelem zaktualizowanym 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);
            }
        }
    }
}

Co zmieniło się od kroku 2?

Zmiana klucza to dodanie parametru IFileService do konstruktora. W kroku 2 utworzono wystąpienie AllNotes modelu ViewModel za pomocą konstruktora bez parametrów (allNotes = new AllNotes()). Teraz, gdy model AllNotes wymaga, aby usługa plików realizowała swoje operacje, ViewModel otrzymuje IFileService przez wstrzykiwanie konstruktora i przekazuje to modelowi.

Ta zmiana zapewnia prawidłowy przepływ zależności — usługa plików jest wstrzykiwana na poziomie najwyższym, którym jest ViewModel, a następnie przepływa do modelu. ViewModel nadal koncentruje się na koordynowaniu procesu ładowania i zachowaniu obserwowalnej Notes kolekcji zsynchronizowanej z danymi modelu bez konieczności znajomości szczegółów implementacji sposobu ładowania plików.

Aktualizowanie modelu NoteViewModel

Zaktualizuj NoteViewModel, aby wstrzyknąć usługę plików i użyć systemu wiadomości zestawu narzędzi 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();
        }
    }
}

Co zmieniło się od kroku 2?

Kilka ważnych zmian obsługuje iniekcję zależności i komunikację między modelami ViewModel:

  1. Iniekcja usługi plików: konstruktor akceptuje IFileService teraz jako parametr i przechowuje go w polu. Ta usługa jest przekazywana do Note modelu podczas tworzenia nowych instancji, zapewniając możliwość wykonywania operacji na plikach dla wszystkich notatek.

  2. WeakReferenceMessenger: Delete() metoda teraz używa zestawu narzędzi MVVM do emisji WeakReferenceMessenger.Default.Send() po usunięciu notatki. Takie podejście umożliwia luźne sprzężenie między modelami ViewModel — inne części aplikacji (na przykład NotePage) mogą nasłuchiwać tego komunikatu i odpowiednio reagować (na przykład cofając się do zaktualizowanej listy notatek) bez konieczności bezpośredniego NoteViewModel odwoływania się do nich.

Funkcja WeakReferenceMessenger jest kluczowym elementem zestawu narzędzi MVVM Toolkit, który zapobiega wyciekom pamięci przez użycie słabych odwołań. Składniki mogą subskrybować wiadomości bez tworzenia silnych odwołań, które uniemożliwiają odśmiecanie pamięci.

Dowiedz się więcej w dokumentacji:

Tworzenie klasy NoteDeletedMessage

Do WeakReferenceMessenger potrzebna jest klasa wiadomości do wysyłania między składnikami. Utwórz nową klasę reprezentującą zdarzenie usuwania notatek:

  1. W projekcie WinUINotes.Bus dodaj nowy plik NoteDeletedMessage.csklasy :

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

Ta klasa komunikatów dziedziczy z klasy ValueChangedMessage<Note>, która jest wyspecjalizowanym typem komunikatu dostarczonym przez zestaw narzędzi MVVM Toolkit do przekazywania powiadomień o zmianie wartości. Konstruktor akceptuje Note element i przekazuje go do klasy bazowej, udostępniając ją adresatom wiadomości za pośrednictwem Value właściwości . Po tym, jak NoteViewModel wyśle ten komunikat, każdy składnik, który subskrybuje NoteDeletedMessage, otrzyma go i może uzyskać dostęp do usuniętej notatki za pośrednictwem właściwości Value.

Jak działa obsługa komunikatów w zestawie narzędzi MVVM:

  1. NadawcaNoteViewModel.Delete(): metoda wysyła komunikat przy użyciu polecenia WeakReferenceMessenger.Default.Send(new NoteDeletedMessage(note)).
  2. Odbiornik: strony (na przykład NotePage) mogą rejestrować się w celu odbierania wiadomości przez zaimplementowanie IRecipient<NoteDeletedMessage> i zarejestrowanie się w messengerze. Po odebraniu wiadomości strona może wrócić do listy wszystkich notatek.
  3. Luźne sprzężenie: Nadawca nie musi wiedzieć, kto (jeśli ktoś) nasłuchuje. Odbiorca nie potrzebuje bezpośredniego odwołania do nadawcy. Ta konfiguracja zapewnia niezależne i testowalne składniki.

Słabe podejście referencyjne oznacza, że jeśli składnik jest odśmiecany, jego subskrypcja komunikatów jest automatycznie czyszczona bez powodowania przecieków pamięci.

Aktualizowanie stron w celu zastosowania wstrzykiwania zależności

Zaktualizuj konstruktory strony, aby otrzymywać ViewModel poprzez DI.

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

Co zmieniło się od kroku 2?

Aplikacja pobiera teraz element AllNotesViewModel z kontenera wstrzykiwania zależności przy użyciu App.Current.Services.GetService<AllNotesViewModel>() zamiast tworzyć go bezpośrednio za pomocą new AllNotesViewModel(). Takie podejście ma kilka korzyści:

  1. Automatyczne rozwiązywanie zależności: Kontener DI automatycznie zapewnia IFileService zależność, której wymaga AllNotesViewModel w konstruktorze.
  2. Zarządzanie cyklem życia: kontener DI zarządza cyklem życia modelu ViewModel zgodnie z tym, jak został zarejestrowany (w tym przypadku jako przejściowy, dostarczając nowe wystąpienie).
  3. Możliwość testowania: ten wzorzec ułatwia zamianę implementacji lub pozorowanie zależności w testach.
  4. Łatwość konserwacji: Jeśli w przyszłości zależności ViewModelu ulegną zmianie, wystarczy zaktualizować tę konfigurację DI, zamiast w każdym miejscu, w którym tworzony jest ViewModel.

Pozostała część kodu pozostaje taka sama. Metoda OnNavigatedTo() nadal wywołuje LoadAsync() metodę w celu odświeżenia listy notatek, gdy użytkownik przejdzie do tej strony.

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

Co zmieniło się od kroku 2?

Kilka ważnych zmian integruje funkcje wstrzykiwania zależności i obsługi komunikatów:

  1. ViewModel z kontenera DI: element NoteViewModel jest teraz pobierany z kontenera iniekcji zależności przy użyciu App.Current.Services.GetService<NoteViewModel>() stosowanego w metodzie OnNavigatedTo(), zamiast być bezpośrednio tworzonym jako wystąpienie. Takie podejście zapewnia, że model ViewModel automatycznie odbiera wymaganą IFileService zależność.
  2. Rejestracja komunikatów: nowa RegisterForDeleteMessages() metoda subskrybuje NoteDeletedMessage przy użyciu WeakReferenceMessenger. Gdy notatka zostanie usunięta (z NoteViewModel.Delete() metody ), ta strona odbiera komunikat i przechodzi z powrotem do listy wszystkich notatek przy użyciu polecenia Frame.GoBack().
  3. Wzorzec obsługi komunikatów: ten wzorzec pokazuje luźne sprzężenie umożliwione przez system obsługi komunikatów MVVM Toolkit. NoteViewModel nie musi wiedzieć o nawigacji ani strukturze strony — po prostu wysyła komunikat po usunięciu notatki, a strona obsługuje odpowiedź na nawigację niezależnie.
  4. Chronometraż cyklu życia: Model ViewModel jest tworzony, a rejestracja komunikatów odbywa się w OnNavigatedTo(), zapewniając, że wszystko jest poprawnie inicjowane, gdy strona zostaje aktywna.

Ten wzorzec skutecznie oddziela obawy: model ViewModel koncentruje się na operacjach logiki biznesowej i danych, podczas gdy strona obsługuje problemy specyficzne dla interfejsu użytkownika, takie jak nawigacja.