Verwenden des MVVM (Model-View-ViewModel)-Musters in Hilo (Windows Store-Apps mit C++ und XAML)

Aus: Umfassende Entwicklung einer Windows Store-App mit C++ und XAML: Hilo

Leitfaden-Logo

Vorherige Seite | Nächste Seite

Wir entschieden uns zu einem frühen Zeitpunkt des Projekts, für die Architektur von Hilo das MVVM (Model-View-ViewModel)-Muster zu verwenden. Uns gefiel, dass das MVVM-Muster das Verwalten und Testen der Windows Store-App mit C++ und XAML erleichtert, insbesondere bei zunehmender Größe der App. MVVM ist ein relativ neues Muster für C++-Apps.

Download

Herunterladen des Hilo-Beispiels
Buch herunterladen (PDF)

Anweisungen zu dem heruntergeladenen Code finden Sie unter Erste Schritte mit Hilo.

Sie erfahren Folgendes:

  • Vorteile von MVVM für Windows Store-Apps
  • Empfohlene Verfahren zum Anwenden des MVVM-Musters auf Windows Store-Apps.
  • Wie Sie UI-Elementen Ansichten zuordnen.
  • Wie Sie Ansichtsmodelle ansichtsübergreifend verwenden.
  • Wie Sie Befehle in einem Ansichtsmodell ausführen.

Betrifft

  • Windows-Runtime für Windows 8
  • Visual C++-Komponentenerweiterungen (C++/CX)
  • XAML

Was ist MVVM?

MVVM ist ein Architekturmuster. Es ist spezielle Variante des von Martin Fowler entwickelten Darstellungsmodells. MVVM ist zudem mit dem MVC (Model-View-Controller)-Muster und dem MVP (Model-View-Presenter)-Muster verwandt, die Sie vielleicht bereits kennen.

In einer App, die MVVM verwendet, sind Geschäftslogik, UI und Darstellungsverhalten getrennt.

  • Modelle stellen den Zustand und die Vorgänge von Geschäftsobjekten dar, die von der App geändert werden. Beispielsweise liest und ändert Hilo Bilder, daher ist es sinnvoll, dass Datentypen für Bilddateien und Vorgänge für Bilddateien Teil des Modells von Hilo sind.
  • Ansichten enthalten UI-Elemente, und sie enthalten sämtlichen Code, der die Benutzererfahrung der App implementiert. Eine Ansicht definiert die Struktur, das Layout und die Darstellung des Inhalts, der für den Benutzer auf dem Bildschirm angezeigt wird. Raster, Seiten, Schaltflächen und Textfelder sind Beispiele für Elemente, die von Ansichtsobjekten verwaltet werden.
  • Ansichtsmodelle kapseln den Zustand, die Aktionen und die Vorgänge der App. Ein Ansichtsmodell dient als Entkopplungsebene zwischen dem Modell und der Ansicht. Es stellt die Daten in einem Format bereit, das von der Ansicht genutzt werden kann, und es aktualisiert das Modell, sodass die Ansicht nicht mit dem Modell interagieren muss. Ansichtsmodelle reagieren auf Befehle und lösen Ereignisse aus. Sie fungieren außerdem als Datenquelle für alle Daten, die in Ansichten angezeigt werden. Ansichtsmodelle werden speziell für die Unterstützung einer Ansicht erstellt. Sie können sich ein Ansichtsmodell als App ohne Benutzeroberfläche vorstellen. In Windows Store-Apps können Sie Ansichten deklarativ an die entsprechenden Ansichtsmodelle binden.

Nachfolgend werden die Beziehungen zwischen einer Ansicht, einem Ansichtsmodell und einem Modell beschrieben.

Beziehungen zwischen Ansicht, Ansichtsmodell und Modell

[Oben]

MVVM in Hilo

In Hilo gibt es eine Ansichtsklasse pro Seite der UI. (Eine Seite ist eine Instanz der Windows::UI::Xaml::Controls::Page-Klasse.) Jede Ansicht verfügt über eine entsprechende Ansichtsmodellklasse. Alle Ansichtsmodelle in Hilo verwenden gemeinsam das Domänenmodell der App, das häufig einfach als "Modell" bezeichnet wird. Das Modell besteht aus Klassen, mit denen die Ansichtsmodelle die Funktionen der App implementieren.

Hinweis  In Erstellen von Seiten und Navigieren zwischen Seiten in Hilo finden Sie eine exemplarische Vorgehensweise zum Code für Ansichten und Ansichtsmodelle in Hilo.

