Delen via


MVVM implementeren met de MVVM Toolkit

Nu u de projectstructuur hebt geïmplementeerd, kunt u beginnen met het implementeren van het MVVM-patroon met behulp van de MVVM Toolkit. Deze stap omvat het maken van ViewModels die gebruikmaken van de functies van de MVVM Toolkit, zoals ObservableObject voor het wijzigen van eigenschappen en RelayCommand voor de implementatie van opdrachten.

Het NuGet-pakket MVVM Toolkit installeren

U moet de MVVM Toolkit installeren in zowel de WinUINotes - als WinUINotes.Bus-projecten .

Visual Studio gebruiken

  1. Klik met de rechtermuisknop op het WinUINotes.Bus-project in Solution Explorer.
  2. Selecteer NuGet-pakketten beheren.
  3. Zoek naar CommunityToolkit.Mvvm en installeer de nieuwste stabiele versie.
  4. Herhaal deze stappen voor het WinUINotes-project .

.NET CLI gebruiken

U kunt ook de .NET CLI gebruiken om het pakket te installeren:

dotnet add WinUINotes.Bus package CommunityToolkit.Mvvm
dotnet add WinUINotes package CommunityToolkit.Mvvm

Ontwerpbeslissingen voor de modellaag

Wanneer u MVVM implementeert, is het belangrijk om te bepalen hoe u uw modelklassen moet structuren in relatie tot de ViewModels. In deze zelfstudie zijn de modelklassen (Note en AllNotes) verantwoordelijk voor gegevensweergave, bedrijfslogica en het bijwerken van gegevensopslag. De ViewModels verwerken waarneembare eigenschappen, wijzigingsmeldingen en opdrachten voor interactie met de gebruikersinterface.

