MVVM C#

MVVM C#

MVVM C#

Du möchtest mit MVVM in C# durchstarten und somit deine Anwendungen durch das „Model View ViewModel„-Entwurfsmuster auf ein neues Level bringen?

Oder möchtest Du verstehen, was dieses Entwurfsmuster ist, warum man es verwendet und vor allem wie man es in C# anwendet – dann los!

Vielleicht hast Du in Kombination mit diesem Beitrag auch noch Interesse an diesen anderen Beiträgen: ObservableCollection, WPF DataTemplate, MahApps Metro Framework.

Die alten Zeiten

Verantwortung und Rollen
Verantwortung und Rollen

Die alten Zeiten glänzten vor Misch-Masch, vor allem wenn es um Daten und deren Darstellung in Steuerelementen ging.

Als Entwickler, für gewöhnlich mit der Entwicklung von Prozessen und deren Testung beschäftigt, hatte man sich auch mit der grafischen Oberfläche auseinanderzusetzen.

Dies hatte häufig eine schlechte Wart- und Testbarkeit zur Folge und somit eine Vermischung der Rollen.

Während die Designer Ihr Augenmerk auf UI/UX und die Entwickler Ihren Fokus auf die Logik legen sollten, waren diese Rollen stattdessen fließend.

Du wirst diese Problematik nachvollziehen können, wenn Du selbst einmal mehrere Rollen gleichzeitig ausfüllen musstest.

Vor allem in kleinen Firmen ist häufig aufgrund des Budgets nicht die Manpower vorhanden, um einzelne Bereiche separat abzudecken.

Das hat leider zwangsweise zur Folge, dass diese Bereiche und deren Verantwortlichkeiten nicht sauber getrennt werden.

Ein jeweilige Team muss somit die individuellen Stärken in den Hintergrund stecken und einzelne Rollen aus dem Fokus verlieren.

MVVM das Optimum – Jedem das Seine

Ich denke, jedem sollte seine Rolle überlassen werden, damit er sich voll und ganz darauf konzentrieren und seine Stärken ausspielen kann.

MVVM getrennte Entwickler Designer  Rollen
MVVM getrennte Entwickler Designer Rollen

Das MVVM-Entwurfsmuster bietet eine tolle Basis für die Umsetzung separater Bereiche durch eigene Teams.

DatenbankDesigner können sich auf die Modellierung konzentrieren, SchnittstellenEntwickler auf ihre Schnittstellen und DTOs.

Weitere Entwickler setzen den Fokus auf ihre Business-Logik und die Designer letztendlich auf Ihre grafische Oberfläche.

Natürlich gibt es auch noch weitere Bereiche, Welche besonders in kleinen Firmen vermischt werden, aber tauchen wir nicht zu tief darin ab.

MVVM erklärt – MVVM C#

Bevor wir nun den Bezug zu C# konkretisieren, beschäftigen wir uns noch näher mit den Details des MVVM-Entwurfsmusters.

Schaue Dir ggf. zeitgleich zu den Erklärungen noch folgendes Bild in z. B. einem zusätzlichen Tab an:

MVVM Ebenen dargestellt
MVVM Ebenen dargestellt

View

Den ersten Block auf den Du oben im Bild stoßen wirst, ist das View.

Es spiegelt einzelne visuelle Bestandteile wie eine Sidebar oder auch Kacheln wider.

Denke bei einem View bitte nicht zwangsweise an ein stupides Fenster.

Auch wenn ein Fenster sicherlich eine Art von View ist, ist es nicht das Eine“ View schlechthin.

Viele sich noch nicht lange mit dem MVVM-Pattern beschäftigende Menschen kommen bei dem Gedanken an ein View sofort Fenster in den Sinn.

Sicher ist das typische Fenster ein Anfang, jedoch könnte man es genauso gut in weitere Sub-Views und Sub-ViewModels unterteilen.

Diese können dann entweder direkt, oder indirekt miteinander kommunizieren, bzw. interagieren.

