Uso del modello MVVM (Model View ViewModel) in Hilo (app di Windows Store scritte in C++ e XAML)

Applies to Windows only

Da: Sviluppo di un'app di Windows Store end-to-end con C++ e XAML: Hilo

Logo Patterns & Practices

Pagina precedente | Pagina successiva

Nelle fasi iniziali del progetto abbiamo deciso di adottare il modello di progettazione MVVM (Model View ViewModel) per l'architettura di Hilo. Siamo stati incentivati dal fatto che il modello di progettazione MVVM semplifica le attività di gestione e di test dell'app di Windows Store scritta in C++ e XAML, in particolare man mano che si sviluppa. Il modello di progettazione MVVM è un modello relativamente nuovo per le app scritte in C++.

Download

Download dell'esempio Hilo
Download guida (PDF)

Dopo aver scaricato il codice, per le istruzioni vedi Introduzione a Hilo.

In questo articolo verranno illustrati gli argomenti seguenti

  • Vantaggi del modello di progettazione MVVM per le app di Windows Store.
  • Tecniche consigliate per l'applicazione del modello MVVM alle app di Windows Store.
  • Come mappare le visualizzazioni agli elementi dell'interfaccia utente.
  • Come condividere i modelli visualizzazione tra visualizzazioni.
  • Come eseguire i comandi in un modello visualizzazione.

Si applica a

  • Windows Runtime per Windows 8
  • Estensioni del componente di Visual C++ (C++/CX)
  • XAML

Che cos'è il modello MVVM?

MVVM è un modello di architettura. È una specializzazione del modello di presentazione introdotto da Martin Fowler. È inoltre collegato al modello di progettazione MVC (Model View Controller) (MVC) e al modello di progettazione MVP (Model View Presenter) che forse già conosci.

Un'app che usa il modello di progettazione MVVM separa la logica di business, l'interfaccia utente e il comportamento relativo alla presentazione.

  • I modelli rappresentano lo stato e le operazioni degli oggetti business manipolati dall'app. Ad esempio, Hilo legge e modifica i file di immagine, pertanto è consigliabile che i tipi di dati per i file di immagine e le operazioni sui file di immagine facciano parte del modello di Hilo.
  • Le visualizzazioni contengono elementi dell'interfaccia utente e includono il codice che implementa l'esperienza utente dell'app. Una visualizzazione definisce struttura, layout e aspetto di ciò che l'utente vede sullo schermo. Griglie, pagine, pulsanti e caselle di testo sono esempi degli elementi gestiti dagli oggetti visualizzazione.
  • I modelli visualizzazione incapsulano lo stato, le azioni e le operazioni dell'app. Un modello di visualizzazione funge da livello di disaccoppiamento tra il modello e la visualizzazione. Fornisce i dati in un formato utilizzabile dalla visualizzazione e aggiorna il modello in modo che la visualizzazione non debba interagire con il modello. I modelli visualizzazione rispondono ai comandi e attivano eventi. Fungono inoltre da origini dati per qualsiasi dato mostrato dalle visualizzazioni. I modelli visualizzazione sono progettati appositamente per il supporto di una visualizzazione. Puoi pensare a un modello di visualizzazione come all'app senza l'interfaccia utente. Nelle app di Windows Store puoi associare in modo dichiarativo le visualizzazioni ai relativi modelli visualizzazione corrispondenti.

Ecco le relazioni esistenti tra una visualizzazione, un modello di visualizzazione e un modello.

Relazioni tra visualizzazione, modello visualizzazione e modello

[Torna all'inizio]

MVVM in Hilo

In Hilo è presente una classe di visualizzazione per ogni pagina dell'interfaccia utente. (Una pagina è un'istanza della classe Windows::UI::Xaml::Controls::Page). Ogni visualizzazione dispone di una classe del modello visualizzazione corrispondente. In Hilo tutti i modelli visualizzazione condividono il modello di dominio dell'app, che spesso viene semplicemente chiamato "modello". Il modello è composto da classi che i modelli visualizzazione usano per implementare le funzionalità dell'app.

Nota  Per passare direttamente a una simulazione di codice per la visualizzazione e il modello di visualizzazione di Hilo, vedi Creazione ed esplorazione di pagine in Hilo.

Nella soluzione Hilo.sln di Visual Studio sono presenti cartelle soluzione denominate in base a ogni livello MVVM.

Cartelle soluzione di Visual Studio
  • La cartella Models contiene i file con estensione cpp (C++) e h (intestazione C++) che costituiscono il modello Hilo.
  • La cartella Views contiene le classi dell'interfaccia utente e i file XAML.
  • La cartella ViewModels contiene i file con estensione cpp e h per le classi del modello visualizzazione dell'app.

Con il modello di progettazione MVVM, l'associazione dati XAML consente a un modello visualizzazione di agire come contesto dati di una pagina. Un contesto dati fornisce le proprietà che a loro volta forniscono alla visualizzazione i dati per gli elementi dell'interfaccia utente nella pagina.

Nota  Non devi usare l'associazione dati per connettere le visualizzazioni e i modelli visualizzazione. Puoi anche usare un file code-behind contenente codice C++ associato alle classi delle pagine. Puoi riconoscere i file code-behind dal suffisso .xaml.cpp. In Hilo, ad esempio, il file MainHubView.xaml.cpp è il file code-behind per la pagina definita dal file MainHubView.xaml. Molti strumenti di progettazione visiva, ad esempio Microsoft Expression sono ottimizzati per l'uso con l'associazione dati.

I modelli visualizzazione si connettono al modello sottostante dell'app chiamando i metodi di istanza. Per effettuare queste chiamate non sono necessarie associazioni particolari. Se desideri una separazione netta tra il modello e i modelli visualizzazione dell'app, puoi creare un pacchetto con le classi del modello e includerlo in una libreria separata. Hilo non usa una libreria separata per il relativo modello. Mantiene tuttavia i file che definiscono le classi del modello in una cartella separata nel progetto Hilo in Visual Studio.

