Dela via


Implementera MVVM med MVVM Toolkit

Nu när du har projektstrukturen på plats kan du börja implementera MVVM-mönstret med hjälp av MVVM Toolkit. Det här steget handlar om att skapa ViewModels som använder MVVM Toolkits funktioner, till exempel ObservableObject för meddelande om egenskapsändring och RelayCommand för kommandoimplementering.

Installera MVVM Toolkit NuGet-paketet

Du måste installera MVVM Toolkit i både WinUINotes - och WinUINotes.Bus-projekten .

Använda Visual Studio

  1. Högerklicka på WinUINotes.Bus-projektet i Solution Explorer.
  2. Välj Hantera NuGet-paket.
  3. Sök efter CommunityToolkit.Mvvm och installera den senaste stabila versionen.
  4. Upprepa de här stegen för WinUINotes-projektet .

Använda .NET CLI

Du kan också använda .NET CLI för att installera paketet:

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

Designbeslut för modellskiktet

När du implementerar MVVM är det viktigt att bestämma hur du ska strukturera dina modellklasser i förhållande till ViewModels. I den här självstudien ansvarar modellklasserna (Note och AllNotes) för datarepresentation, affärslogik och uppdatering av datalagring. ViewModels hanterar observerbara egenskaper, ändringsmeddelanden och kommandon för interaktion med användargränssnittet.

I en enklare implementering kan du använda vanliga gamla CLR-objekt (POCOs) för modellklasserna utan affärslogik eller dataåtkomstmetoder. I så fall hanterar ViewModels alla dataåtgärder via tjänstlagret. I den här handledningen innehåller dock modellklasserna metoder för att läsa in, spara och ta bort anteckningar för att ge en tydligare separation av ansvar och hålla ViewModels fokuserade på presentationslogik.

Flytta anteckningsmodellen

Note Flytta klassen till Projektet WinUINotes.Bus. Det är fortfarande en enkel modellklass med viss logik för datarepresentation och tillståndshantering, men utan några MVVM Toolkit-funktioner. ViewModels hanterar de observerbara egenskaperna och ändringsmeddelandet, inte själva modellen.

  1. I Projektet WinUINotes.Bus skapar du en ny mapp med namnet Modeller.

  2. Note.cs Flytta filen från WinUINotes-projektet till mappen WinUINotes.Bus/Models.

  3. Uppdatera namnområdet så att det matchar den nya platsen:

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

Klassen Note är en enkel datamodell. Det behöver inte ändras eftersom ViewModels hanterar observerbara egenskaper och meddelar användargränssnittet om ändringar.

Flytta AllNotes-modellen

AllNotes Flytta klassen till Projektet WinUINotes.Bus.

  1. AllNotes.cs Flytta filen från WinUINotes-projektet till mappen WinUINotes.Bus/Models.

  2. Uppdatera namnområdet så att det matchar den nya platsen:

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

Precis som Note klassen AllNotes är en enkel modellklass. ViewModel hanterar det observerbara beteendet och hanterar samlingen med anteckningar.

Skapa AllNotesViewModel

  1. I Projektet WinUINotes.Bus skapar du en ny mapp med namnet ViewModels.

  2. Lägg till en ny klassfil med namnet AllNotesViewModel.cs med följande innehåll:

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

Hanterar AllNotesViewModel den samling anteckningar som visas i användargränssnittet:

  • [ObservableProperty]: Fältet notes genererar automatiskt en offentlig Notes egenskap med ändringsmeddelande. När samlingen Notes ändras uppdateras användargränssnittet automatiskt.
  • allNotes modell: Det här privata fältet innehåller en instans av AllNotes modellen som hanterar de faktiska dataåtgärderna.
  • [RelayCommand]: Det här attributet genererar en LoadCommand egenskap från LoadAsync() metoden, vilket gör att användargränssnittet kan utlösa inläsningsåtgärden via databindning.
  • LoadAsync() metod: Den här metoden läser in anteckningar från modellen, rensar den aktuella observerbara samlingen och fyller den med de inlästa anteckningarna. Det här mönstret säkerställer att den gränssnittsbundna samlingen förblir synkroniserad med underliggande data.

Separationen allNotes mellan modellen (dataåtgärder) och den Notes observerbara samlingen (UI-bindning) är ett nyckelmönster i MVVM som håller ansvarsområden åtskilda och vyn synkroniserad med ViewModelens data.

Läs mer i dokumenten:

Skapa NoteViewModel

  1. I mappen ViewModels lägger du till en ny klassfil med namnet 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();
            }
        }
    }
    

Visar NoteViewModel flera viktiga MVVM Toolkit-funktioner:

  • [ObservableProperty]: Fälten filename, textoch date genererar automatiskt offentliga egenskaper (Filename, Text, Date) med stöd för ändringsmeddelanden.
  • [NotifyCanExecuteChangedFor]: Det här attributet säkerställer att när Filename eller Text ändras utvärderar de associerade kommandona om de kan köras. När du till exempel skriver text aktiverar eller inaktiverar knappen Spara automatiskt baserat på valideringslogik.
  • [RelayCommand(CanExecute = nameof(CanSave))]: Det här attributet genererar en SaveCommand egenskap som är bunden till valideringsmetoden CanSave(). Kommandot aktiveras endast när båda Text och Filename har värden.
  • InitializeForExistingNote(): Den här metoden läser in en befintlig antecknings data i egenskaperna ViewModel, som sedan uppdaterar användargränssnittet via databindning.
  • Spara logik: Metoden Save() uppdaterar den underliggande Note modellen med aktuella egenskapsvärden och anropar SaveAsync() modellen. När du har sparat, meddelar DeleteCommand att det ska utvärdera på nytt (eftersom det nu finns en fil som kan tas bort).
  • Ta bort logik: Metoden Delete() anropar DeleteAsync() anteckningsmodellen och skapar en ny tom anteckning.

Senare i den här självstudien integrerar du filtjänsten för att hantera de faktiska filåtgärderna och använder klassen MVVM Toolkit WeakReferenceMessenger för att meddela andra delar av appen när en anteckning tas bort samtidigt som den är löst kopplad.

Läs mer i dokumenten:

Uppdatera vyerna så att de använder ViewModels

Nu måste du uppdatera dina XAML-sidor för att binda till nya ViewModels.

Uppdatera AllNotesPage-vyn

  1. I AllNotesPage.xamluppdaterar du bindningen ItemsSource för ItemsView för att använda egenskapen ViewModel Notes :

    <ItemsView ItemsSource="{x:Bind viewModel.Notes}"
    ...
    
  2. AllNotesPage.xaml.cs Uppdatera filen så här:

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

I den här kod-bakgrundsfilen instansierar konstruktören AllNotesViewModel direkt. Metoden OnNavigatedTo() anropar LoadAsync() metoden på ViewModel när sidan navigeras till. Den här metoden läser in anteckningarna från lagringen och uppdaterar den observerbara samlingen. Det här mönstret säkerställer att data alltid uppdateras när användaren navigerar till sidan alla anteckningar.

Senare i den här självstudien omstrukturerar du koden så att den använder beroendeinmatning, vilket gör att ViewModel kan matas in i sidkonstruktorn i stället för att skapas direkt. Den här metoden förbättrar testbarheten och gör det enklare att hantera ViewModel-livscykeln.

Uppdatera vyn NotePage

  1. I NotePage.xaml, uppdatera bindningarna för TextBox och Text för att använda egenskaperna för ViewModel. Uppdatera StackPanel-knapparna så att de är bundna till kommandona istället för att använda Click-händelserna.

    ...
    <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>
    ...
    

    Du ställer också in UpdateSourceTrigger på bindningen TextBox.Text för att se till att ändringar skickas till ViewModel medan användaren skriver. Inställningen Save gör att knappen kan aktiveras eller inaktiveras i realtid baserat på indata.

  2. I NotePage.xaml.csuppdaterar du koden för att använda NoteViewModel:

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

    Händelserna Click för Save och Delete tas bort eftersom knapparna nu binder direkt till kommandona i ViewModel. NoteViewModel instansieras i OnNavigatedTo() metoden. Om en Note parameter skickas initieras ViewModel med befintliga anteckningsdata.

Läs mer i dokumenten: