Korzystanie z modelu widoków

Ukończone

Po zapoznaniu się ze składnikami tworzącym wzorzec MVVM prawdopodobnie okazało się, że model i widok były łatwe do zdefiniowania. Przyjrzyjmy się, jak używać modelu viewmodel, aby lepiej zdefiniować swoją rolę we wzorcu.

Uwidacznianie właściwości interfejsu użytkownika

Podobnie jak w poprzednim przykładzie, modele widoków zwykle opierają się na modelach dla większości danych i dowolnej logiki biznesowej. Jest to jednak model widoków, który formatuje, konwertuje i wzbogaca dane w dowolny sposób, jaki wymaga bieżący widok.

Formatowanie przy użyciu modelu viewmodel

Przedstawiono już przykład formatowania z czasem urlopu. Formatowanie dat, kodowanie znaków i serializacja to przykłady formatowania danych z modelu przez model viewmodel.

Konwertowanie przy użyciu modelu viewmodel

Często model dostarcza informacje w sposób pośredni. Ale model widoków może to naprawić. Załóżmy na przykład, że chcesz pokazać na ekranie, czy pracownik jest przełożonym. Ale nasz Employee model nie mówi nam, że bezpośrednio. Zamiast tego musisz wywnioskować ten fakt na podstawie tego, czy osoba ma do nich inne raporty. Załóżmy, że model ma tę właściwość:

public IList<Employee> DirectReports
{
    get
    {
        ...
    }
}

Jeśli lista jest pusta, możesz wywnioskować, że nie jest to Employee nadzorca. W tym przypadku EmployeeViewModel zawiera właściwość IsSupervisor , która zapewnia tę logikę:

public bool IsSupervisor => _model.DirectReports.Any();

Wzbogacanie przy użyciu modelu viewmodel

Czasami model może podać tylko identyfikator powiązanych danych. Może też być konieczne przejście do kilku klas modelu, aby skorelować dane wymagane dla jednego ekranu. Model widoków zapewnia idealne miejsce do wykonywania tych zadań. Załóżmy, że chcesz wyświetlić wszystkie projekty, którymi obecnie zarządza pracownik. Te dane nie są częścią Employee klasy modelu. Dostęp do niego można uzyskać, przeglądając klasę CompanyProjects modelu. Nasz EmployeeViewModel, jak zawsze, ujawnia swoją pracę jako własność publiczną:

public IEnumerable<string> ActiveProjects => CompanyProjects.All
    .Where(p => p.Owner == _model.Id && p.IsActive)
    .Select(p => p.Name);

Używanie właściwości przekazywania z modelem viewmodel

Często model widoków potrzebuje dokładnie właściwości, którą zapewnia model. W przypadku tych właściwości model viewmodel po prostu przekazuje dane za pośrednictwem:

public string Name
{
    get => _model.Name;
    set => _model.Name = value;
}

Ustawianie zakresu modelu widoków

Możesz użyć modelu widoków na dowolnym poziomie, na którym znajduje się widok. Strona zwykle ma model widoku, ale może więc podglądać strony. Jedną z typowych przyczyn zagnieżdżonych modelu widoków jest wyświetlenie strony ListView na stronie. Lista zawiera model widoków, który reprezentuje kolekcję, taką jak EmployeeListViewModel. Każdy element na liście jest elementem EmployeeViewModel.

Diagram modelu EmployeeListViewModel z kilkoma podobiektami EmployeeViewModel.

Często zdarza się również, że model widoku najwyższego poziomu przechowuje dane i stan dla całej aplikacji, ale nie jest skojarzony z żadną konkretną stroną. Taki model widoku jest często używany do obsługi elementu "aktywny". ListView Rozważmy właśnie opisany przykład. Gdy użytkownik wybierze wiersz pracownika, ten pracownik reprezentuje bieżący element. Jeśli użytkownik przejdzie do strony szczegółów lub wybierze przycisk paska narzędzi po wybraniu tego wiersza, akcja lub ekran powinien być wyświetlany dla tego pracownika. Elegancki sposób obsługi tego scenariusza polega na tym ListView.SelectItem , że dane są powiązane z właściwością, do których może również uzyskiwać dostęp pasek narzędzi lub strona szczegółów. Umieszczenie tej właściwości na centralnym modelu widokowym działa dobrze.

Identyfikowanie, kiedy należy ponownie używać modeli widoków z widokami

Sposób definiowania relacji między modelem widoku a modelem i między modelem widoków i widokiem jest dyktowany bardziej przez wymagania aplikacji niż przez reguły. Celem modelu widoku jest zapewnienie widoku struktury i danych, których potrzebuje. Powinno to kierować decyzjami na temat "jak duży", aby określić zakres modelu widoków.