[Torna all'inizio]

Perché usare il modello di progettazione MVVM per Hilo?

Esistono due approcci principali per l'implementazione dell'interfaccia utente: l'uso di un file code-behind per la logica di presentazione oppure la separazione della struttura dell'interfaccia utente e della logica di presentazione tramite un modello, ad esempio il modello di progettazione MVVM. Dopo avere esaminato le nostre esigenze, per Hilo abbiamo scelto l'approccio MVVM per i motivi seguenti:

  • Volevamo testare la logica di presentazione. MVVM semplifica la separazione tra la logica di visualizzazione e i controlli dell'interfaccia utente e risulta quindi importante per l'automazione dei test.
  • Volevamo essere sicuri che la logica di visualizzazione e di presentazione potessero evolversi in modo indipendente e ridurre le dipendenze tra i progettisti dell'interfaccia utente e gli sviluppatori. Il modello di progettazione MVVM usato con l'associazione dati del codice XAML consente di ottenere questo risultato.

[Torna all'inizio]

Per altre info

Puoi trovare altre info sul modello MVVM online. Di seguito sono riportati alcuni esempi nel codice gestito, ma i concetti si applicano anche a C++:

[Torna all'inizio]

Variazioni del modello di progettazione MVVM

Puoi personalizzare il modello MVVM in diversi modi. Esaminiamone qualcuno.

Mapping delle visualizzazioni a elementi dell'interfaccia utente diversi dalle pagine

In Hilo ogni classe della pagina è un oggetto visualizzazione MVVM e tutte le visualizzazioni MVVM sono pagine. Non sei obbligato a fare lo stesso. Ad esempio, una visualizzazione può essere un elemento DataTemplate per un oggetto in un elemento ItemsControl.

Condivisione dei modelli visualizzazione tra più visualizzazioni

Una visualizzazione può avere un modello visualizzazione personalizzato oppure condividere il modello visualizzazione di un'altra visualizzazione. La scelta di una o dell'altra opzione dipende dal numero di funzionalità condivise dalle visualizzazioni. In Hilo ogni visualizzazione è associata a un modello visualizzazione univoco, per maggiore semplicità.

Esecuzione dei comandi in un modello visualizzazione

Puoi usare l'associazione dati per i pulsanti e altri controlli dell'interfaccia utente che specificano all'app di eseguire operazioni. Se il controllo è un'origine comando, la proprietà Command del controllo è associata a una proprietà ICommand sul modello visualizzazione. Quando il comando del controllo viene richiamato, il codice nel modello visualizzazione viene eseguito. Quando usi il modello MVVM, per i comandi ti consigliamo di usare l'associazione dati.

Di seguito è riportato un esempio relativo all'esecuzione di un comando da Hilo. La pagina relativa alla rotazione dell'immagine contiene un elemento dell'interfaccia utente per il pulsante Salva file. Il codice XAML proviene dal file RotateImageView.xaml.


<Button x:Name="SaveButton"
        x:Uid="AcceptAppBarButton"
        Command="{Binding SaveCommand}" 
        Style="{StaticResource AcceptAppBarButtonStyle}"
        Tag="Save" />


L'espressione "Command={Binding SaveCommand}" crea un'associazione tra la proprietà Command del pulsante e la proprietà SaveCommand della classe RotateImageViewModel. La proprietà SaveCommand contiene un handle a un oggetto ICommand. Il codice seguente proviene dal file RotateImageViewModel.cpp.


ICommand^ RotateImageViewModel::SaveCommand::get()
{
    return m_saveCommand;
}


La variabile membro m_saveCommand è inizializzata nel costruttore della classe RotateImageViewModel. Ecco il codice proveniente dal file RotateImageViewModel.cpp.


m_saveCommand = ref new DelegateCommand(ref new ExecuteDelegate(this, &RotateImageViewModel::SaveImage), nullptr);


La classe DelegateCommand di Hilo è un'implementazione dell'interfaccia ICommand. La classe definisce il tipo di delegato ExecuteDelegate. I delegati ti permettono di usare un puntatore a una funzione membro C++ come un oggetto Windows Runtime che può essere chiamato. Hilo richiama ExecuteDelegate quando l'interfaccia utente attiva il comando.

Nota  Per altre info sull'estensione del linguaggio per i delegati in C++/CX, vedi Delegati (C++/CX).

Poiché abbiamo usato l'associazione dati, per modificare l'azione del comando di salvataggio è sufficiente assegnare un oggetto DelegateCommand diverso alla variabile membro m_saveCommand. Non è necessario modificare il file XAML della visualizzazione.

In questo esempio la funzione membro sottostante proviene dal file RotateImageViewModel.cpp.


void RotateImageViewModel::SaveImage(Object^ parameter)
{
   // Asynchronously save image file
}	  

Uso di un oggetto localizzatore modello visualizzazione per associare le visualizzazioni ai modelli visualizzazione

In Hilo ogni visualizzazione (pagina) dispone di un modello visualizzazione corrispondente.

Se usi il modello di progettazione MVVM, l'app deve connettere le visualizzazioni ai relativi modelli visualizzazione. Ciò significa che ogni visualizzazione deve avere un modello visualizzazione assegnato alla relativa proprietà DataContext. Per Hilo, abbiamo usato una classe ViewModelLocator singola in quanto dovevamo impostare il codice da eseguire prima di associare gli elementi dell'interfaccia utente al modello visualizzazione. La classe ViewModelLocator dispone di proprietà che recuperano un oggetto modello visualizzazione per ogni pagina dell'app. Vedere Creazione ed esplorazione tra pagine per una descrizione del modo in cui la classe ViewModelLocator associa le visualizzazioni e i modelli visualizzazione in Hilo.

Non è necessario usare una classe ViewModelLocator. Infatti, esistono numerosi modi per associare una visualizzazione al relativo oggetto modello visualizzazione. Se non usi una classe ViewModelLocator, puoi associare la creazione e la distruzione delle istanze del modello visualizzazione alla durata dell'oggetto visualizzazione corrispondente. Puoi ad esempio creare una nuova istanza del modello visualizzazione ogni volta che viene caricata la pagina.

Puoi inoltre associare le visualizzazioni ai modelli visualizzazione in un file code-behind. Il codice in un file code-behind può creare una nuova istanza del modello visualizzazione e assegnarla alla proprietà DataContext della visualizzazione. Puoi creare istanze del modello visualizzazione nel metodo Initialize della pagina o nel relativo metodo OnNavigatedTo.

[Torna all'inizio]

Suggerimenti per la progettazione di app di Windows Store con MVVM

Di seguito sono riportati alcuni suggerimenti per l'applicazione del modello di progettazione MVVM alle app di Windows Store in C++.

Mantenere le dipendenze della visualizzazione al di fuori del modello visualizzazione

Quando progetti un'app di Windows Store con il modello di progettazione MVVM, devi decidere quali sono gli elementi da posizionare nel modello, nelle visualizzazioni e nei modelli visualizzazione. Questa divisione è spesso una questione di gusti, ma valgono comunque alcuni principi generali. Ti consigliamo di definire la visualizzazione con l'XAML e con una quantità minima di code-behind non contenente logica di business. Ti consigliamo inoltre di mantenere il modello visualizzazione libero da dipendenze sui tipi di dati per gli elementi dell'interfaccia utente o le viste. Non includere file di intestazione della visualizzazione nei file di origine del modello visualizzazione.

Centralizzare le conversioni di dati nel modello visualizzazione o in un livello di conversione

Il modello visualizzazione fornisce i dati del modello in un formato che può essere facilmente usato dalla visualizzazione. A questo scopo, talvolta il modello visualizzazione deve eseguire una conversione di dati. Posizionare la conversione di dati nel modello visualizzazione è un'ottima soluzione in quanto fornisce le proprietà in un formato al quale l'interfaccia utente può associarsi.

È anche possibile precedere un livello di conversione dati separato, posizionato tra il modello visualizzazione e la visualizzazione. Ciò può essere utile, ad esempio, quando i tipi di dati necessitano di una formattazione speciale non fornita dal modello visualizzazione.

Esporre le modalità operative nel modello visualizzazione

Il modello visualizzazione può anche essere responsabile della definizione delle modifiche dello stato logico che interessano alcuni aspetti della visualizzazione, ad esempio l'indicazione di operazioni in attesa o della disponibilità di un comando specifico. Non hai bisogno di code-behind per abilitare e disabilitare gli elementi dell'interfaccia utente, puoi ottenere questo risultato con l'associazione a una proprietà del modello visualizzazione.

Ecco un esempio.


<Grid Background="{Binding HasPhotos, Converter={StaticResource BrushConverter}}"
      Height="150"
      IsTapEnabled="{Binding HasPhotos}"
      PointerEntered="OnZoomedOutGridPointerEntered"
      Margin="0"
      Width="150">


In questo esempio l'attributo IsTapEnabled è associato alla proprietà HasPhoto del modello visualizzazione.

Verificare che i modelli visualizzazione dispongano dell'attributo Bindable

Per associare un modello visualizzazione alla vista, le classi del modello visualizzazione devono avere l'attributo Windows::UI::Xaml::Data::Bindable per garantire che il tipo sia incluso nel file generato del codice XAML.

Devi inoltre includere l'intestazione per il modello visualizzazione in un file di intestazione App.xaml.h, in modo diretto o indiretto. In Hilo tutti i file di intestazione dei modelli visualizzazione sono inclusi nel file ViewModelLocator.h, a sua volta incluso nel file App.xaml.h. Questo garantisce che tutti i tipi necessari per funzionare con l'XAML vengano generati correttamente in fase di compilazione.

Nota  Per altre info sull'estensione del linguaggio per gli attributi di C++/CX, vedi Attributi definiti dall'utente (C++/CX).

Segue un esempio dell'attributo Bindable.


[Windows::UI::Xaml::Data::Bindable] 
[Windows::Foundation::Metadata::WebHostHiddenAttribute]
public ref class MainHubViewModel sealed : public ViewModelBase 
{ 
  // ...  
} 	  

Verificare che i modelli visualizzazione implementino l'interfaccia INotifyProperyChanged per il corretto funzionamento dell'associazione dati

I modelli visualizzazione che devono notificare ai client che un valore di proprietà è cambiato devono generare l'evento PropertyChanged. A questo scopo, le classi del modello visualizzazione devono implementare l'interfaccia Windows::UI::Xaml::Data::INotifyPropertyChanged. Windows Runtime registra un gestore per questo evento durante l'esecuzione dell'app. Visual Studio fornisce un'implementazione dell'interfaccia INotifyPropertyChanged nella classe del modello BindableBase che puoi usare come classe di base per qualsiasi origine dati XAML. Ecco il file di intestazione generato, dal file BindableBase.h:



[Windows::Foundation::Metadata::WebHostHidden]
public ref class BindableBase : Windows::UI::Xaml::DependencyObject, Windows::UI::Xaml::Data::INotifyPropertyChanged, Windows::UI::Xaml::Data::ICustomPropertyProvider
{
  public:
    virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;

   // ...

  protected:
    virtual void OnPropertyChanged(Platform::String^ propertyName);
};	  

L'implementazione generata richiama il gestore quando viene generato l'evento.


void BindableBase::OnPropertyChanged(String^ propertyName)
{
    PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));
}	  

Nota  In C++/CX sono disponibili eventi e proprietà come parte del linguaggio di programmazione. Sono inclusi l'evento e le parole chiave della proprietà. I tipi di Windows Runtime dichiarano gli eventi nelle relative interfacce pubbliche che l'app può sottoscrivere. Il sottoscrittore esegue azioni personalizzate quando l'entità di pubblicazione genera l'evento. Per altre info sulle funzionalità di C++/CX che supportano Windows Runtime, vedi Creazione di componenti Windows Runtime in C++. Per altre info sull'estensione del linguaggio per gli eventi di C++/CX usata in questo esempio di codice, vedi Eventi (C++/CX).

Le classi del modello visualizzazione possono ereditare l'implementazione INotifyPropertyChanged mediante la derivazione dalla classe BindableBase. Ecco, ad esempio, la dichiarazione della classe ViewModelBase in Hilo. Il codice proviene dal file ViewModelBase.h.


public ref class ViewModelBase : public Common::BindableBase
{
  // ...
}	  

Ogni volta che i modelli visualizzazione devono comunicare all'interfaccia utente che una proprietà associata è stata modificata, chiamano il metodo OnPropertyChanged ereditato dalla classe BindableBase. Ecco, ad esempio un metodo dell'insieme di proprietà definito nella classe RotateImageViewModel.


void RotateImageViewModel::RotationAngle::set(float64 value)
{
    m_rotationAngle = value;

    // Derive margin so that rotated image is always fully shown on screen.
    Thickness margin(0.0);
    switch (safe_cast<unsigned int>(m_rotationAngle))
    {
    case 90:
    case 270:
        margin.Top = 110.0;
        margin.Bottom = 110.0;
        break;
    }
    m_imageMargin = margin;
    OnPropertyChanged("ImageMargin");
    OnPropertyChanged("RotationAngle");
}


Nota  Le notifiche delle proprietà all'XAML devono verificarsi nel thread dell'interfaccia utente. Ciò significa che il metodo OnPropertyChanged e tutti i relativi chiamanti devono anch'essi verificarsi nel thread dell'interfaccia utente. In generale, una convenzione utile prevede che tutti i metodi e le proprietà del modello visualizzazione debbano essere chiamati nel thread dell'interfaccia utente dell'app.

Mantenere le visualizzazioni e i modelli visualizzazione indipendenti

Se segui i principi indicati in questo articolo, sarai in grado di implementare di nuovo un modello visualizzazione senza apportare alcuna modifica alla visualizzazione. L'associazione delle visualizzazioni a una particolare proprietà nella relativa origine dati deve essere la dipendenza principale di una visualizzazione sul relativo modello visualizzazione corrispondente. (Se rinomini una proprietà associata nel modello visualizzazione devi rinominarla anche nell'espressione di associazione dati XAML).

Usare tecniche di programmazione asincrone per garantire la velocità di risposta dell'interfaccia utente

Le app di Windows Store sono caratterizzate da un'esperienza utente veloce e fluida. Per questo motivo Hilo mantiene il thread dell'interfaccia utente sbloccato. Hilo usa metodi di libreria asincroni per le operazioni di I/O e le attività parallele quando le operazioni eseguono una quantità di calcoli significativa. Hilo genera eventi per notificare alla vista la modifica di una proprietà in modo asincrono.

Per altre info, vedi Programmazione asincrona per le app di Windows Store in C++ e XAML.

Osservare sempre le regole di threading per gli oggetti Windows Runtime

Gli oggetti creati dalle chiamate in Windows Runtime sono talvolta a thread singolo. Ciò significa che devi richiamare i metodi, le proprietà e i gestori di eventi dal contesto del thread usato per creare l'oggetto. Nella maggior parte dei casi, il contesto è il thread dell'interfaccia utente dell'app.

Per evitare errori, Hilo è stato progettato in modo che le chiamate nei relativi modelli visualizzazione si verifichino nel thread dell'interfaccia utente dell'app. (Le classi dei modelli eseguono operazioni che richiedono molto tempo, ad esempio l'elaborazione delle immagini dei thread del worker).

Per altre info, vedere Modelli di programmazione per l'interfaccia utente asincrona. Per informazioni sul modello di threading usato dalle app di Windows Store, vedi Controllo del thread di esecuzione.

Per una simulazione sull'uso della programmazione asincrona di Hilo, vedi Programmazione asincrona per le app di Windows Store in C++ e XAML.

[Torna all'inizio]

 

 

Mostra:
© 2014 Microsoft