Im Grund ist das View eine Art Template, Welches nur die benötigten Anweisungen für die Datenbindungen bekommt.

Somit hat es alle notwendigen Informationen, um die durch Templates bestimmte Elemente anhand der Daten darzustellen.

Die Datenbindungen bestimmen z. B. auch im ViewModel vorgesehene Methoden, die z. B. bei Klicks von Buttons aufgerufen werden sollen.

ViewModel

Das ViewModel beinhaltet die aufbereiteten Daten, die dem View durch Datenbindungen zur Verfügung gestellt werden.

Dem ViewModel werden über diverse Techniken wie die Dependency-Injection, Services und mehr injiziert, Welche das ViewModel dann verwenden kann.

Über diese Services kann das VM dann diverse Daten/Models laden und anschließend nach eigener Logik aufbereiten.

Wie oben schon erwähnt beinhaltet das ViewModel auch die für die Buttons relevanten Methoden.

Zusätzlich dazu, kann ein ViewModel gewiss auch eigene Ereignisse auslösen und auch auf abonnierte Ereignisse reagieren.

Somit hast Du nun auch schon einen möglichen Weg kennengelernt, wie ViewModels untereinander kommunizieren können.

Ein alternativer Weg zu dieser Kommunikationsweise wäre die Verwendung eines EventBus.

Dieser Weg funktioniert zwar auch mit einer Art Ereignissen, jedoch ein wenig anders.

Model

Das Model im MVVM-Entwurfsmuster stellt häufig die Daten mit denen man arbeitet selbst dar.

Wenn Du zum Beispiel einen Terminkalender programmierst, wäre das Erste, was einem dazu wohl einfällt der Termin selbst.

Du würdest Dir Gedanken machen, welche Eigenschaften wie z. B.: Betreff, Startdatum, usw. der Termin hat.

Danach würdest Du vermutlich hingehen und Dir eine Klasse wie Diese hier bauen:

Public Class Appointment
    
    Public Property Subject As String

    Public Property StartDate As Date

    Public Property Priority As Priority

End Class
public class Appointment
{
    
    public string Subject { get; set; }

    public DateTime StartDate { get; set; }

    public Priority Priority { get;set; }

}

Hier ist die zum Termin gehörige Enumeration der Prioritäten:

Public Enum Priority As Byte
  NORMAL
  HIGH
  VERY_HIGH
End Enum
public enum Priority : byte
{
    NORMAL,
    HIGH,
    VERY_HIGH
}

Vorteile von MVVM

Wie Du oben schon lesen konntest, bringt dass MVVM-Entwurfsmuster einige Vorteile mit sich.

Welche Vorteile das genauer genommen sind, werden wir uns nun noch einmal separat ansehen.

Trennung von Daten und deren Darstellung

Das Gotcha dieser Herangehensweise mag vielleicht nicht immer auf ersten Blick erkennbar sein.

Lass es mich Dir so erklären: Eine saubere Trennung, also die Unabhängigkeit von Dingen gibt Diesen immer eine Möglichkeit getrennt zu wachsen.

Gezielt auf unser Beispiel in der Softwareentwicklung bedeutet das natürlich einen Vorteil im Bereich Aufwand, etc.

Darstellungsmöglichkeiten

Stelle Dir vor, dein TerminModel hat ein Startund ein Enddatum, Welche Du in verschiedenen Views ggf. unterschiedlich darstellen möchtest.

Für einen Termin im Kalender selbst, spielen die Sekunden ggf. keine Rolle, bei einer Abrechnung für einen größeren Zeitraum allerdings schon!

Würdest Du dann dein Model verschmutzenwollen, indem Du für 3 unterschiedliche Verwendungsfälle 3 unterschiedliche neue Eigenschaften hinzufügst?

Ich würde lieber das Model unangetastet lassen und das jeweilige ViewModel die Daten aufbereitet darstellen lassen!

Aggregation von Daten

Leider kommt es eher selten vor, dass Du die benötigten Daten in jener Kombination hast, wie Du sie brauchst.

Daher hat man durch die Verwendung von ViewModels die Möglichkeit, Models zu aggregieren, bzw. zu kombinieren.

Zwei-Wege Bindungen & Code-Duplikate

Ein weiterer mir sofort einfallender Vorteil ist die Zwei-Wege Datenbindung, womit man sich teils nervige Aufrufe wie „DeinControl.Text = ..“ spart.

Die geänderten Daten werden durch die Bindung direkt von der Quelle zum Ziel und umgekehrt weitergeleitet.

Testbarkeit

Aufgrund der sauberen Trennung von Daten und Oberfläche, ist es relativ leicht, ein ViewModel mit verschiedenen Views zu testen.

Dies gilt natürlich auch andersherum, es hindert mich keiner daran, das gleiche View mit einem anderen ViewModel zu testen.

Ebenso können die ViewModels in Test-Umgebungen völlig autark getestet und teils ohne Oberflächen überprüft werden.

Support in .NET

Der MVVMSupport in .NET ist durch viele bereitgestellte Schnittstellen wie z. B. „INotifyPropertyChanged“ und „INotifyCollectionChanged“ grandios.

Natürlich bietet das .NET Framework auch bereits „ready-to-use„-Implementierungen für diverse Schnittstellen an.

Von vielen Außenstehenden wird WPF hoch gelobt und auch wenn ich damals selbst sehr zögerlich an die Sache ging.

Die Zeit die ich bisher mit WPF verbracht habe, hat mich definitiv zur Meinung gebracht, nie wieder ohne arbeiten zu wollen.

Styling

Es macht auch einen riesen Spaß, einfach ViewModel-Daten an spezifische Stile zu binden und das Look-And-Feel der Anwendung darüber zu gestalten.

Wieder beim Beispiel Termine angekommen, könnte man hier einfach je nach Priorität einen Termin kinderleicht anders färben.

MVVM C# Beispiel

Einen beispielhaften Termin mit Priorität „Normal“ stelle ich zum Beispiel im nächsten Bild einmal dar:

MVVM C# – Foreground anhand Enum für Termin festlegen
MVVM C# – Foreground anhand Enum für Termin festlegen

Wenn ich die Priorität des Termins nun im Code ändere, wird das Label beim nächsten Start anders dargestellt:

MVVM C# – Foreground anhand Enum für Termin festlegen 2
MVVM C# – Foreground anhand Enum für Termin festlegen 2

Normalerweise würde man das hier statt erst nach Programmstart natürlich auch via Datenbindung + ÄnderungsBenachrichtigung bauen.

Allerdings wollte ich es für das Beispiel so einfach wie möglich halten!

Man könnte natürlich auch noch wesentlich tiefer in die Materie eindringen, allerdings denke ich, dass dieses Beispiel erstmal reicht.

Du hast hier letztendlich Daten, die sich im Hintergrund befinden und durch Regeln“ im View unterschiedlich dargestellt werden.

Dafür nutzen wir ein ViewModel, was die Datenquelle des MainWindows wird und instanziieren dort eine Instanz des Termin-Models.

Basierend auf der Priorität hätte man auch die Möglichkeit, völlig verschiedene Templates zu wählen, aber das wird ein späteres Thema sein.

Fazit

MVVM ist also ein sogenanntes Entwurfsmuster für Anwendungen, um diese besser managen zu können.

Von der Rollenverteilung der involvierten Mitarbeiter, bis hin zu Testbarkeit und mehr, lässt sich dadurch besser gestalten.

Wie Du sehen konntest handelte es sich in diesem Beitrag erstmal um das Grundverständnis von MVVM allgemein.

Sicherlich werde ich in Zukunft noch weitere und vor allem detailliertere Beispiele demonstrieren.

Diese wirst Du dann in Form von konkreten Einsatzmöglichkeiten und MiniProjekten in anderen Beiträgen finden.

Downloads

Schreibe einen Kommentar

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