Sdílet prostřednictvím


Implementace MVVM pomocí MVVM Toolkit

Teď, když máte zavedenou strukturu projektu, můžete začít implementovat model MVVM pomocí MVVM Toolkit. Tento krok zahrnuje vytváření modelů ViewModel, které využívají funkce MVVM Toolkit, například ObservableObject pro oznámení o změně vlastností a RelayCommand pro implementaci příkazů.

Nainstalujte balíček NuGet MVVM Toolkit

Sadu nástrojů MVVM musíte nainstalovat do projektů WinUINotes i WinUINotes.Bus .

Použití sady Visual Studio

  1. V Průzkumníku řešení klikněte pravým tlačítkem na projekt WinUINotes.Bus .
  2. Vyberte Spravovat balíčky NuGet.
  3. Vyhledejte CommunityToolkit.Mvvm a nainstalujte nejnovější stabilní verzi.
  4. Tento postup opakujte pro projekt WinUINotes .

Použití rozhraní příkazového řádku .NET

Případně můžete balíček nainstalovat pomocí rozhraní příkazového řádku .NET:

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

Rozhodnutí o návrhu vrstvy modelu

Při implementaci MVVM je důležité rozhodnout, jak strukturovat třídy modelu ve vztahu k modelem ViewModels. V tomto kurzu jsou třídy modelů (Note a AllNotes) zodpovědné za reprezentaci dat, obchodní logiku a aktualizaci úložiště dat. Modely ViewModel zpracovávají pozorovatelné vlastnosti, oznámení o změnách a příkazy pro interakci s uživatelským rozhraním.

V jednodušší implementaci můžete pro třídy modelu použít prosté staré objekty CLR (POCOs) bez obchodní logiky nebo metod přístupu k datům. V takovém případě modely ViewModel zpracovávají všechny operace s daty prostřednictvím vrstvy služby. V tomto kurzu však třídy modelu zahrnují metody pro načítání, ukládání a odstraňování poznámek, aby poskytovaly jasnější oddělení obav a zachovaly modely ViewModels zaměřené na logiku prezentace.

Přesunutí modelu poznámky

Note Přesuňte třídu do projektu WinUINotes.Bus. Zůstává to jednoduchá třída modelu s určitou logikou pro reprezentaci dat a správu stavu, ale bez funkcí MVVM Toolkit. Modely ViewModel zpracovávají pozorovatelné vlastnosti a oznámení o změnách, nikoli samotný model.

  1. V projektu WinUINotes.Bus vytvořte novou složku s názvem Models.

  2. Note.cs Přesuňte soubor z projektu WinUINotes do složky WinUINotes.Bus/Models.

  3. Aktualizujte obor názvů tak, aby odpovídal novému umístění:

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

Třída Note je jednoduchý datový model. Nevyžaduje oznámení o změně, protože modely ViewModels spravují pozorovatelné vlastnosti a upozorňují uživatelské rozhraní na změny.

Přesunutí modelu AllNotes

AllNotes Přesuňte třídu do projektu WinUINotes.Bus.

  1. AllNotes.cs Přesuňte soubor z projektu WinUINotes do složky WinUINotes.Bus/Models.

  2. Aktualizujte obor názvů tak, aby odpovídal novému umístění:

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

Stejně jako třída NoteAllNotes je jednoduchá třída modelu. Model ViewModel zpracovává pozorovatelné chování a spravuje kolekci poznámek.

Vytvoření modelu AllNotesViewModel

  1. V projektu WinUINotes.Bus vytvořte novou složku s názvem ViewModels.

  2. Přidejte nový soubor třídy s názvem AllNotesViewModel.cs s následujícím obsahem:

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

Komponenta AllNotesViewModel spravuje kolekci poznámek zobrazených v uživatelském rozhraní:

  • [ObservableProperty]: Pole notes automaticky vygeneruje veřejnou Notes vlastnost s oznámením o změně. Když se Notes kolekce změní, uživatelské rozhraní se automaticky aktualizuje.
  • allNotes model: Toto soukromé pole obsahuje instanci AllNotes modelu, která zpracovává skutečné datové operace.
  • [RelayCommand]: Tento atribut generuje LoadCommand vlastnost z LoadAsync() metody, což uživatelské rozhraní umožňuje aktivovat operaci načítání prostřednictvím datové vazby.
  • LoadAsync() metoda: Tato metoda načte poznámky z modelu, vymaže aktuální pozorovatelnou kolekci a naplní ji načtenými poznámkami. Tento model zajišťuje, že kolekce vázané na uživatelské rozhraní zůstane synchronizovaná s podkladovými daty.

