Laden...

MVVM vs ValueConverter

Erstellt von edsplash vor 14 Jahren Letzter Beitrag vor 14 Jahren 3.561 Views
edsplash Themenstarter:in
390 Beiträge seit 2008
vor 14 Jahren
MVVM vs ValueConverter

Hallo Zusammen

Im Zusammenhang mit MVVM und ValueConvertern ist mir vorhin folgende Frage durch den Kopf gegangen:
Wenn ich im Model ein Property als DateTime anbiete, muss ich es im ViewModel als String oder ebenfalls als DateTime anbieten?
Würde ich es als String anbieten, wäre ich gezwungen die Formatierung im ViewModel vorzunehmen, was das ViewModel automatisch weniger "reusable" macht. Biete ich das Property als DateTime an, bin ich gewzwungen einen ValueConverter zu verwenden. Dies hätte zwar den Vorteil, dass das VM besser wiederverwendet werden kann. Allerdings nimmt es so dem ViewModel nahezu die Daseinsberechtigung, da das ViewModel ja eigentlich die Repräsentation des Views darstellt, obwohl diese Aussage wieder auf den Streit "Ein ViewModel pro View oder ein ViewModel pro Model" hinauslaufen würde.

Basierend auf folgendem Thread habe ich mir noch weitere Gedanken gemacht:
Thought: MVVM eliminates 99% of the need for ValueConverters

Schlussendlich läuft es auch dort auf ein hypothetisches "MMVVVM" Pattern hinaus, was mich meine Frage auf folgende Art beantworten lässt:
Existiert nur ein ViewModel pro Model (wie z.B. Customer und CustomerViewModel), sollte das ViewModel die Konvertierung NICHT vornehmen, da es wieder-verwendet werden können sollte. Ein ViewModel welches allerdings fest an ein View gekoppelt ist, dürfte ruig die Konvertierung selber in die Hand nehmen. Ein gutes Beispiel für ein solches ViewModel wäre ein MainWindowViewModel.

Man hätte also zwei Arten ViewModels. Die eine Art repräsentiert ein Model und die andere Art repräsentiert ein View. Ein solches ViewModel, welches ein View repräsentiert, würde eigentlich das View antreiben. Was ist aber mit den ViewModels, die ein Model repräsentieren?

To put it another way, I think it risks mixing concerns. If I'm browsing the
VM following the process for downloading the file and communicating whether
it's done, I'd rather not see the code adding "%" after each number or an
IsCancelButtonEnabled property. I'd much rather a ProgressPercentage integer
property and a HasCompleted boolean property, and let the view convert it.
The VM manages state, the view manages how it's visualized.

Um das Beispiel mit den Customers aufzugreifen, stelle ich folgendes Szenario auf:
Eine Klasse Customer wird von einem CustomerViewModel gekapselt. Eine View AllCustomersView hätte ein AllCustomersViewModel hinter sich. All CustomersViewModel bestünde wiederum aus einer Liste von CustomverViewModel. Diese Liste wird per Binding an einen DataGrid gebunden. AllCustomersViewModel beinhaltet weiterhin die nötigen Commands und Logik um die lokalen Daten zu aktualisieren. Ein weiteres View EditCustomerView hätte wiederum ein ViewModel EditCustomerViewModel hinter sich, welches intern ein CustomerViewModel enthält. Auch beinhaltet dieses EditCustomerViewModel die Commands um den veränderten Customer der Datenbank zu übergeben. Das CustomerViewModel selbst enthält weder Commands, die an ein View gebunden werden könen, um die Datenbank zu aktualisieren noch solche um die neusten Daten zu holen. Das CustomerViewModel leitet eigentlich nur die Daten weiter.
Wenn man nun wieder das Zitat von Paul Stovell liest. Bemerkt man, dass auch eine Konvertierung im CustomerViewModel am falschen Ort ist, da man, wie er sagt, „not see the code adding "%" after each number“. Zu diesem Zeitpunkt beginne ich, an der Daseinsberechtigung des CustomerViewModels zu zweifeln. Es konvertiert weder Daten, noch ist es in der Lage ein View anzutreiben. Es leitet lediglich die Daten weiter. Da die Konvertierung sowieso von einem ValueConverter übernommen wird, kann man doch sagen, dass das CustomerViewModel nur noch für das „NotifyPropertyChanged“ da ist?

Rechtfertigt sich aber ein solcher Aufwand, nur damit man kein NotifyPropertyChanged in den Modelklassen implementieren muss?

Gruss

using Skill

5.742 Beiträge seit 2007
vor 14 Jahren

Hallo edsplash,

Wenn ich im Model ein Property als DateTime anbiete, muss ich es im ViewModel als String oder ebenfalls als DateTime anbieten?

Tja - ich denke, dass muss jeder für sich entscheiden.
Auch ich habe über diese Frage (in ähnlicher Weise) eine Weile nachgedacht und bin zu keinem wirklichen Ergebnis gekommen.

Zur Zeit (könnte sich in Zukunft auch mal ändern 😁 ) tendiere ich eher zum String: Dann kann das ViewModel genau beeinflussen, wie das Datum angezeigt wird. Das geht zwar auch per Binding und Formatstring (somit braucht man nicht unbdedingt einen ValueConverter) - wenn man aber das z.B. zur Laufzeit vom Benutzer verändern lassen wollen würde, stößt dieses Vorgehen sehr schnell an seine Grenzen.
Außerdem vereinfacht es die Lokalisierung etwas, da man sonst evtl. sogar verschiedene XAML-Varianten bräuchte.
Aber wie gesagt: Eine eindeutige Antwort kann ich da leider nicht geben (auch wenn ich es gerne würde).

Früher habe ich zugegebenermaßen auch eher hin zum ValueConverter tendiert - zwecks der besseren "Reusabilität" der ViewModels.
Allerdings kam ich dann bald zu dem Schluss, dass ein ValueConverter eigentlich auch nur suboptimal ist und habe daraufhin zu einem noch komplexeren Konstrukt gegriffen. Als nächstes habe ich dann festgestellt, dass es ja auch sinnvoll wäre, Layout, Inhalt und Design zu trennen (dann wäre ja der XAML-Code auch universeller einsetzbar).
Irgendwann habe ich mich dann aber glücklicherweise gefragt, was ich mir eigentlich von dme ganzen verspreche: Klar, Universlität ist immer gut - aber leider wurde auch alles immer komplexer. Der eigentliche Sinn von MVVM (nämlich: Logik vom UI in einer einfachen Art und Weise zu trennen) rückte mehr und mehr in den Hintergrund.
Daher sehe ich das jetzt etwas pragmatischer: Auch mal ohne schlechtes Gewissen was ins ViewModel packen - später ändern bzw. allgemeiner fassen kann man es immer noch (und zwar relativ einfach).
Wo ich allerdings tatsächlich noch auf XAML Code zurückgreife, sind z.B. Farben, Schriftarten usw. Allerdings dann meist nicht via Converter, sondern via (Data-)Trigger. Auch bei Geschichten wie Visibility werkelt bei mir meist ein Converter (wenn gerade kein DataTemplate passt, was im Großteil der Fälle nicht zutrifft).

Allerdings nimmt es so dem ViewModel nahezu die Daseinsberechtigung, da das ViewModel ja eigentlich die Repräsentation des Views darstellt, obwohl diese Aussage wieder auf den Streit "Ein ViewModel pro View oder ein ViewModel pro Model" hinauslaufen würde.

Für mich hat hinsichtlich dieser Entscheidung inzwischen das von dir auch schon angesprochene MMVVVM-Pattern gewonnen - also sowohl ein ViewModel pro View als auch ein ViewModel pro Model, wobei man sich hier IMHO große Ausnahmen erlauben kann bzw. je nach Situation auch sollte.

Allerdings nimmt es so dem ViewModel nahezu die Daseinsberechtigung, da das ViewModel ja eigentlich die Repräsentation des Views darstellt, obwohl diese Aussage wieder auf den Streit "Ein ViewModel pro View oder ein ViewModel pro Model" hinauslaufen würde.

Wie gesagt: Dass passiert bei zu viel "Verallgemeinerung", die deshalb vermieden werden sollte.

kann man doch sagen, dass das CustomerViewModel nur noch für das „NotifyPropertyChanged“ da ist?

Theoretisch könnte das MVM (also in deinem Beispiel CustomerViewModel auch geringere Veränderungen an der Struktur der Daten vornehmen (gerade z.B. Dictionaries auflösen usw.).
Zudem ist es denkbar, das auch transparentes Lazy-Loading usw. eingebaut werden könnte.

Rechtfertigt sich aber ein solcher Aufwand, nur damit man kein NotifyPropertyChanged in den Modelklassen implementieren muss?

Auch wenn mein Bauchgefühl sagt: "Ja, das ist der Fall", hängt das IMHO doch sehr stark von der Größe und Art der Anwendung ab.

edsplash Themenstarter:in
390 Beiträge seit 2008
vor 14 Jahren

Hallo winSharp93

"Schön" zu sehen das sich auch andere über dieses Thema den Kopf zerbrechen können 😉

Irgendwann habe ich mich dann aber glücklicherweise gefragt, was ich mir eigentlich von dme ganzen verspreche: Klar, Universlität ist immer gut - aber leider wurde auch alles immer komplexer. Der eigentliche Sinn von MVVM (nämlich: Logik vom UI in einer einfachen Art und Weise zu trennen) rückte mehr und mehr in den Hintergrund.
Daher sehe ich das jetzt etwas pragmatischer: Auch mal ohne schlechtes Gewissen was ins ViewModel packen - später ändern bzw. allgemeiner fassen kann man es immer noch (und zwar relativ einfach).

Da hast Du natürlich recht. Idealismus und der Drang zur Perfektion ist in solchen Fällen eher hinderlich.

Theoretisch könnte das MVM (also in deinem Beispiel CustomerViewModel auch geringere Veränderungen an der Struktur der Daten vornehmen (gerade z.B. Dictionaries auflösen usw.).
Zudem ist es denkbar, das auch transparentes Lazy-Loading usw. eingebaut werden könnte.

Stimmt, das LazyLoading habe ich nicht bedacht.

Gruss

using Skill

1.044 Beiträge seit 2008
vor 14 Jahren

Hallo edsplash,

die Aufbereitung des Models findet im ViewModel statt und wird letzen endes auf der View repräsentiert. Die View ist somit die eigentliche Repräsentation des ViewModels. Das ViewModel - oder auch PresentationModel - macht nichts anders, als das Model aufzubereiten und die Daten der View zur Verfügung zu stellen. Alles, was mit der visuellen Repräsentation zutun hat, darf nicht im ViewModel sattfinden, da dies eine Sache der View ist. Das ViewModel soll wiegesagt die Daten bereitstellen und der View bereitstellen. In diesem Falle wäre ein Converter für die View spezifischer als für das ViewModel. Wenn das ViewModel schon die Daten bereitstellt, so kann dies auch als string sein. Ein String wäre in diesem Falle nichts anderes, als "etwas", was als DateTime fungiert und somit eher unsinnig. Es darf allerdings nicht sein, dass der Converter Logik abnimmt, die durch ein ViewModel stattfinden muss! Diese Programmatik habe ich hier beschrieben. Der Converter bietet meiner Meinung nach her mehr Möglichkeiten und trennt die allgemeine Logik von dem Visuellen. Eine weitere Möglichkeit ist es, wenn das ViewModel selber die Konvertierung vornimmt. Dies bietet wäre in Anbetracht deiner Situation ein wenig mehr Flexibilität.

AllCustomersViewModel bestünde wiederum aus einer Liste von CustomverViewModel

Ich finde es persönlich besser, wenn man die CustomerViewModel-Klasse verwendet, obwohl die Klassen wenig Sinn hat. Dadurch hat man ein "klares" Pattern, da man sonst eher das Pattern zwingt sich zu verändern. Ob man nun pro View ein ViewModel verwendet oder es irgendwie anders löst, bleibt jedem selber überlassen. Ein Pattern ist mehr eine Richtline und eine Struktur, wie man etwas gut realisieren kann. Es kommt wiegesagt auf den konkreten Fall an. Man kann noch einen Schritt weitergehen und sich folgendes Fragen: "Soll eine ListBox eine Reihe von Views oder ViewModels anzeigen?".

zero_x

edsplash Themenstarter:in
390 Beiträge seit 2008
vor 14 Jahren

Hallo zero_x

Alles, was mit der visuellen Repräsentation zutun hat, darf nicht im ViewModel sattfinden, da dies eine Sache der View ist.

Ein String wäre in diesem Falle nichts anderes, als "etwas", was als DateTime fungiert und somit eher unsinnig.

Man kann das Problem jedoch auch so betrachten, dass der String bereits eine visuelle Repräsentation des DateTimes ist. Vielleicht möchte das View das Datum auch als eine Art Uhr anzeigen und da wäre der String dann eher hinderlich. Allerdings kann man auch hier sagen, dass man es von Fall zu Fall beurteilen muss.

Man kann noch einen Schritt weitergehen und sich folgendes Fragen: "Soll eine ListBox eine
>
von Views oder ViewModels anzeigen?".

Eigentlich leicht zu beantworten: Weder noch und trotzdem beides. Die Liste enthält zwar die ViewModels, diese werden allerdings gerendert und der Benutzer bekommt eigentlich das View zu sehen.

Gruss

using Skill

1.044 Beiträge seit 2008
vor 14 Jahren

Hallo edsplash,

die DateTime-Klasse zu verwenden bietet - wie ich schon sagte - mehr Möglichkeiten als ein String. Damit das ViewModel einen String zurückliefert, muss das ViewModel dies umwandeln und dann auf der View anzeigen. Man kann gleich auf der View die DateTime mithilfe eines Converter anzeigen lassen. Soll auf der View eine Uhr angezeigt werden, so kann man das mit einem DataTemplate machen.

Beinhaltet die CustomerViewModel-Klasse Eigenschaften wie z.B. die IsSelected, so hat man mehr Möglichkeiten und Flexibilität, ansatt wenn man direkt das Model daran bindet.

Die beiden von mir erwähnten Punkte bringen meiner Meinung nach nur Vorteile mit sich. Beachte bitte auch meinen vorherigen Post.

zero_x