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 Model

Definition ViewModel

Das ViewModel stellt das Model für die View dar. Es gibt das eigentliche Model nach außen. Darauf kann per Datenbindung gebunden werden. Durch die im Model implementierte Change Notification werden Änderungen direkt an die View weitergeben. Code, der dies manipuliert, ist nicht notwendig. Ebenfalls stellt das ViewModel Funktionalitäten per Commands zur Verfügung. Diese werden durch die View ebenfalls gebunden, wodurch in der View kein Code dafür anfällt.

Besonders wichtig ist, dass das ViewModel nicht mit einem Code-behind verwechselt werden darf. Es ist nicht gestattet, Referenzen auf Elemente der View zu erstellen und auf diese zuzugreifen. Dies würde eine direkte Abhängigkeit erzeugen und den Versuch des Trennens und der losen Kopplung per Datenbindung ad absurdum führen. Muss auf UI-Elemente zugegriffen werden, dann ist das Code-behind der View die korrekte Stelle.

Auflistungen von modifizierbaren Objekten sind über ObservableCollection<> nach außen zu geben. Durch diese Klasse können Änderungen an der Auflistung erkannt und an das Binding System weitergegeben werden, ohne dafür zusätzlichen Code schreiben zu müssen.

Das ViewModel selbst bietet durch die Abstraktion der View die Möglichkeit an, alle angebotenen Funktionalitäten per Testing abzudecken. Der umständliche Weg, die Funktionen über Views zu testen, entfällt hierdurch.

Beispielhaftes ViewModel

Eine einfache Basisklasse für ViewModels könnte folgendermaßen aussehen:

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

    public event PropertyChangedEventHandler PropertyChanged;
}

Und so eine konkrete Implementierung:

public class CompanyViewModel : ViewModelBase
{
    private CompanyModel model;

    public CompanyModel Company
    {
        get { return model; }
        set
        {
            if (model == value)
                return;
            model = value;
            RaisePropertyChanged("Company");
        }
    }
}

Zu ergänzen ist an dieser Stelle, dass das ViewModel zuständig ist, die Daten zu laden (bzw. diesen Teil der Logik aufzurufen). Ebenfalls werden Commands für die unterschiedlichsten Funktionalitäten zur Verfügung gestellt.

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

Veröffentlicht von Norbert Eder

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

Beteilige dich an der Unterhaltung