Oddělení mezi modelem allNotes (pro datové operace) a pozorovatelnou kolekcí Notes (navázání uživatelského rozhraní) je klíčovým vzorem MVVM, který udržuje rozdělení odpovědností a zobrazení synchronizované s daty ViewModelu.

Další informace najdete v dokumentaci:

Vytvoření modelu NoteViewModel

  1. Do složky ViewModels přidejte nový soubor třídy s názvem 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();
            }
        }
    }
    

Ukazuje NoteViewModel několik klíčových funkcí MVVM Toolkit:

  • [ObservableProperty]: Pole filenametexta date pole automaticky generují veřejné vlastnosti (Filename, Text, Date) s podporou oznámení o změnách.
  • [NotifyCanExecuteChangedFor]: Tento atribut zajišťuje, že když se Filename nebo Text změní, přidružené příkazy znovu vyhodnotí, zda se mohou provést. Když například zadáte text, tlačítko Uložit automaticky povolí nebo zakáže na základě ověřovací logiky.
  • [RelayCommand(CanExecute = nameof(CanSave))]: Tento atribut generuje SaveCommand vlastnost, která je vázána na metodu CanSave()ověřování . Příkaz je povolen pouze tehdy, když obě Text a Filename mají hodnoty.
  • InitializeForExistingNote(): Tato metoda načte data existující poznámky do vlastností ViewModel, které pak aktualizují uživatelské rozhraní prostřednictvím datové vazby.
  • Logika uložení: Metoda Save() aktualizuje základní Note model hodnotami aktuálních vlastností a volá SaveAsync() na modelu. Po uložení upozorní DeleteCommand , že by se měl znovu vyhodnotit (protože soubor teď existuje a je možné ho odstranit).
  • Logika odstranění: Metoda Delete() volá DeleteAsync() model poznámek a vytvoří novou prázdnou poznámku.

Později v tomto kurzu integrujete souborovou službu pro zpracování skutečných operací se soubory a pomocí třídy MVVM Toolkit WeakReferenceMessenger, která informuje ostatní části aplikace o odstranění poznámky, zatímco zůstane volně vázaná.

Další informace najdete v dokumentaci:

Aktualizujte zobrazení tak, aby používala ViewModels

Teď potřebujete aktualizovat stránky XAML tak, aby se svážely s novými modely ViewModels.

Aktualizace zobrazení AllNotesPage

  1. V AllNotesPage.xaml aktualizujte vazbu ItemsView tak, aby používala vlastnost ViewModelu Notes.

    <ItemsView ItemsSource="{x:Bind viewModel.Notes}"
    ...
    
  2. AllNotesPage.xaml.cs Aktualizujte soubor tak, aby vypadal takto:

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

V tomto souboru code-behind konstruktor přímo vytváří instanci AllNotesViewModel. Metoda OnNavigatedTo() volá metodu LoadAsync() v Modelu ViewModel při přechodu na stránku. Tato metoda načte poznámky z úložiště a aktualizuje pozorovatelnou kolekci. Tento vzor zajišťuje, aby se data vždy aktualizovala, když uživatel přejde na stránku všech poznámek.

Později v tomto kurzu refaktorujete tento kód tak, aby používal injektáž závislostí, což umožňuje vložit model ViewModel do konstruktoru stránky namísto přímého vytvoření. Tento přístup zlepšuje testovatelnost a usnadňuje správu životního cyklu Modelu ViewModel.

Aktualizace zobrazení Poznámkové stránky

  1. Ve NotePage.xaml aktualizujte TextBox vazby pro Text a Header tak, aby používaly vlastnosti ViewModelu. StackPanel Aktualizujte tlačítka, aby byla propojena s příkazy místo používání Click událostí:

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

    Také jste nastavili UpdateSourceTrigger na TextBox.Text vazbu, aby se zajistilo, že se změny odesílají do ViewModel, jak uživatel píše. Toto nastavení umožňuje Save tlačítku povolit nebo zakázat v reálném čase na základě vstupu.

  2. V NotePage.xaml.cs aktualizujte kód tak, aby používal 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);
                }
            }
        }
    }
    

    Události Click pro Save a Delete jsou odebrány, protože tlačítka se nyní sváže přímo s příkazy v modelu ViewModel. NoteViewModel je instancován v metodě OnNavigatedTo(). Pokud je předán parametr Note, inicializuje ViewModel s existujícími daty poznámky.

Další informace najdete v dokumentaci: