Usare un modello di visualizzazione

Completato

Dopo aver appreso i componenti che costituiscono il modello MVVM, è probabile che il modello e la vista siano facili da definire. Si esaminerà ora come usare il modello di visualizzazione per definirne meglio il ruolo nel modello.

Esporre le proprietà all'interfaccia utente

Come nell'esempio precedente, i modelli di visualizzazione si basano in genere sui modelli per la maggior parte dei dati e la logica di business. È il modello di visualizzazione che formatta, converte e arricchisce i dati in base alle esigenze della visualizzazione corrente.

Formattare usando un modello di visualizzazione

È stato già illustrato un esempio di formattazione relativo ai giorni di ferie. Formattazione delle date, codifiche dei caratteri e serializzazione sono tutti esempi di come il modello di visualizzazione può formattare i dati del modello.

Convertire usando un modello di visualizzazione

Il modello spesso offre informazioni in modi indiretti, ma il modello di visualizzazione può risolvere questo problema. Si supponga, ad esempio, di voler visualizzare su schermo se un dipendente è un supervisore. Il modello Employee tuttavia non offre questa informazione direttamente. L'informazione dovrà essere invece dedotta in base alla presenza di altri dipendenti che riportano al dipendente specifico. Si supponga che il modello includa questa proprietà:

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

Se l'elenco è vuoto, si può dedurre che Employee non è un supervisore. In questo caso, EmployeeViewModel include la proprietà IsSupervisor che fornisce tale logica:

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

Arricchire i dati usando un modello di visualizzazione

In alcuni casi un modello fornisce solo un ID per i dati correlati. Oppure potrebbe essere necessario passare a diverse classi di modello per correlare i dati necessari per una singola schermata. Il modello di visualizzazione è lo strumento ideale anche per eseguire queste attività. Si supponga di voler visualizzare tutti i progetti attualmente gestiti da un dipendente. Questi dati non sono inclusi nella classe di modello Employee. È possibile accedervi esaminando la classe di modello CompanyProjects. EmployeeViewModel, come sempre, espone le proprie operazioni come proprietà pubbliche:

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

Usare proprietà pass-through con un modello di visualizzazione

Spesso un modello di visualizzazione necessita esattamente della proprietà offerta dal modello. Per queste proprietà, il modello di visualizzazione passa semplicemente i dati:

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

Impostare l'ambito per il modello di visualizzazione

È possibile usare un modello di visualizzazione in qualsiasi livello sia presente una visualizzazione. Analogamente alle pagine, anche le visualizzazioni secondarie delle pagine possono avere un modello di visualizzazione. Un motivo comune per i modelli di visualizzazione annidati è quando la pagina visualizza un ListView nella pagina. L'elenco ha un modello di visualizzazione che rappresenta la raccolta, ad esempio EmployeeListViewModel. Ogni elemento dell'elenco è un oggetto EmployeeViewModel.

Diagramma di un oggetto EmployeeListViewModel con diversi oggetti secondari EmployeeViewModel.

È abbastanza comune avere un modello di visualizzazione di primo livello che contiene i dati e lo stato per l'intera applicazione, ma non è associato ad alcuna pagina in particolare. Questo tipo di modello di visualizzazione viene comunemente usato per mantenere l'elemento "attivo". Si consideri l'esempio ListView appena descritto. Quando l'utente seleziona la riga di un dipendente, quest'ultimo rappresenta l'elemento corrente. Se l'utente passa a una pagina dei dettagli o seleziona un pulsante della barra degli strumenti quando la riga è selezionata, l'azione o la visualizzazione deve essere relativa a quel dipendente. Un modo elegante di gestire questo scenario consiste nello stabilire un'associazione dati tra ListView.SelectItem e una proprietà a cui anche la barra degli strumenti o la pagina dei dettagli possano accedere. L'inserimento di questa proprietà in un modello di visualizzazione centrale offre buoni risultati.

Identificare quando riusare i modelli di visualizzazione con le visualizzazioni

Il modo in cui si definisce la relazione tra modello di visualizzazione e modello e quella tra modello di visualizzazione e visualizzazione è dettato più dai requisiti dell'app che dalle regole. La funzione del modello di visualizzazione è quella di fornire alla visualizzazione la struttura e i dati necessari. Ciò deve essere considerato quando si decide l'"ampiezza" dell'ambito di un modello di visualizzazione.

I modelli di visualizzazione spesso riflettono fedelmente la struttura di una classe di modello e hanno una relazione uno-a-uno con la classe. In precedenza è stato illustrato un esempio in cui EmployeeViewModel eseguiva il wrapping e aumentava una singola istanza di Employee. Tuttavia, la relazione non è sempre uno-a-uno. Se il modello di visualizzazione è progettato per fornire le esigenze della visualizzazione, è possibile che venga invece visualizzato un elemento simile a HRDashboardViewModel per offrire una panoramica di un reparto risorse umane, che non ha alcuna relazione esplicita con alcun modello, ma può usare i dati di qualsiasi classe del modello.

In modo analogo, è possibile che i modelli di visualizzazione e le visualizzazioni abbiano spesso una relazione uno-a-uno. Ma anche in questo caso non è necessariamente così. Si consideri di nuovo un elemento ListView che visualizza una riga per ogni dipendente. Quando si seleziona una delle righe, viene visualizzata la pagina dei dettagli di un dipendente.

La pagina di elenco include il relativo modello di visualizzazione con una raccolta. Come suggerito in precedenza, la raccolta può essere una raccolta di oggetti EmployeeViewModel. E quando l'utente seleziona una riga, l'istanza di EmployeeViewModelpuò essere passata a EmployeeDetailPage. E la pagina dei dettagli può usare EmployeeViewModel come BindingContext.

Questo scenario può essere un'ottima opportunità per il riutilizzo del modello di visualizzazione. Ma è sempre opportuno ricordare che i modelli di visualizzazione sono progettati per offrire alla visualizzazione le informazioni necessarie. In alcuni casi può essere utile usare modelli di visualizzazione distinti anche se basati sulla stessa classe di modello. In questo esempio è probabile che le righe ListView necessitino di una quantità di informazioni molto inferiore rispetto alla pagina dei dettagli completa. Se il recupero dei dati necessari per la pagina dei dettagli comporta un sovraccarico elevato, può essere opportuno avere entrambi i modelli EmployeeListRowViewModel e EmployeeDetailViewModel che servono queste rispettive visualizzazioni.

Modello a oggetti Viewmodel

L'uso di una classe base che implementa INotifyPropertyChanged significa che non è necessario riapplicare l'interfaccia in ogni modello di visualizzazione. Si consideri l'applicazione HR come descritto nella parte precedente di questo modulo di training. La classe EmployeeViewModel ha implementato l'interfaccia INotifyPropertyChanged e ha fornito un metodo helper denominato OnPropertyChanged per generare l'evento PropertyChanged. Altri modelli di visualizzazione nel progetto, ad esempio quelli che descrivono le risorse assegnate a un dipendente, richiedono anche a INotifyPropertyChanged di integrarsi completamente con una visualizzazione.

La libreria MVVM Toolkit, parte di .NET Community Toolkit, è una raccolta di tipi standard, autonomi e leggeri che forniscono un'implementazione iniziale per la creazione di app moderne usando il modello MVVM.

Invece di scrivere una classe di base viewmodel personalizzata, si eredita dalla classe ObservableObject del toolkit, che fornisce tutto il necessario per una classe di base viewmodel. L'oggetto EmployeeViewModel può essere semplificato da:

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

Nel codice seguente:

using Microsoft.Toolkit.Mvvm.ComponentModel;

public class EmployeeViewModel : ObservableObject
{
    private string _name;

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

MVVM Toolkit viene distribuito tramite il pacchetto NuGet CommunityToolkit.Mvvm.

Verificare le conoscenze

1.

Quando si usa il modello MVVM con .NET MAUI, il modello, la visualizzazione e il modello di visualizzazione non sono completamente separati l'uno dall'altro. Quale scelta descrive una dipendenza comune tra le parti MVVM?

2.

Quale di questi elementi è più probabile che sia fortemente associato alla piattaforma e per il quale la creazione di unit test risulta difficile: il modello, la visualizzazione o il modello di visualizzazione?