none
MVVM - Liste in Model für Binding verwenden RRS feed

  • Frage

  • Das Thema bringt mich etwas in's Grübeln. Mal angenommen, mein Model hat eine Methode "PersonFile LoadPersons(string fileName)", welche eine Datei einliest und zu einem .NET-Objekt umwandelt. Das Objekt ist vom Typ PersonFile und besitzt eine Property "IEnumerable<Person> Persons { get; set; }". Das PersonFile wird dann im ViewModel als Property zur Verfügung gestellt, mit Benachrichtigungsmöglichkeit per INotifyPropertyChanged.

    Weiter nehmen wir mal an, dass in der View die Persons aus dem PersonFile des ViewModels per Binding angezeigt werden und es einen Button gibt, über welchen eine neue Person in einem PersonFile angelegt werden kann, wobei die Person dann einfach der Auflistung "Persons" in dem PersonFile hinzugefügt wird. Natürlich wird diese neue Person nicht in der View angezeigt, da die View nicht weiß, dass sich die Auflistung geändert hat. Der erste Gedanke wäre eine ObservableCollection<Person>, aber die in das Datenmodell zu nehmen erscheint mir als falsch. Und hier würde ich gerne wissen, wie ich das am besten löse.

    Freitag, 2. Dezember 2011 00:00

Antworten

  • Mir ist nicht klar, was Dein PersonFile ist. Wenn das ein Objekt ist, welches im ViewModel erzeugt wird und Veränderungen der darin enthaltenen Daten meldet (INotiffyPropertyChanged), dann kannst Du dieses Objekt problemlos für komplexe Bindungen nutzen. Ein Objekt vom Typ ObservableCollection macht genau das. Wenn Dich eine feste Kopplung zwischen den Schichten nicht stört, dann kann dieses Objekt auch schon im Model erzeugt werden und direkt bis in die UI durchgereicht werden. Eine feste Kopplung verursacht aber bei Notwendigkeit der mehrfachen Nutzung meist immense Probleme.
     
    Wo die Klassen definiert sind, ist nebensächlich. Wichtig ist, dass beide Schichten, die die Klassen nutzen, diese auch kennen. Zirkuläre Referenzen sind dabei nicht zulässig, da sie sich nicht auflösen lassen.
     
    Ein überschaubarer Lösungsweg kann so aussehen:
     
    1. In verschiedenen Schichten genutzte Klassen werden in einer separaten Bibliothek gehalten. Alle Schichten können auf diese Bibliothek verweisen.
    2. Auf Anfrage des ViewModels stellt das Model Listen mit Datenobjekten bereit.
    3. Das ViewModel nutzt diese Listen, indem es passende Sichten (Views) bereitstellt, die bei Änderungen diese auch mitteilen (INotifyPropertyChanged). Eine optimale Möglichkeit wird mit ObservableCollection, ICollectionVie und CollectionViewSource geboten.
    4. In der Oberfläche (UI, View) werden dann die vom ViewModel bereitgestellten Objekte gebunden und die Anzeige wird direkt aus den Objekten oder über explizit vom ViewModel ausgelöste PropertyChanged aktualisiert. 
     
    --
    Viele Gruesse
    Peter
    • Als Antwort markiert YoYoFreakCJ Samstag, 3. Dezember 2011 10:41
    Samstag, 3. Dezember 2011 10:05

Alle Antworten

  • In nur äußerst seltenen Fällen muss der Model die UI über Änderungen informieren. Das wäre beispielsweise der Fall, wenn der Model über einen Zeitgeber gesteuert neue Daten bereitstellt.
     
    In dem von Dir geschilderten Fall löst die UI (View) den Änderungsvorgang aus, der dann im ViewModel resp. Geschäftslogik verarbeitet wird. Die Geschäftslogik entscheidet, dass eine neue Person der Liste hinzuzufügen ist und kann damit über NotifyPropertyChanged der IU mitteilen, dass sich etwas geändert hat. Dass die im ViewModel gekapselte Geschäftslogik zusätzlich auch den Model für die Ablage der Daten nutzt, ist dabei nebensächlich. Jedenfalls ist in diesem Szenario nicht der Model der Auslöser der Änderungen und braucht deshalb auch nichts zu tun.Er stellt die Daten auf Anforderung bereit und der Anforderer (ViewModel) hat sich um die Information an die UI zu kümmern. Ohne Neubindung der Liste mit den Datenobjekten muss die Liste selbst informieren, was am einfachsten mit einer ObservableCollection realisierbar ist.
     
    --
    Viele Gruesse
    Peter
    Freitag, 2. Dezember 2011 04:34
  • Danke Peter.

    Zum Sicherstellen dass ich dich richtig verstanden hab:

    Das Model bleibt also unverändert, soweit klar. Du schreibst: "Ohne Neubindung der Liste mit den Datenobjekten muss die Liste selbst informieren, was am einfachsten mit einer ObservableCollection realisierbar ist." Bedeutet das, dass ich im ViewModel nicht nur das PersonFile, sondern auch eine ObservableCollection<Person> anbieten soll, welche beim Erstellen einer neuen Person auch das Model updated? Dazu hätte ich die generelle Frage, ob das ViewModel überhaupt Daten aus dem Model anbieten sollte, oder diese Kapseln (so wie in diesem Fall über die ObservableCollection).

    Mein Verständnis war bisher so, dass im Model die Daten per Data Layer gelesen und geschrieben werden, aber auch, dass im Model die Klassen definiert sind, welche vom Model ausgelesen werden (in meinem fall Person und PersonFile). Ist das korrekt so?

    Freitag, 2. Dezember 2011 15:07
  • Mir ist nicht klar, was Dein PersonFile ist. Wenn das ein Objekt ist, welches im ViewModel erzeugt wird und Veränderungen der darin enthaltenen Daten meldet (INotiffyPropertyChanged), dann kannst Du dieses Objekt problemlos für komplexe Bindungen nutzen. Ein Objekt vom Typ ObservableCollection macht genau das. Wenn Dich eine feste Kopplung zwischen den Schichten nicht stört, dann kann dieses Objekt auch schon im Model erzeugt werden und direkt bis in die UI durchgereicht werden. Eine feste Kopplung verursacht aber bei Notwendigkeit der mehrfachen Nutzung meist immense Probleme.
     
    Wo die Klassen definiert sind, ist nebensächlich. Wichtig ist, dass beide Schichten, die die Klassen nutzen, diese auch kennen. Zirkuläre Referenzen sind dabei nicht zulässig, da sie sich nicht auflösen lassen.
     
    Ein überschaubarer Lösungsweg kann so aussehen:
     
    1. In verschiedenen Schichten genutzte Klassen werden in einer separaten Bibliothek gehalten. Alle Schichten können auf diese Bibliothek verweisen.
    2. Auf Anfrage des ViewModels stellt das Model Listen mit Datenobjekten bereit.
    3. Das ViewModel nutzt diese Listen, indem es passende Sichten (Views) bereitstellt, die bei Änderungen diese auch mitteilen (INotifyPropertyChanged). Eine optimale Möglichkeit wird mit ObservableCollection, ICollectionVie und CollectionViewSource geboten.
    4. In der Oberfläche (UI, View) werden dann die vom ViewModel bereitgestellten Objekte gebunden und die Anzeige wird direkt aus den Objekten oder über explizit vom ViewModel ausgelöste PropertyChanged aktualisiert. 
     
    --
    Viele Gruesse
    Peter
    • Als Antwort markiert YoYoFreakCJ Samstag, 3. Dezember 2011 10:41
    Samstag, 3. Dezember 2011 10:05
  • Und wieder, danke Peter.

    Das PersonFile war bisher auch als Klasse im Model definiert, weil ich fälschlicherweise davon ausging dass die Typen alle im Model definiert werden sollen. Es beinhaltet die IEnumerable<Person>, sowie Daten zur Datei selbst, wie den Ablagepfad.

    Deine Antworten sind echt klasse. Etwas mehr als gefragt war, aber alles, was man zu dem Thema wissen muss. Genau solche Beiträge will man finden, wenn man danach googled. :)

    Samstag, 3. Dezember 2011 10:46