Die Visual Studio-Projektmappe "Hilo.sln" enthält Projektmappenordner, die nach den einzelnen MVVM-Ebenen benannt sind.

Visual Studio-Projektmappenordner
  • Der Ordner "Models" enthält CPP-Dateien (C++) und H-Dateien (C++-Header), die das Hilo-Modell bilden.
  • Der Ordner "Views" enthält die UI-Klassen und XAML-Dateien.
  • Der Ordner "ViewModels" enthält CPP-Dateien und H-Dateien für die Ansichtsmodellklassen der App.

Bei Verwendung des MVVM-Musters fungiert ein Ansichtsmodell mithilfe von XAML-Datenbindung als Datenkontext der Seite. Der Datenkontext stellt Eigenschaften bereit, die der Ansicht Daten für die UI-Elemente auf der Seite liefern.

Hinweis  Sie müssen keine Datenbindung verwenden, um Ansichten und Ansichtsmodelle zu verknüpfen. Sie können auch eine CodeBehind-Datei verwenden, die C++-Code enthält, der Seitenklassen zugeordnet ist. Sie erkennen CodeBehind-Dateien am Suffix ".xaml.cpp". In Hilo ist z. B. die Datei "MainHubView.xaml.cpp" die CodeBehind-Datei für die Seite, die von der Datei "MainHubView.xaml" definiert wird. Viele Tools für die visuelle Entwicklung, z. B. Microsoft Expression sind im Hinblick auf die Verwendung von Datenbindungen optimiert.

Ansichtsmodelle werden durch den Aufruf von Instanzmethoden mit dem zugrunde liegenden Modell der App verknüpft. Für diese Aufrufe benötigen Sie keine besondere Bindung. Wenn Sie eine strikte Trennung zwischen dem Modell und den Ansichtsmodellen der App wünschen, können Sie Modellklassen in einer eigenen Bibliothek packen. Hilo verwendet für sein Modell keine eigene Bibliothek. Stattdessen werden einfach die Dateien, die Modellklassen definieren, in einem eigenen Ordner des Visual Studio-Projekts "Hilo" gespeichert.

[Oben]

Warum ist MVVM für Hilo sinnvoll?

Es gibt zwei Hauptimplementierungsstrategien für eine UI: die Verwendung einer CodeBehind-Datei für die Darstellungslogik oder das Trennen der UI-Struktur und der Darstellungslogik mithilfe eines Musters, z. B. des MVVM-Musters. Nach der Analyse der Anforderungen wählten wir für Hilo aus den folgenden Gründen den MVVM-Ansatz:

  • Wir wollten die Darstellungslogik testen. MVVM erleichtert die saubere Trennung der Ansichtslogik von den UI-Steuerelementen, was für die Testautomatisierung wichtig ist.
  • Wir wollten sicherstellen, dass die Ansichtslogik und die Darstellungslogik unabhängig voneinander entwickelt werden können, und Abhängigkeiten zwischen UX-Designer und Entwicklern reduzieren. Dies wird durch die gemeinsame Verwendung von MVVM mit der XAML-Datenbindung ermöglicht.

[Oben]

Weitere Informationen

Online finden Sie weitere Informationen über MVVM. Dies sind einige Beispiele mit verwaltetem Code, die Konzepte gelten jedoch auch für C++:

[Oben]

Varianten des MVVM-Musters

Sie können das MVVM-Muster auf verschiedene Weise anpassen. Betrachten wir einige davon.

Zuordnen von Ansichten zu anderen UI-Elementen als Seiten

In Hilo ist jede Seitenklasse ein MVVM-Ansichtsobjekt, und alle MVVM-Ansichten sind Seiten. Sie müssen jedoch nicht auf die gleiche Weise vorgehen. Beispielsweise kann eine Ansicht eine DataTemplate für ein Objekt in einem ItemsControl. sein.

Gemeinsames Nutzen von Ansichtsmodellen für mehrere Ansichten

Eine Ansicht kann über ein eigenes Ansichtsmodell verfügen oder das Ansichtsmodell einer anderen Ansicht verwenden. Die Entscheidung hängt davon ab, ob Ansichten viel gemeinsame Funktionalität aufweisen. In Hilo ist der Einfachheit halber jede Ansicht einem eindeutigen Ansichtsmodell zugeordnet.

Ausführen von Befehlen in einem Ansichtsmodell

Sie können Datenbindung für Schaltflächen und andere UI-Steuerelemente verwenden, die bewirken, dass die App Vorgänge ausführt. Wenn das Steuerelement eine Befehlsquelle ist, werden die Daten der Command-Eigenschaft des Steuerelements an eine ICommand-Eigenschaft im Ansichtsmodell gebunden. Beim Aufruf des Befehls für das Steuerelement wird der Code im Ansichtsmodell ausgeführt. Es wird empfohlen, bei Nutzung von MVVM Datenbindung für Befehle zu verwenden.

Hier finden Sie ein Beispiel für das Ausführen eines Befehls in Hilo. Die Seite zum Drehen von Bildern enthält ein UI-Element für die Schaltfläche "Datei speichern". Dieser XAML-Code stammt aus der Datei "RotateImageView.xaml".


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


Durch den Ausdruck "Command={Binding SaveCommand}" wird eine Bindung zwischen der Command-Eigenschaft der Schaltfläche und der SaveCommand-Eigenschaft der RotateImageViewModel-Klasse erstellt. Die SaveCommand-Eigenschaft enthält ein Handle für ein ICommand-Objekt. Der folgende Code stammt aus der Datei "RotateImageViewModel.cpp".


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


Die m_saveCommand-Membervariable wird im Konstruktor der RotateImageViewModel-Klasse initialisiert. Hier ist der Code aus der Datei "RotateImageViewModel.cpp".


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


Die DelegateCommand-Klasse von Hilo ist eine Implementierung der ICommand-Schnittstelle. Die Klasse definiert den ExecuteDelegate-Delegattyp. Delegaten ermöglichen Ihnen die Verwendung eines Zeigers auf eine C++-Memberfunktion als aufrufbares Windows-Runtime-Objekt. Hilo ruft den ExecuteDelegate auf, wenn die UI den Befehl auslöst.

Hinweis  Weitere Informationen über die Spracherweiterung für Delegaten in C++/CX finden Sie unter Delegaten (C++/CX).

Da wir Datenbindung verwendeten, bestand das Ändern des Befehls zum Speichern im Zuweisen eines anderen DelegateCommand-Objekts zur m_saveCommand-Membervariablen. Die XAML-Datei der Ansicht muss nicht geändert werden.

In diesem Beispiel stammt die zugrunde liegende Memberfunktion aus der Datei "RotateImageViewModel.cpp".


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

Verwenden eines Ansichtsmodelllocator-Objekts zum Binden von Ansichten an Ansichtsmodelle

In Hilo verfügt jede Ansicht (Seite) über ein entsprechendes Ansichtsmodell.

Wenn Sie MVVM verwenden, müssen die Ansichten der App mit den entsprechenden Ansichtsmodellen verknüpft werden. Dies bedeutet, dass der DataContext-Eigenschaft jeder Ansicht ein Ansichtsmodell zugewiesen sein muss. Für Hilo verwendeten wir eine einzelne ViewModelLocator-Klasse, da wir Code einrichten mussten, der ausgeführt wird, bevor UI-Elemente an das Ansichtsmodell gebunden werden. Die ViewModelLocator-Klasse verfügt über Eigenschaften, die für jede Seite der App ein Ansichtsmodellobjekt abrufen. Eine Beschreibung der Bindung von Ansichten und Ansichtsmodellen in Hilo durch die ViewModelLocator-Klasse finden Sie unter Erstellen von Seiten und Navigieren zwischen Seiten.

Sie müssen keine Ansichtsmodelllocator-Klasse verwenden. Es gibt mehrere Möglichkeiten, eine Ansicht an das entsprechende Ansichtsmodellobjekt zu binden. Wenn Sie keine Ansichtsmodelllocator-Klasse verwenden, können Sie die Erstellung und Zerstörung von Ansichtsmodellinstanzen mit der Lebensdauer des entsprechenden Ansichtsobjekts verknüpfen. Beispielsweise kann bei jedem Laden der Seite eine neue Ansichtsmodellinstanz erstellt werden.

Sie können auch in einer CodeBehind-Datei Ansichten mit Ansichtsmodellen verknüpfen. Der Code in einer CodeBehind-Datei kann eine neue Ansichtsmodellinstanz instanziieren und diese der DataContext-Eigenschaft der Ansicht zuweisen. Sie können das Ansichtsmodell in der Initialize-Methode oder in der OnNavigatedTo-Methode der Seite instanziieren.

[Oben]

Tipps zum Entwerfen von Windows Store-Apps mit MVVM

Hier sind einige Tipps zum Anwenden des MVVM- Musters auf Windows Store-Apps in C++.

Lassen Sie Ansichtsabhängigkeiten außerhalb des Ansichtsmodells

Wenn Sie eine Windows Store-App mit dem MVVM-Muster entwerfen, müssen Sie entscheiden, was sich im Modell, was sich in den Ansichten und was sich in den Ansichtsmodellen befinden soll. Diese Unterteilung kann häufig nach persönlichen Vorlieben erfolgen, es gelten jedoch einige allgemeine Grundsätze. Im Idealfall definieren Sie die Ansicht mit XAML, mit nur begrenztem CodeBehind, der keine Geschäftslogik enthält. Zudem sollte das Ansichtsmodell keine Abhängigkeiten von den Datentypen für UI-Elemente oder Ansichten enthalten. Schließen Sie in die Quelldateien des Ansichtsmodells keine Ansichtsheaderdateien ein.

Zentralisieren Sie Datenkonvertierungen im Ansichtsmodell oder auf einer Konvertertierungsebene

Das Ansichtsmodell stellt Daten aus dem Modell in einem Format bereit, das von der Ansicht problemlos verwendet werden kann. Zu diesem Zweck muss das Ansichtsmodell manchmal Datenkonvertierung ausführen. Es empfiehlt sich, die Datenkonvertierung im Ansichtsmodell erfolgen zu lassen, da dieses Eigenschaften in einem Format bereitstellt, an das die UI gebunden werden kann.

Sie können auch eine eigene Datenkonvertierungsebene zwischen dem Ansichtsmodell und der Ansicht verwenden. Dies kann beispielsweise erfolgen, wenn Datentypen eine besondere Formatierung erfordern, die nicht vom Ansichtsmodell bereitgestellt wird.

Machen Sie Ausführungsmodi im Ansichtsmodell verfügbar

Möglicherweise ist es außerdem Aufgabe des Ansichtsmodells, Änderungen des logischen Zustands zu definieren, die sich auf einen Aspekt der Anzeige in der Ansicht auswirken, z. B. die Angabe, dass ein Vorgang aussteht oder ob ein bestimmter Befehl verfügbar ist. Zum Aktivieren und Deaktivieren von UI-Elementen ist kein CodeBehind erforderlich. Sie können dies auch durch Bindung an eine Ansichtsmodelleigenschaft erreichen.

Hier sehen Sie ein Beispiel.


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


In dem Beispiel wird das IsTapEnabled-Attribut an die HasPhoto-Eigenschaft des Ansichtsmodells gebunden.

Stellen Sie sicher, dass Ansichtsmodelle über das Bindable-Attribut verfügen

Damit das Ansichtsmodell in die Datenbindung an die Ansicht einbezogen ist, müssen Ansichtsmodellklassen über das Windows::UI::Xaml::Data::Bindable -Attribut verfügen, um sicherzustellen, dass der Typ in der generierten XAML-Datei enthalten ist.

Sie müssen außerdem den Header für das Ansichtsmodell direkt oder indirekt in die Headerdatei ".App.xaml.h" einschließen. In Hilo befinden sich alle Ansichtsmodell-Headerdateien in der Datei "ViewModelLocator.h", die in der Datei "App.xaml.h " enthalten ist. Dies stellt sicher, dass zur Kompilierzeit die für XAML erforderlichen Typen ordnungsgemäß generiert werden.

Hinweis  Weitere Informationen über die Spracherweiterung von C++/CX für Attribute finden Sie unter Benutzerdefinierte Attribute (C++/CX).

Hier ist ein Beispiel für das Bindable-Attibut.


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

Stellen Sie sicher, dass Ansichtsmodelle die INotifyPropertyChanged-Schnittstelle implementieren, damit Datenbindung erfolgen kann

Ansichtsmodelle, die Clients über die Änderung eines Eigenschaftswerts benachrichtigen müssen, müssen das PropertyChanged-Ereignis auslösen. Zu diesem Zweck müssen Ansichtsmodellklassen die Windows::UI::Xaml::Data::INotifyPropertyChanged-Schnittstelle implementieren. Die Windows-Runtime registriert einen Handler für dieses Ereignis, wenn die App ausgeführt wird. Visual Studio stellt eine Implementierung der INotifyPropertyChanged-Schnittstelle in der BindableBase-Vorlagenklasse bereit, die Sie als Basisklasse für jede XAML-Datenquelle verwenden können. Dies ist die generierte Headerdatei aus "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);
};	  

Die generierte Implementierung ruft den Handler auf, wenn das Ereignis ausgelöst wird.


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

Hinweis  Ereignisse und Eigenschaften sind in C++/CX Teil der Programmiersprache. Diese enthält das event-Schlüsselwort und das property-Schlüsselwort. Windows-Runtime-Typen deklarieren Ereignisse in ihren öffentlichen Schnittstellen, die von der App abonniert werden können. Der Abonnent führt benutzerdefinierte Aktionen aus, wenn der Herausgeber das Ereignis auslöst. Weitere Informationen über C++/CX-Features, die die Windows-Runtime unterstützen, finden Sie unter Erstellen von Windows Runtime-Komponenten in C++. Weitere Informationen über die in diesem Codebeispiel verwendete C++/CX-Spracherweiterung für Ereignisse finden Sie unter Ereignisse (C++/CX).

Ansichtsmodellklassen können die INotifyPropertyChanged-Implementierung durch Ableitung von der BindableBase-Klasse erben. Dies ist beispielsweise die Deklaration der ViewModelBase-Klasse in Hilo. Der Code stammt aus der Datei "ViewModelBase.h".


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

Immer wenn Ansichtsmodelle die UI über die Änderung einer gebundenen Eigenschaft informieren müssen, rufen sie die OnPropertyChanged-Methode auf, die sie von der BindableBase-Klasse erben. Dies ist beispielsweise eine in der RotateImageViewModel-Klasse definierte Eigenschaftensatzmethode.


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


Hinweis  Benachrichtigungen an XAML über Eigenschaften müssen im UI- Thread erfolgen. Dies bedeutet, dass die OnPropertyChanged-Methode und alle ihre Aufrufer sich ebenfalls im UI-Thread der App befinden müssen. Im Allgemeinen sollte der Grundsatz befolgt werden, alle Methoden und Eigenschaften des Ansichtsmodells im UI-Thread der App aufzurufen.

Lassen Sie Ansichten und Ansichtsmodelle unabhängig

Wenn Sie die hier beschriebenen Grundsätze befolgen, können Sie Ansichtsmodelle ohne jegliche Änderung der Ansicht neu implementieren. Die Bindung von Ansichten an eine bestimmte Eigenschaft in der Datenquelle der Ansicht sollte die primäre Abhängigkeit der Ansicht von ihrem Ansichtsmodell sein. (Wenn Sie eine gebundene Eigenschaft im Ansichtsmodell umbenennen, müssen Sie sie auch im XAML-Datenbindungsausdruck umbenennen.)

Verwenden Sie Verfahren der asynchronen Programmierung, damit die UI reaktionsfähig bleibt

Windows Store-Apps sollten eine dynamische Benutzererfahrung bieten. Aus diesem Grund wird der UI-Thread in Hilo nicht blockiert. In Hilo werden für E/A-Vorgänge und parallele Aufgaben asynchrone Bibliotheksmethoden verwendet, wenn Vorgänge mit erheblichem Rechenaufwand erfolgen. Hilo löst Ereignisse aus, um die Ansicht asynchron über eine Eigenschaftsänderung zu benachrichtigen.

Weitere Informationen finden Sie unter Asynchrone Programmierung für Windows Store-Apps mit C++ und XAML.

Befolgen Sie immer Threadingregeln für Windows-Runtime- Objekte

Objekte, die durch Aufrufe der Windows-Runtime erstellt werden, sind manchmal Singlethreadobjekte. Dies bedeutet, dass Sie die Methoden, Eigenschaften und Ereignishandler aus dem Threadkontext aufrufen müssen, der zum Erstellen des Objekts verwendet wurde. In den meisten Fällen ist der Kontext der UI-Thread der App.

Um Fehler zu vermeiden, wurde im Entwurf von Hilo festgelegt, dass Aufrufe der Ansichtsmodelle im UI-Thread der App erfolgen. (Modellklassen führen zeitaufwendige Vorgänge, z. B. Bildverarbeitung, in Workerthreads aus.)

Weitere Informationen finden Sie unter Programmiermuster für asynchrone UI. Informationen über das in Windows Store-Apps verwendete Threadingmodell finden Sie unter Steuern des Ausführungsthreads.

Eine exemplarische Vorgehensweise zur asynchronen Programmierung in Hilo finden Sie unter Asynchrone Programmierung für Windows Store-Apps mit C++ und XAML.

[Oben]

 

 

Anzeigen:
© 2014 Microsoft