Das Model ist ein Bestandteil des Patterns MVVM. Dieser Beitrag gibt eine Einführung in dieses Thema und zeigt ebenfalls eine praxisnahe Verwendung anhand von Beispielen auf.

Kürzlich habe ich eine Anfrage erhalten, was die einzelnen Objekte, welche durch das Pattern MVVM beschrieben werden, tatsächlich beinhalten. Darauf möchte ich kurz mit meiner Interpretation dieses Patterns eingehen und zur Diskussion stellen.

Die weiteren Bestandteile des MVVM-Patterns:
Die View
Das ViewModel

Definition Model

Vielfach wird das Model als reine Abbildung der Datenquelle gesehen. Dies ist für MVVM nicht immer richtig. Vielmehr stellt das Model eine Abbildung der Daten dar, welche für die visuelle Anwendung benötigt werden. Das Model enthält also Daten, die dem Benutzer zur Verfügung gestellt und von ihm manipuliert werden.

Dies bedeutet aber auch, dass das Model folgende Funktionalitäten bereit stellt:

  • Validierung
  • Benachrichtigung bei Eigenschafts-Änderungen
  • Verarbeitung von Business-Rules

Was tatsächlich im Model zum Einsatz kommt, wird durch die Anforderungen an das Model bestimmt. Im Grunde werden für das Model die Interfaces INotifyPropertyChanged (für die Benachrichtigung von Eigenschafts-Änderungen) und IDataErrorInfo (Validierung) implementiert. Werden die Daten von einem OR-Mapper oder einem Service zurück geliefert, kann aus meiner Sicht die Model-Schicht entfallen und diese Funktionalität nur im ViewModel implementiert werden.

Beispielhaftes Model

Eine Basis für ein Model kann folgendermaßen abgebildet werden:

public class ModelBase : INotifyPropertyChanged, IDataErrorInfo
{
    protected void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public virtual string Error
    {
        get { return String.Empty; }
    }

    public virtual string this[string columnName]
    {
        get { return String.Empty; }
    }

}

Und hier nun ein konkretes Beispiel für ein Model, welches ein einfaches Unternehmen repräsentiert:

public class CompanyModel : ModelBase
{
    private string name;
    private string street;
    private string streetNumber;
    private string city;
    private string zip;
    private string country;

    public string Name
    {
        get { return name; }
        set
        {
            if (name == value)
                return;
            name = value;
            RaisePropertyChanged("Name");
        }
    }

    public string Street
    {
        get { return street; }
        set
        {
            if (street == value)
                return;
            street = value;
            RaisePropertyChanged("Street");
        }
    }

    public string StreetNumber
    {
        get { return streetNumber; }
        set
        {
            if (streetNumber == value)
                return;
            streetNumber = value;
            RaisePropertyChanged("StreetNumber");
        }
    }

    public string City
    {
        get { return city; }
        set
        {
            if (city == value)
                return;
            city = value;
            RaisePropertyChanged("City");
        }
    }

    public string Zip
    {
        get { return zip; }
        set
        {
            if (zip == value)
                return;
            zip = value;
            RaisePropertyChanged("Zip");
        }
    }

    public string Country
    {
        get { return country; }
        set
        {
            if (country == value)
                return;
            country = value;
            RaisePropertyChanged("Country");
        }
    }

    public override string this[string columnName]
    {
        get
        {
            switch (columnName)
            {
                case "Name":
                    if (String.IsNullOrWhiteSpace(Name))
                        return "Name des Unternehmens muss befüllt sein";
                    break;
            }
            return null;
        }
    }
}

Weiterführende praxisrelevante Informationen bieten diese Beispiele:
Lose Kommunikation zwischen ViewModels
Lose Kommunikation zwischen ViewModels 2
Binden von ViewModels via Locator
Binden von ViewModels via Locator 2

Über den Autor

Norbert Eder

Ich bin ein leidenschaftlicher Softwareentwickler und Fotograf. Mein Wissen und meine Gedanken teile ich nicht nur hier im Blog, sondern auch in Fachartikeln und Büchern.

4 Kommentare

  • Hallo! Echt tolle Artikel, jedoch stellt sich für mich eine konkrete Frage:
    Die Properties in Model und ViewModel sehen für mich im Aufbau genau gleich aus, ist das so richtig? Entschuldigung für meine Unwissenheit, ich versuche zurzeit noch mir WPF mit MVVM anzueignen.

    Danke im Voraus für die Antwort.
    MfG
    Patrick

    • Hallo Patrick,

      vielen Dank für dein Feedback.

      INotifyPropertyChanged kann sowohl im ViewModel als auch im Model eingesetzt werden, da auf beiden Ebenen Veränderungen vorgenommen werden könnten und so eine korrekte Benachrichtigung darüber ermöglicht wird.

  • Hallo Norbert,

    ich bin ziemlich „jungfräulich“, was MVVM und WPF angeht. Bisher habe ich immer mit WinForms gearbeitet, was aber für ein neues Projekt nicht mehr reicht aufgrund der Anforderungen an das GUI.

    Was ich bisher überhaupt nicht verstanden habe ist der der Umfang mit 3rd-Party-Assemblies. Arbeite ich damit im Model oder implementiere ich die gleich im ViewModel?

    In meinem Fall soll ein Softphone programmiert werden. Der komplette SIP-Stack einschließlich aller erforderlichen Methoden und Events (Dial, IncomingCallNotification, Settings in Datei speichern etc.) werden im 3rd-Party-SDK bereitgestellt.

    Im View muss ich also zum einen Elemente zur Verfügung stellen, die entsprechende Einstellungen, welche das SDK nutzt, editierbar und speicherbar machen und zum anderen die Eingabe einer Telefonnummer ermöglichen sowie eine Schaltfläche zum Wählen. Den eigenlichen Verbindungsaufbau macht das SDK.

    Für mich scheint das in dem Fall irgendwie ein Zwitter zu sein und ich bin ein wenig ratlos, weil ich nicht weiß, wie ich ansetzen soll.

    Vielleicht kannst Du mir ja einen Tipp geben.

    • Alle „Kommandos“ d.h. alle bereitgestellten Funktionalitäten werden über das ViewModel zur Verfügung gestellt. Ein Model brauchst du nur, wenn du Daten, die für das ViewModel/die View benötigt werden gehalten werden müssen.

      Für die Telefonnummer hast du ein Model (evtuell weitere einzugebenede Daten). Wenn du nun auf der View auf „Wählen“ gehst, wird diese Funktionalität im ViewModel aufgerufen und aus dem Model der Wert für die Telefonnummer herangezogen (der ohnehin auch im ViewModel auch verfügbar ist – an dieser Stelle muss man sich überlegen, wie sinnvoll ein Model überhaupt ist) um damit die Funktionalität aus dem SDK aufzurufen.

      Hier nochmals anders dargestellt:

      View:
      – Feld Telefonnummer (Binding an ViewModel „Phone“)
      – Schaltfläche „Dial“ -> Command im ViewModel

      ViewModel:
      – Property „Phone“
      – Command „Dial“ -> ruft SDK-Funktionalität auf

      Model (optional, außer auch genutzt zur Persistenz, siehe History etc.)
      – Property „Phone“

      Ich hoffe, das hilft vorerst weiter.