Modele widoków często ściśle odzwierciedlają strukturę klasy modelu i mają relację jeden do jednego z tą klasą. Pokazano przykład wcześniej z EmployeeViewModel tym opakowanym i rozszerzonym pojedynczym Employee wystąpieniem. Ale nie zawsze jest to relacja jeden do jednego. Jeśli model widoku został zaprojektowany tak, aby zapewnić, czego potrzebuje widok, możesz zamiast tego przedstawić coś HRDashboardViewModel podobnego do omówienia działu kadr, który nie ma jawnej relacji z żadnym modelem, ale może używać danych z dowolnej klasy modelu.

Podobnie może się okazać, że modele widoków i widoki często mają relację jeden do jednego. Ale to również niekoniecznie jest tak. Pomyślmy ponownie o tym ListView , że pokazuje wiersz dla każdego pracownika. Po wybraniu jednego z wierszy przejdź do strony szczegółów pracownika.

Na stronie listy znajduje się model widoku z kolekcją. Jak wspomniano wcześniej, ta kolekcja może być kolekcją EmployeeViewModel obiektów. A gdy użytkownik wybierze wiersz, EmployeeViewModel wystąpienie może zostać przekazane do obiektu EmployeeDetailPage. Strona szczegółów może używać jej EmployeeViewModel jako .BindingContext

Ten scenariusz może być doskonałą okazją do ponownego użycia modelu viewmodel. Należy jednak pamiętać, że modele widoków mają na celu zapewnienie potrzeb widoku. W niektórych przypadkach możesz chcieć użyć oddzielnych modeli widoków, nawet jeśli są one oparte na tej samej klasie modelu. W tym przykładzie ListView wiersze prawdopodobnie będą potrzebować znacznie mniej informacji niż pełna strona szczegółów. W przypadku pobierania danych strona szczegółów wymaga dużej ilości narzutu, warto mieć oba EmployeeListRowViewModel modele, EmployeeDetailViewModel które obsłużą te widoki.

Model obiektu Viewmodel

Użycie klasy bazowej, która implementuje INotifyPropertyChanged , oznacza, że nie trzeba ponownie wdrażać interfejsu na każdym modelu widoków. Rozważmy aplikację KADR zgodnie z opisem w poprzedniej części tego modułu szkoleniowego. Klasa EmployeeViewModel zaimplementowała INotifyPropertyChanged interfejs i dostarczyła metodę pomocnika o nazwie OnPropertyChanged w celu wywołania PropertyChanged zdarzenia. Inne modele widoków w projekcie, takie jak te, które opisują zasoby przypisane do pracownika, również wymagają INotifyPropertyChanged pełnej integracji z widokiem.

Biblioteka zestawu narzędzi MVVM, część zestawu narzędzi .NET Community Toolkit , jest kolekcją standardowych, samodzielnych, lekkich typów, które zapewniają początkową implementację do tworzenia nowoczesnych aplikacji przy użyciu wzorca MVVM.

Zamiast pisać własną klasę bazową modelu viewmodel, dziedziczysz z klasy zestawu narzędzi ObservableObject , która zapewnia wszystko, czego potrzebujesz do klasy bazowej modelu viewmodel. Element EmployeeViewModel można uprościć z następujących funkcji:

using System.ComponentModel;

public class EmployeeViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    private Employee _model;

    public string Name
    {
        get {...}
        set
        {
            _model.Name = value;
            OnPropertyChanged(nameof(Name))
        }
    }

    protected void OnPropertyChanged(string propertyName) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Do następującego kodu:

using Microsoft.Toolkit.Mvvm.ComponentModel;

public class EmployeeViewModel : ObservableObject
{
    private string _name;

    public string Name
    {
        get => _name;
        set => SetProperty(ref _name, value);
    }
}

Kod można jeszcze bardziej uprościć przy użyciu generatorów źródłowych dostarczonych przez zestaw narzędzi MVVM Toolkit. Tworząc klasę partial i dodając element [ObservableProperty] do zmiennej private , właściwość Name publiczna jest generowana przy użyciu odpowiednich powiadomień o zmianie właściwości.

using Microsoft.Toolkit.Mvvm.ComponentModel;

public partial class EmployeeViewModel : ObservableObject
{
    [ObservableProperty]
    private string _name;
}

Zestaw narzędzi MVVM Jest dystrybuowany za pośrednictwem CommunityToolkit.Mvvm pakietu NuGet.

Sprawdź swoją wiedzę

1.

W przypadku korzystania ze wzorca MVVM za pomocą interfejsu MAUI platformy .NET model, widok i model widoku nie są całkowicie oddzielone od siebie. Który wybór opisuje jedną wspólną zależność między elementami MVVM?

2.

Co jest przeważnie ściśle powiązane z platformą i trudne do utworzenia testów jednostkowych dla: modelu, widoku lub modelu widoku?