Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Die Abhängigkeitsinjektion (DI) unterstützt Sie bei der Verwaltung des Lebenszyklus Ihrer ViewModels und Dienste. Dadurch wird ihr Code leichter testbar und einfacher verwaltet. In diesem Schritt konfigurieren Sie DI in Ihrer App und aktualisieren Ihre Modelle so, dass ein Dateidienst für Dateivorgänge verwendet wird.
Weitere Hintergrundinformationen zum .NET-Abhängigkeitsinjektionsframework finden Sie unter .NET-Abhängigkeitsinjektion und das Lernprogramm zum Verwenden der Abhängigkeitsinjektion .
Installieren von Microsoft.Extensions-Paketen
Fügen Sie DI-Unterstützung zu Ihren Projekten hinzu.
Installieren Sie
Microsoft.Extensions.DependencyInjectionin den Projekten WinUINotes und WinUINotes.Bus:dotnet add WinUINotes package Microsoft.Extensions.DependencyInjection dotnet add WinUINotes.Bus package Microsoft.Extensions.DependencyInjection
Erstellen einer Dateidienstschnittstelle und -implementierung
Erstellen Sie im Projekt WinUINotes.Bus einen neuen Ordner namens "Dienste".
Hinzufügen einer Schnittstellendatei
IFileService.cs: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(); } }Die Dateidienstschnittstelle definiert Methoden für Dateivorgänge. Es sorgt dafür, dass die Dateiverarbeitungsdetails von den ViewModels und Modellen abstrahiert werden. Die Parameter und Rückgabewerte sind alle grundlegenden .NET-Typen oder Schnittstellen. Mit diesem Design wird sichergestellt, dass der Dienst in Komponententests leicht simuliert oder ersetzt werden kann, um lose Kopplung und Testbarkeit zu fördern.
Fügen Sie die Implementierungsdatei
WindowsFileService.cshinzu: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); } } }
Die WindowsFileService Implementierung stellt konkrete Dateivorgänge mithilfe der Windows-Runtime (WinRT) und .NET-Speicher-APIs bereit:
-
Konstruktorinjektion: Der Dienst akzeptiert ein
IStorageFolderin seinem Konstruktor. Mit diesem Ansatz können Sie den Speicherort konfigurieren, wenn Sie den Dienst instanziieren. Dieser Ansatz macht den Dienst flexibel und testbar. -
CreateOrUpdateFileAsync(): Mit dieser Methode wirdTryGetItemAsync()überprüft, ob bereits eine Datei vorhanden ist. Wenn dies der Fall ist, aktualisiert die Methode die vorhandene Datei. Andernfalls erstellt sie eine neue Datei mithilfe vonCreateFileAsync(). Dieser Ansatz behandelt sowohl Erstellungs- als auch Aktualisierungsszenarien in einer einzigen Methode. -
DeleteFileAsync(): Vor dem Löschen einer Datei überprüft diese Methode, ob die Datei mithilfe vonTryGetItemAsync()existiert. Diese Überprüfung verhindert, dass Ausnahmen ausgelöst werden, wenn man versucht, nicht vorhandene Dateien zu löschen. -
FileExists(): Diese synchrone Methode überprüft das Vorhandensein einer Datei, indem sie die asynchroneTryGetItemAsync()aufruft und mit.Resultblockiert. Obwohl dieser Ansatz im Allgemeinen nicht empfohlen wird, wird er hier verwendet, um dieCanDelete()Validierungsmethode im ViewModel zu unterstützen, die synchron sein muss. -
Speicherelementmethoden: Die
GetStorageItemsAsync()Methoden undGetTextFromFileAsync()Methoden ermöglichen den Zugriff auf Dateien und deren Inhalte mithilfe von WinRT-Speicher-APIs. Mit diesen Methoden können die Modelle Notizen laden und aufzählen.
Durch die Implementierung der IFileService Schnittstelle können Sie diese Klasse ganz einfach durch eine Simuliertimplementierung zum Testen oder einen anderen Speicheranbieter ersetzen, falls erforderlich.
Weitere Informationen finden Sie in den Dokumenten:
Konfigurieren der Abhängigkeitsinjektion in App.xaml.cs
Bevor Sie die Modelle und ViewModels für die Verwendung des Dateidiensts aktualisieren, konfigurieren Sie die Abhängigkeitseinfügung, damit der Dienst aufgelöst und in die Konstruktoren eingefügt werden kann.
Aktualisieren Sie die App.xaml.cs Datei, um den DI-Container einzurichten:
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;
}
Diese Konfiguration richtet den Container zum Einfügen von Abhängigkeiten mit allen erforderlichen Diensten ein:
-
ConfigureServices()-Methode: Eine statische Methode, die die Dienstauflistung erstellt und konfiguriert. Durch das Trennen dieser Methode wird die Konfiguration besser verwaltet und einfacher zu testen. -
Services-Eigenschaft: Eine Instanz-Eigenschaft, die dieIServiceProvider. Der Konstruktor legt diese Eigenschaft durch AufrufenConfigureServices()fest. -
App.Currentstatische Eigenschaft: Bietet bequemen Zugriff auf die aktuelleAppInstanz, die nützlich ist, wenn Modelle oder andere Klassen auf den Dienstanbieter zugreifen müssen. -
IFileServiceregistration: VerwendetActivatorUtilities.CreateInstancezum Erstellen einerWindowsFileServiceInstanz mitApplicationData.Current.LocalFolderals Parameter. Mit diesem Ansatz kann der Konstruktorparameter während der Registrierung eingefügt werden. Registrieren Sie den Dienst als Singleton, da Dateivorgänge zustandslos sind und eine einzelne Instanz in der gesamten Anwendung geteilt werden kann. - ViewModels-Registrierung: Registrieren Sie beide ViewModels als vorübergehend, d. h., eine neue Instanz wird jedes Mal erstellt, wenn eine angefordert wird. Mit diesem Ansatz wird sichergestellt, dass jede Seite eine eigene ViewModel-Instanz mit sauberem Zustand erhält.
Modelle und andere Klassen können bei Bedarf auf den Dienstanbieter App.Current.Services.GetService() zugreifen, um registrierte Dienste abzurufen.
Weitere Informationen finden Sie in den Dokumenten:
Aktualisieren von Modellen zur Verwendung des Dateidiensts
Nachdem der Dateidienst nun über die Abhängigkeitseinfügung verfügbar ist, aktualisieren Sie die Modellklassen, um ihn zu verwenden. Die Modelle erhalten den Dateidienst und verwenden ihn für alle Dateivorgänge.
Aktualisieren des Notizmodells
Aktualisieren Sie die Note Klasse so, dass der Dateidienst akzeptiert wird, und verwenden Sie sie für Speicher-, Lösch- und Dateiexistenzvorgänge:
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);
}
}
Das Note-Modell erhält jetzt den Dateidienst über die Konstruktor-Injektion.
-
Konstruktor: Akzeptiert einen
IFileServiceParameter, wodurch die Abhängigkeit explizit und erforderlich wird. Dieses Design fördert die Testbarkeit und stellt sicher, dass das Modell immer Zugriff auf den benötigten Dateidienst hat. - Dateinamegenerierung: Der Konstruktor generiert automatisch einen eindeutigen Dateinamen mithilfe des aktuellen Zeitstempels, um sicherzustellen, dass jede Notiz einen eindeutigen Dateinamen aufweist.
-
Dateivorgänge: Die Methoden
SaveAsync(),DeleteAsync()undNoteFileExists()delegieren alle an den eingefügten Dateidienst und konzentrieren das Modell auf die Koordination der Vorgänge, anstatt die Details der Datei-Eingabe/Ausgabe zu implementieren.
Bei diesem Ansatz ist es nicht erforderlich, dass das Modell das Dienstlocatormuster verwendet, indem es auf App.Services direkt zugreift, was die Testbarkeit verbessert und Abhängigkeiten deutlich macht.
Aktualisieren des AllNotes-Modells
Aktualisieren Sie die AllNotes Klasse so, dass Notizen aus dem Speicher mithilfe des Dateidiensts geladen werden:
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);
}
}
}
}
Das AllNotes Modell empfängt den Dateidienst über die Konstruktoreinfügung, genau wie das Note Modell. Da sich diese Klasse im WinUINotes.Bus Projekt befindet, kann sie nicht über das App.Current.Services Projekt zugreifen WinUINotes (aufgrund von Projektverweisbeschränkungen).
Die LoadNotes() Methode ruft die private GetFilesInFolderAsync() Methode auf, um alle Dateien im lokalen Speicherordner und deren Unterordner rekursiv aufzählen zu können. Für jedes Speicherelement:
- Wenn es sich um einen Ordner handelt, ruft sich die Methode rekursiv auf, um den Inhalt des Ordners zu verarbeiten.
- Wenn es sich um eine Datei handelt, wird eine neue
NoteInstanz mit dem eingefügten Dateidienst erstellt. - Die Notiz
Filenameist auf den Dateinamen gesetzt. - Die
Textder Notiz wird durch das Lesen der Dateiinhalte unter Verwendung vonGetTextFromFileAsync()befüllt. - Die Notiz
Dateist auf das Erstellungsdatum der Datei festgelegt. - Die Notiz wird der ObservableCollection
Noteshinzugefügt.
Dieser Ansatz stellt sicher, dass alle aus dem Speicher geladenen Notizen Zugriff auf den Dateidienst haben, den sie für zukünftige Speicher- und Löschvorgänge benötigen.
Aktualisieren von ViewModels für die Verwendung des Dateidiensts
Wenn die Modelle jetzt den Dateidienst verwenden, müssen Sie die ViewModels aktualisieren. Da die Modelle die Dateivorgänge jedoch direkt verarbeiten, konzentrieren sich die ViewModels hauptsächlich auf die Orchestrierung der Modelle und das Verwalten von observierbaren Eigenschaften.
Aktualisieren von AllNotesViewModel
Aktualisieren Sie AllNotesViewModel, um mit dem aktualisierten AllNotes Modell zu arbeiten.
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);
}
}
}
}
Was hat sich seit Schritt 2 geändert?
Die Schlüsseländerung ist das Hinzufügen des IFileService Parameters zum Konstruktor. In Schritt 2 instanziiert AllNotes das ViewModel mit einem parameterlosen Konstruktor (allNotes = new AllNotes()). Da das AllNotes-Modell nun den Dateidienst zur Ausführung seiner Vorgänge benötigt, erhält das ViewModel die IFileService-Konstruktor-Injektion und übergibt sie an das Modell.
Diese Änderung behält den ordnungsgemäßen Abhängigkeitsfluss bei – der Dateidienst wird auf oberster Ebene (ViewModel) implementiert und fließt bis zum Modell. Das ViewModel konzentriert sich weiterhin auf die Koordination des Ladevorgangs und die Synchronisierung der feststellbaren Notes Sammlung mit den Daten des Modells, ohne die Implementierungsdetails darüber zu kennen, wie Dateien geladen werden.
Aktualisieren von NoteViewModel
Aktualisieren Sie NoteViewModel, um den Dateidienst einzufügen und das Messagingsystem des MVVM-Toolkits zu verwenden:
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();
}
}
}
Was hat sich seit Schritt 2 geändert?
Mehrere wichtige Änderungen unterstützen die Einfügung von Abhängigkeiten und die Kommunikation zwischen ViewModel:
Dateidienstinjektion: Der Konstruktor akzeptiert
IFileServicejetzt als Parameter und speichert ihn in einem Feld. Dieser Dienst wird beim Erstellen neuer Instanzen an dasNoteModell übergeben, um sicherzustellen, dass alle Notizen Dateivorgänge ausführen können.WeakReferenceMessenger: Die
Delete()Methode verwendet jetzt das MVVM-ToolkitWeakReferenceMessenger.Default.Send()zum Übertragen einerNoteDeletedMessageNotiz nach dem Löschen einer Notiz. Dieser Ansatz ermöglicht eine lose Kopplung zwischen ViewModels – andere Teile der Anwendung (wieNotePagez.B.) können auf diese Nachricht lauschen und entsprechend reagieren (z.B. durch Navigieren, um die aktualisierte Notizliste anzuzeigen), ohne dassNoteViewModeldafür einen direkten Verweis benötigt.
Dies WeakReferenceMessenger ist ein wichtiges Feature des MVVM-Toolkits, das Speicherverluste mithilfe schwacher Verweise verhindert. Komponenten können Nachrichten abonnieren, ohne starke Verweise zu erstellen, die die Garbage Collection verhindern.
Weitere Informationen finden Sie in den Dokumenten:
Erstellen der NoteDeletedMessage-Klasse
Für den Versand zwischen Komponenten benötigt die WeakReferenceMessenger eine Nachrichtenklasse. Erstellen Sie eine neue Klasse zur Darstellung des Löschens eines Notizenereignisses.
Fügen Sie im Projekt WinUINotes.Bus eine neue Klassendatei
NoteDeletedMessage.cshinzu:using CommunityToolkit.Mvvm.Messaging.Messages; using WinUINotes.Models; namespace WinUINotes { public class NoteDeletedMessage : ValueChangedMessage<Note> { public NoteDeletedMessage(Note note) : base(note) { } } }
Diese Nachrichtenklasse erbt von ValueChangedMessage<Note>, bei dem es sich um einen speziellen Nachrichtentyp handelt, der vom MVVM-Toolkit zum Tragen von Wertänderungsbenachrichtigungen bereitgestellt wird. Der Konstruktor akzeptiert ein Note und übergibt es an die Basisklasse, sodass es Nachrichtenempfängern über die Value-Eigenschaft zur Verfügung gestellt wird. Wenn NoteViewModel diese Nachricht sendet, erhält jede Komponente, die NoteDeletedMessage abonniert, sie und kann über die Value-Eigenschaft auf die gelöschte Notiz zugreifen.
Funktionsweise von Messaging im MVVM-Toolkit:
-
Sender: Die
NoteViewModel.Delete()Methode sendet die Nachricht mithilfe vonWeakReferenceMessenger.Default.Send(new NoteDeletedMessage(note)). -
Empfänger: Seiten (wie
NotePage) können sich registrieren, um Nachrichten zu empfangen, indem sieIRecipient<NoteDeletedMessage>implementieren und sich beim Messenger registrieren. Wenn die Nachricht empfangen wird, kann die Seite zurück zur Liste aller Notizen navigieren. - Lose Kopplung: Der Absender muss nicht wissen, wer (wenn jemand) hört. Der Empfänger benötigt keinen direkten Verweis auf den Absender. Diese Einrichtung sorgt dafür, dass Ihre Komponenten unabhängig und testbar sind.
Der schwache Verweisansatz bedeutet, dass das Nachrichtenabonnement automatisch bereinigt wird, wenn eine Komponente von der Speicherbereinigung betroffen ist, ohne Speicherverluste zu verursachen.
Seiten für die Nutzung von Dependency Injection aktualisieren
Aktualisieren Sie Ihre Seitenkonstruktoren, um die ViewModels über DI zu empfangen.
Aktualisieren von 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();
}
}
}
}
Was hat sich seit Schritt 2 geändert?
Die App erhält nun das AllNotesViewModel aus dem Dependency Injection Container, indem sie App.Current.Services.GetService<AllNotesViewModel>() verwendet, anstatt es direkt mit new AllNotesViewModel() zu erstellen. Dieser Ansatz hat mehrere Vorteile:
-
Automatische Abhängigkeitsauflösung: Der DI-Container stellt automatisch die Abhängigkeit bereit, die
IFileServiceAllNotesViewModelin seinem Konstruktor benötigt. - Lebenszyklusverwaltung: Der DI-Container verwaltet den Lebenszyklus von ViewModel entsprechend der Registrierung (in diesem Fall als vorübergehende Bereitstellung einer neuen Instanz).
- Testbarkeit: Dieses Muster erleichtert das Austauschen von Implementierungen oder simulierten Abhängigkeiten in Tests.
- Verwendbarkeit: Wenn sich die Abhängigkeiten von ViewModel in Zukunft ändern, müssen Sie nur die DI-Konfiguration aktualisieren, nicht an jedem Ort, an dem das ViewModel erstellt wird.
Der Rest des Codes bleibt gleich. Die OnNavigatedTo() Methode ruft weiterhin auf LoadAsync() , um die Notizenliste zu aktualisieren, wenn der Benutzer zu dieser Seite navigiert.
Aktualisieren 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);
}
}
}
}
Was hat sich seit Schritt 2 geändert?
Mehrere wichtige Änderungen integrieren Abhängigkeitsinjektions- und Messagingfunktionen:
-
ViewModel über Dependency Injection Container: Das
NoteViewModelwird jetzt mithilfe vonApp.Current.Services.GetService<NoteViewModel>()in derOnNavigatedTo()-Methode aus dem Dependency Injection Container abgerufen, anstatt direkt instanziiert zu werden. Mit diesem Ansatz wird sichergestellt, dass das ViewModel automatisch seine erforderlicheIFileServiceAbhängigkeit erhält. -
Nachrichtenregistrierung: Die neue
RegisterForDeleteMessages()Methode abonniertNoteDeletedMessagemithilfe derWeakReferenceMessenger. Wenn eine Notiz (aus derNoteViewModel.Delete()Methode) gelöscht wird, empfängt diese Seite die Nachricht und navigiert mithilfe der VerwendungFrame.GoBack()zurück zur gesamten Notizenliste. -
Messagingmuster: Dieses Muster veranschaulicht die lose Kopplung, die vom Messagingsystem des MVVM-Toolkits aktiviert wird. Das
NoteViewModelmuss nichts über die Navigation oder die Seitenstruktur wissen. Es sendet einfach eine Nachricht, wenn eine Notiz gelöscht wird, und die Seite verarbeitet die Navigationsantwort eigenständig. -
Lebenszyklusdauer: Das ViewModel wird instanziiert, und die Nachrichtenregistrierung erfolgt in
OnNavigatedTo(), um sicherzustellen, dass alles ordnungsgemäß initialisiert wird, wenn die Seite aktiv wird.
Dieses Muster trennt effektiv Probleme: Das ViewModel konzentriert sich auf Geschäftslogik und Datenvorgänge, während die Seite UI-spezifische Probleme wie die Navigation behandelt.
Windows developer