9 Kommentare

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

  1. Hi Norbert,
    ich bin ein newbies in WPF/C# und arbeite momentan an einem Projekt. Dabei komme ich natürlich an die berühmte MVVM-pattern nicht vorbei.
    Ich habe ein Messgerät mit vielen einstellbaren Parameters und Methode zu messen, dies habe ich als mein Model definiert. Die Parameters werden als public Properties implementiert, mein Model implementiert selbst verständlich die INotifyPropertyChanged-Interface um die Änderung der Parameters über Events nach draußen zu benachrichtigen.
    Ein Beispiel:
    public double IntegrationTime
    {
    get { return GetPropertyValue(); }
    set { SetPropertyValue(value); }
    }

    Meine Business-Logik habe ich in ViewModel beherbergt. Das ViewModel braucht die Parameters von dem Messgerät für die Business-Logik und auch für den View, deshalb habe ich diese Parameters als DependencyProperty definiert, ein Beispiel: für die IntegrationTime-Paramter des Gerätes habe ich dementsprechend im ViewModel auch ein Dp implementiert:
    public static readonly DependencyProperty IntegrationTimeProperty =
    DependencyProperty.Register(“IntegrationTime”, typeof(double), typeof(SpectrometerViewModel),
    new PropertyMetadata((double)0, new PropertyChangedCallback(OnIntegrationTimeChanged), new CoerceValueCallback(CoerceIntegrationTime)));

    und verbinde die beide Parameters mit einander:
    Binding integTimeBinding = new Binding();
    integTimeBinding.Source = Spectrometer;
    integTimeBinding.Mode = BindingMode.TwoWay;
    integTimeBinding.Path = new PropertyPath(“IntegrationTime”);
    BindingOperations.SetBinding(this, IntegrationTimeProperty, integTimeBinding);

    Jetzt bin ich in der Lage, die IntegrationTime -Parameter dem Benutzer zur Verfügung zu stellen und über ihrer Änderung informiert.
    Nächste Schritt ist die Validierung des Parameter-wertes, dafür habe ich bei der Registrierung der IntegrationTimeProperty die CoerceValueCallback vorgesehen:
    private static object CoerceIntegrationTime(DependencyObject d, object baseValue)
    {
    SpectrometerViewModel spvm = d as SpectrometerViewModel;
    double integTime = (double)baseValue;
    if (integTime spvm.IntegrationTimeMax)
    integTime = spvm.IntegrationTimeMax;
    return integTime;
    }
    Diese soll nur den Wert der Parameter anhand max und min überprüfen und entsprechend korrigieren
    Soweit so gut , alles funktioniert wenn die IntegrationTimeProperty mit einem verbindet wird, wenn ein falschen Wert im TextBox angegeben wird, wird dieser wert korrigiert. Aber die Parameter IntegrationTime des Models wird nicht korrigiert, obwohlt diese vorher mit dem IntegrationTimeProperty ober BindingOperation verbindet wurde.
    Die Frage ist, ob das ein Bug von WPF oder habe ich was vergessen??

  2. Hallo,

    ich habe vor ca. 30min zum Laden der Daten bekommen, allerdings weiß ich jetzt nicht ob das noch MVVM "konform" ist.

    Um genau zu sein Binde ich meine Objekt wie folgender maßen an meine View.

    <Textbox Text="{Binding Path=CompanyModel.Street , Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

    Das Initialisieren des Objekts, sowie das setzen des DataContexts auf das ViewModel ist an diesem Punkt bereits abgeschlossen und das Binding wird erfolgreich vollzogen.

    Aber ist das noch MVVM konform?

  3. Schwer zu sagen, da ich deinen Aufbau nicht kenne.

    Das ViewModel selbst muss zusehen, dass die notwendigen Daten (Model) geladen wird. Wird das Laden derselben ausgeführt und zwar zum richtigen Zeitpunkt? Mir erscheint es so zu sein, dass deine Daten durch das ViewModel gar nicht oder zu einem zu späten Zeitpunkt geladen werden.

    Eventuell wirfst du darauf einen Blick …

  4. Hallo,

    erstmal ist zu sagen, dass dies ein sehr guter Artikel ist, welche viele Hilfestellungen bietet.
    Allerdings geht mir eine Sache nicht ganz auf bzw. verstehe ich diesen Aspekt noch nicht so ganz.

    Wie schaffe ich es, meine Daten aus dem Model mithilfe des ViewModels an meine View zu binden? Ich habe es nach deinen Beispielen versucht, allerdings ohne Erfolg.

    Mein Model ähnelt deinem CompanyModel und mein ViewModel ist auf dieses angepasst. Nun habe ich mein mein ViewModel via Locator an die View gebunden, allerdings werden meine Properties nicht gelesen oder beschrieben.

    Der DataContext, also das ViewModel, wird auch gefunden. Allerdings ist das Model standardmäßig null. Auch durch ein instanziieren des Models im ViewModel konnte ich keine Änderung herbeiführen.

    Kannst du mir weiterhelfen?
    MfG
    Raiko

  5. Ja, du hast recht, hatte auch diesen Lösungsansatz gestern noch umgesetzt.
    War vorher davon ausgegangen, das ich FrameworkElement in meinem Viewmodel nutzen kann, solange sie nicht eine direkten zusammenhang mit der View haben.
    In dem Aufbau konnte ich durchaus ohne Probleme view und Model trennen um zB eine andere View zu nutzen.
    Ich danke Dir für deine Hilfe, man lernt ja nie aus.

    Ich wünsche ein weiteres schönes Wochende und bin mal gespannt was man als nächstes in Deinem Blog lesen kann.

    Grüße aus dem Norden,

    Michael

  6. Deine Sektoren sind aber für die Anzeige von Daten zuständig, oder? Vermutlich ähnliche Daten, aber eben nicht dieselben? Eventuell mit gemeinsamer Basisklasse?

    Dein ViewModel gibt die eigentlichen Datenobjekte (idealerweise wieder als ViewModel) nach aussen. Dazu brauchst du dann noch für den entsprechenden Typ ein entsprechendes DataTemplate, welches beschreibt, wie das einzelne Objekt darzustellen ist. Deine Sektoren selbst kannst du per Collection nach aussen geben, die Darstellung übernimmt ein ItemsControl (ListView, etc.).

    Damit hat dein ViewModel mit UI-Elementen nichts am Hut, du bist lose gekoppelt und kannst die Darstellung der einzelnen Objekttypen jederzeit über ein ResourceDictionary ändern.

    Hoffe, das hilft dir soweit weiter.

  7. Ja da liegt mein Dilemma.

    Ich habe einen Screen, welcher unterschiedliche "Sektoren" anzeigen soll.
    Diese Sektoren sind UIElemente welche wiederum aus unterschiedlichen Background und Content bestehen.
    Dementsprechend Weiß ich vorab nicht Wieviele Sektoren ich zur Laufzeit habe.

    Meinst du, das ich entsprechend in meinem Screen ein Itemscontrol habe, in welchem ich mit meinem DataTemplate die Sektoren Darstelle ( über bindings bezüglich position, größen, etc )?
    Ich stehe zur Zeit noch recht am Anfang vom MVVM, und stolpere daher leider noch über einige Sachen…

    Entwickelt wird dies übrigens mit Silverlight 4, falls dies wichtig ist.

  8. Dein ViewModel darf keine Eigenschaft vom Typ FrameworkElement haben. Das ViewModel ist kein Code-behind und darf auch nicht so behandelt werden. Das FrameworkElement gehört in die View.

    Was willst du denn machen? Vermutlich läßt sich dein Vorhaben über ein DataTemplate lösen.

  9. Hallo Norbert,

    vielen Dank für diesen sehr Interessanten Blog.
    Mir stellt sich noch die Frage wie ich es Löse, wenn ich in meinem ViewModel als Eigenschaft ein Frameworkelement habe, das dieses entsprechend an zB. ein Canvas gebunden wird.
    Ich stehe dort im Moment ein wenig auf dem Schlauch wie ich dies Elegant löse…

    Grüße Michael

Cookie-Einstellungen
Auf dieser Website werden Cookie verwendet. Diese werden für den Betrieb der Website benötigt oder helfen uns dabei, die Website zu verbessern.
Alle Cookies zulassen
Auswahl speichern
Individuelle Einstellungen
Individuelle Einstellungen
Dies ist eine Übersicht aller Cookies, die auf der Website verwendet werden. Sie haben die Möglichkeit, individuelle Cookie-Einstellungen vorzunehmen. Geben Sie einzelnen Cookies oder ganzen Gruppen Ihre Einwilligung. Essentielle Cookies lassen sich nicht deaktivieren.
Speichern
Abbrechen
Essenziell (1)
Essenzielle Cookies werden für die grundlegende Funktionalität der Website benötigt.
Cookies anzeigen