In een eenvoudigere implementatie kunt u eenvoudige oude CLR-objecten (POCOs's) gebruiken voor de modelklassen zonder bedrijfslogica of methoden voor gegevenstoegang. In dat geval verwerken de ViewModels alle gegevensbewerkingen via de servicelaag. Voor deze zelfstudie bevatten de modelklassen echter methoden voor het laden, opslaan en verwijderen van notities om een duidelijkere scheiding van zorgen te bieden en de ViewModels gefocust te houden op presentatielogica.

Het notitiemodel verplaatsen

Verplaats de Note klasse naar het WinUINotes.Bus-project . Het blijft een eenvoudige modelklasse met enkele logica voor gegevensweergave en statusbeheer, maar zonder MVVM Toolkit-functies. De ViewModels verwerken de waarneembare eigenschappen en wijzigingsmeldingen, niet het model zelf.

  1. Maak in het Project WinUINotes.Bus een nieuwe map met de naam Models.

  2. Verplaats het Note.cs bestand van het WinUINotes-project naar de map WinUINotes.Bus/Models .

  3. Werk de naamruimte bij zodat deze overeenkomt met de nieuwe locatie:

    namespace WinUINotes.Models
    {
        public class Note
        {
            // Existing code remains unchanged
            ...
        }
    }
    

De Note klasse is een eenvoudig gegevensmodel. Er is geen wijzigingsmelding nodig omdat de ViewModels waarneembare eigenschappen beheert en de gebruikersinterface informeert over wijzigingen.

Het AllNotes-model verplaatsen

Verplaats de AllNotes klasse naar het WinUINotes.Bus-project .

  1. Verplaats het AllNotes.cs bestand van het WinUINotes-project naar de map WinUINotes.Bus/Models .

  2. Werk de naamruimte bij zodat deze overeenkomt met de nieuwe locatie:

    namespace WinUINotes.Models
    {
        public class AllNotes
        {
            // Existing code remains unchanged
            ...
        }
    }
    

Net als de Note klasse AllNotes is dit een eenvoudige modelklasse. Het ViewModel verwerkt het waarneembare gedrag en beheert de verzameling notities.

Het AllNotesViewModel maken

  1. Maak in het Project WinUINotes.Bus een nieuwe map met de naam ViewModels.

  2. Voeg een nieuw klassebestand toe met de naam AllNotesViewModel.cs met de volgende inhoud:

    using CommunityToolkit.Mvvm.ComponentModel;
    using CommunityToolkit.Mvvm.Input;
    using System.Collections.ObjectModel;
    using System.Threading.Tasks;
    using WinUINotes.Models;
    
    namespace WinUINotes.ViewModels
    {
        public partial class AllNotesViewModel : ObservableObject
        {
            private readonly AllNotes allNotes;
    
            [ObservableProperty]
            private ObservableCollection<Note> notes;
    
            public AllNotesViewModel()
            {
                allNotes = new AllNotes();
                notes = new ObservableCollection<Note>();
            }
    
            [RelayCommand]
            public async Task LoadAsync()
            {
                await allNotes.LoadNotes();
                Notes.Clear();
                foreach (var note in allNotes.Notes)
                {
                    Notes.Add(note);
                }
            }
        }
    }
    

De AllNotesViewModel beheert de verzameling notities die in de gebruikersinterface worden weergegeven.

  • [ObservableProperty]: Het notes veld genereert automatisch een openbare Notes eigenschap met wijzigingsmelding. Wanneer de Notes verzameling wordt gewijzigd, wordt de gebruikersinterface automatisch bijgewerkt.
  • allNotes model: Dit privéveld bevat een exemplaar van het AllNotes model, waarmee de werkelijke gegevensbewerkingen worden verwerkt.
  • [RelayCommand]: Met dit kenmerk wordt een LoadCommand eigenschap gegenereerd op basis van de LoadAsync() methode, zodat de gebruikersinterface de laadbewerking kan activeren via gegevensbinding.
  • LoadAsync() methode: Met deze methode worden notities uit het model geladen, wordt de huidige waarneembare verzameling gewist en gevuld met de geladen notities. Dit patroon zorgt ervoor dat de aan de gebruikersinterface gebonden verzameling gesynchroniseerd blijft met de onderliggende gegevens.

De scheiding tussen het allNotes model (gegevensbewerkingen) en de Notes waarneembare verzameling (UI-binding) is een belangrijk MVVM-patroon dat zorgen gescheiden houdt en de weergave synchroon blijft met de gegevens van het ViewModel.

Meer informatie in de documenten:

Het NoteViewModel maken

  1. Voeg in de map ViewModels een nieuw klassebestand toe met de naam NoteViewModel.cs:

    using CommunityToolkit.Mvvm.ComponentModel;
    using CommunityToolkit.Mvvm.Input;
    using System;
    using System.Threading.Tasks;
    using WinUINotes.Models;
    
    namespace WinUINotes.ViewModels
    {
        public partial class NoteViewModel : ObservableObject
        {
            private Note note;
    
            [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()
            {
                this.note = new Note();
                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();
            }
    
            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();
            }
        }
    }
    

De NoteViewModel demonstreert verschillende belangrijke MVVM Toolkit-functies:

  • [ObservableProperty]: De filenamevelden , texten date velden genereren automatisch openbare eigenschappen (Filename, Text, Date) met ondersteuning voor wijzigingsmeldingen.
  • [NotifyCanExecuteChangedFor]: Dit kenmerk zorgt ervoor dat wanneer Filename of Text wijzigingen worden uitgevoerd, de bijbehorende opdrachten opnieuw evalueren of ze kunnen worden uitgevoerd. Wanneer u bijvoorbeeld tekst typt, wordt de knop Opslaan automatisch ingeschakeld of uitgeschakeld op basis van de validatielogica.
  • [RelayCommand(CanExecute = nameof(CanSave))]: Met dit kenmerk wordt een SaveCommand eigenschap gegenereerd die is gebonden aan de validatiemethode CanSave(). De opdracht is alleen ingeschakeld wanneer zowel Text als Filename waarden hebben.
  • InitializeForExistingNote(): Met deze methode worden de gegevens van een bestaande notitie in de ViewModel-eigenschappen geladen, die vervolgens de gebruikersinterface bijwerken via gegevensbinding.
  • Logica opslaan: de Save() methode werkt het onderliggende Note model bij met de huidige eigenschapswaarden en aanroepen SaveAsync() op het model. Nadat het is opgeslagen, meldt het DeleteCommand dat het opnieuw moet worden geëvalueerd (omdat er nu een bestand bestaat en kan worden verwijderd).
  • Verwijderlogica: de Delete() methode roept DeleteAsync() het notitiemodel aan en maakt een nieuwe lege notitie.

Verderop in deze zelfstudie integreert u de bestandsservice voor het afhandelen van de werkelijke bestandsbewerkingen en gebruikt u de klasse van WeakReferenceMessenger de MVVM Toolkit om andere onderdelen van de app op de hoogte te stellen wanneer een notitie wordt verwijderd terwijl deze losjes gekoppeld blijft.

Meer informatie in de documenten:

De weergaven bijwerken om de ViewModels te gebruiken

Nu moet u uw XAML-pagina's bijwerken om verbinding te maken met de nieuwe ViewModels.

De weergave van AllNotesPage bijwerken

  1. Werk de AllNotesPage.xaml in de ItemsSource binding van de ItemsView bij om de eigenschap Notes van de ViewModel te gebruiken:

    <ItemsView ItemsSource="{x:Bind viewModel.Notes}"
    ...
    
  2. Werk het AllNotesPage.xaml.cs bestand zo bij dat het er als volgt uitziet:

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

In dit code-behind-bestand instantieert de constructor de AllNotesViewModel direct. De OnNavigatedTo() methode roept de LoadAsync() methode op het ViewModel aan wanneer de pagina naartoe wordt genavigeerd. Met deze methode worden de notities uit de opslag geladen en wordt de waarneembare verzameling bijgewerkt. Dit patroon zorgt ervoor dat de gegevens altijd worden vernieuwd wanneer de gebruiker naar de pagina met alle notities navigeert.

Verderop in deze zelfstudie herstructureert u deze code voor het gebruik van afhankelijkheidsinjectie, waardoor het ViewModel in de paginaconstructor kan worden geïnjecteerd in plaats van rechtstreeks te worden gemaakt. Deze aanpak verbetert de testbaarheid en maakt het eenvoudiger om de levenscyclus van ViewModel te beheren.

De notitiepaginaweergave bijwerken

  1. Werk de TextBox bindingen voor Text en Header in NotePage.xaml bij om de eigenschappen van de ViewModel te gebruiken. Werk de StackPanel knoppen bij om verbinding te maken met de opdrachten in plaats van de Click gebeurtenissen te gebruiken:

    ...
    <TextBox x:Name="NoteEditor"
             Text="{x:Bind noteVm.Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
             AcceptsReturn="True"
             TextWrapping="Wrap"
             PlaceholderText="Enter your note"
             Header="{x:Bind noteVm.Date.ToString()}"
             ScrollViewer.VerticalScrollBarVisibility="Auto"
             MaxWidth="400"
             Grid.Column="1"/>
    
    <StackPanel Orientation="Horizontal"
                HorizontalAlignment="Right"
                Spacing="4"
                Grid.Row="1" Grid.Column="1">
        <Button Content="Save" Command="{x:Bind noteVm.SaveCommand}"/>
        <Button Content="Delete" Command="{x:Bind noteVm.DeleteCommand}"/>
    </StackPanel>
    ...
    

    UpdateSourceTrigger stelt u ook in op de TextBox.Text binding zodat wijzigingen naar het ViewModel worden verzonden als de gebruiker typen. Met deze instelling kan de Save knop in realtime in- of uitschakelen op basis van de invoer.

  2. Werk de code bij in NotePage.xaml.cs om NoteViewModel te gebruiken.

    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();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                base.OnNavigatedTo(e);
                noteVm = new NoteViewModel();
    
                if (e.Parameter is Note note && noteVm is not null)
                {
                    noteVm.InitializeForExistingNote(note);
                }
            }
        }
    }
    

    De Click gebeurtenissen voor Save en Delete worden verwijderd omdat de knoppen nu rechtstreeks aan de opdrachten in het ViewModel worden gekoppeld. De NoteViewModel instantie wordt geïnstantieerd in de OnNavigatedTo() methode. Als een Note parameter wordt doorgegeven, wordt het ViewModel geïnitialiseerd met de bestaande notitiegegevens.

Meer informatie in de documenten: