Ein WPF MVVM Projekt aufsetzen (VB.NET & C#) – 2024 Guide
Inhaltsverzeichnis
- 1 Moderne Software durch WPF MVVM Projekte erstellen
- 2 Kurzfassung – WPF MVVM Projekt aufsetzen
- 3 Details zur INotifyPropertyChanged-Schnittstelle
- 4 Mehr über das MVVM-Entwurfsmuster selbst
- 5 Schritt 1: MVVM-Ordner anlegen
- 6 Schritt 2: MainWindow verschieben & umbenennen
- 7 Schritt 3: Start-Prozess der Software anpassen (Bootstrapping)
- 8 Schritt 4: Ein passendes ViewModel erstellen
- 9 Bonus: WPF MVVM Projektvorlage erstellen
Moderne Software durch WPF MVVM Projekte erstellen
Viele mich anschreibende Zuschauer und Kunden haben sich gewünscht, dass ich mal einen Projektaufbau nach Model-View-ViewModel-Manier zeige – von Beginn an und ohne anderes Zeug. Also hier kommt er nun, der Beitrag, wie man ein VB.NET, bzw. ein C# WPF Projekt anhand der MVVM-Struktur aufsetzt und durch ein Projekttemplate sogar wiederverwendbar macht. In erster Linie sind hier die Ordnerstruktur, die Konfiguration des Start-Prozesses und einige kleine / weitere Dinge relevant, aber genug gequasselt – let’s go 🤓!
Kurzfassung – WPF MVVM Projekt aufsetzen
Grundsätzlich kann man die Schritte wie folgt zusammenfassen, allerdings ist dies natürlich nur extrem kukrz gefasst und Bedarf sicherlich einer genaueren Beschreibung. Diese findest Du selbstverständlich weiter unten, wenn Dir die groben Schritte nicht ausreichen sollten – wovon ich ausgehe, da Diese nur einer Art Leitfaden darstellen.
Die Schritte wären an sich wie folgt:
- Erstelle die MVVM-typischen Ordner: Views, ViewModels, Models, Utils (optional)
- Verschiebe das MainWindow über den Visual Studio Projektmappenexplorer in den „Views„-Ordner
- Passe den Namen des MainWindows zu „MainView“ an
- Sorge dafür, dass das MainView in einem passenden Namespace ist – Achtung: Code behind Datei, sowie auch die XAML-Datei
- Setze im MainView einen DataContext (entweder im XAML oder Code behind)
- Passe die StartupUri in Deiner Application.xaml Datei an: „Views/MainView.xaml“
- Implementiere die INotifyPropertyChanged-Schnittstelle in einer Basisklasse – für die Vererbung an ViewModel-Klassen (in den Utils-Ordner damit)
- Erstelle eine passende MainViewModel–Klasse, Welche von Deiner „PropertyChanged“-Implementierung erbt (häufig auch ShellViewModel genannt)
Details zur INotifyPropertyChanged-Schnittstelle
Beachte, dass ich nicht genauer auf das „INotifyPropertyChanged“-Interface eingehen werden, dies habe ich im Beitrag „Der ultimative INotifyPropertyChanged Guide“ getan. Klicke auf den Link, falls Du die Basisklasse oder auch detaillierte Informationen zum Thema benötigst – der Link schickt Dich direkt zur Basisklasse.
Kurzgesagt ist diese Schnittstelle eine Art Vertrag (eine Schnittstelle eben..), damit die „WPF-Engine“ – so nenne ich Sie jetzt einfach mal – weiß: „Hey, wann und wie werden mir Änderungen kommuniziert?“. Es ist daher unsere Aufgabe als Entwickler, Sie korrekt zu implementieren und an den notwendigen Stellen das „PropertyChanged“-Ereignis auszulösen. Somit weiß die Oberfläche dann: „Aha, da ist was passiert, also muss ich aktiv werden“.
Mehr über das MVVM-Entwurfsmuster selbst
Falls Du eventuell völlig neu im Themenbereich MVVM bist, kann ich Dir hier auch meinen Beitrag über das „Model-View-ViewModel-Entwurfsmuster“ empfehlen. Lass Dich hier bitte nicht von der „Spezialisierung“ auf C# verwirren, letztendlich ist hier alles analog und die Erkenntnisse helfen Dir in beiden .NET-Sprachen gleichermaßen.
Schritt 1: MVVM-Ordner anlegen
Wenn Du noch keine passenden Ordner für die üblichen MVVM-Bestandteile erstellt hast, machen wir dies nun zuerst. Verwende dafür die Werkzeuge aus Visual Studio, damit die Ordner auch von Visual Studio selbst korrekt erfasst werden. Führe dazu einfach z. B. einen Rechtsklick auf Deine Projektmappe (nicht auf die Lösung / Solution) aus und wähle „Hinzufügen->Ordner“.
Erstelle nun innerhalb Deiner Projektmappe unter anderem einen Ordner namens „ViewModels„, hier kommen – wie der Name schon sagt – unsere ViewModels rein. Füge dann bitte noch einen weiteren Ordner namens „Views“ hinzu, dort werden wir die grafischen Gegenstücke – also unsere visuellen Bedien-Oberflächen – zu unseren ViewModels ablegen. Dazu könnten übrigens auch kleinere Komponenten, wie z. B. eine Navigationsleiste gehören (ergo NavigationBarView).
Achtung: Wenn Du MVVM-Hilfs-Frameworks wie „Caliburn Micro“ verwendest, könntest Du allerdings ein wenig an Namenskonventionen gebunden sein. Hierüber jedoch auch noch zu berichten, würde bekanntlich den Rahmen des Beitrages sprengen, schätze ich. Verlasse Dich daher am Anfang auf die in diesem Beitrag angepeilte Routine.
Im letzten Schritt benötigen wir noch einen weiteren kleinen „Hilfs-Ordner„, dort packen wir nützliche Helferlein rein, Welche uns den Alltag in MVVM versüßen. Nenne den Ordner daher einfach „Utils“ (Utilities/Hilfsmittel), später kommt dort dann die wiederverwendbare „PropertyChangedBase“-Klasse hinein. Wenn Du auch den Beitrag zur „INotifyPropertyChanged“-Schnittstelle besucht hast, hast Du hier auch schon die notwendige „PropertyChangedBase“-Basisklasse drin. Besuche ansonsten einfach den Link, ziehe Dir den Code (wo Du direkt auskommen solltest) und packe die Klasse in den „Utils“-Ordner.
Unsere finale Struktur sollte also nun wie folgt aussehen:
PropertyChangedBase im „Utils“-Ordner nicht vergessen!
Schritt 2: MainWindow verschieben & umbenennen
Im folgenden Schritt müssen wir auch noch daran denken, das „MainWindow“ MVVM-gemäß umzubenennen und zu verschieben. Dies führt natürlich aufgrund der Standard-Konfiguration des Projektes zu Fehlern. Weder weiß die „Application.xaml“-Datei nach dem Umzug, wo unser Hauptfenster ist, noch, dass es nicht mehr im Root-Namespace ist (im Namensraum unseres Projektes).
Ab in den richtigen Ordner
Schiebe das MainWindow nun in den Ordner namens „Views“, verwende dazu am besten den Drag & Drop-Vorgang im Visual Studio selbst. Wenn Du dies außerhalb machst, kann es zu Problemen führen, da Visual Studio es ggf. nicht korrekt erkennt. Nun befindet sich unsere grafische Oberfläche (potenziell Eine von Vielen) im richtigen Ordner, allerdings fehlt noch was.
Einen MVVM entsprechenden Namen & Namespace
Benenne im nächsten Schritt das „MainWindow“ in „MainView“ um und achte darauf, dass dies auch anschließend korrekt in der ersten Zeile des XAML-Codes widergespiegelt wird. Verwende dazu einfach die F2-Taste, während Du Dein MainWindow im Projektmappen-Explorer ausgewählt (und den Fokus darauf) hast. Passe – wie hier drunter dargestellt – auch gleich den Namespace an, also schreibe das „Views.“-Prefix vor die eigentliche Bezeichnung.
DataContext durch XAML setzen
Beachte hier, dass Du diese beiden Zeilen (mit x:Class & xmlns:vm) separat in Dein Projekt kopierst / Sie abänderst, sonst zerstörst Du ggf. Dein bestehendes Projekt. Dort stehen ja auch noch andere Dinge, wie z. B. die Fenstergröße, usw. Diese habe ich hier bewusst ausgelassen! Den ViewModels Namespace kannst Du aktuell außer acht lassen, bzw. als Fehler drin lassen – darum kümmern wir uns später. Durch die später erstellte Klasse wird der Namespace hier verfügbar – ggf. musst Du hierfür einmal das Projekt debuggen/builder, damit „er“ es kapiert.
Dann setzen wir noch im letzten Schritt den Fenster-Datenkontext via XAML, wir erstellen also eine Instanz der MainViewModel-Klasse via XAML-Code. Dies könntest Du auch – wie gleich angemerkt – via „Code behind“-Datei durchführen.
<Window x:Class="Views.MainView" xmlns:vm="clr-namespace:WpfCommandsVbTutorial.ViewModels"> <Window.DataContext> <vm:MainViewModel /> </Window.DataContext> <!-- restlicher XAML-Code --> </Window>
Diese Änderungen werden wir nun auch im sogenannten „Code behind“-File übernehmen / sicherstellen. Gehe dazu einfach in diese Datei, indem Du die F7-Taste drückst, während Du Dich im XAML-Designer des MainViews (ehemals MainWindows) befindest. Danach springt die Visual Studio IDE via Hotkey in die „Code behind“-Datei und Du solltest folgenden Code einfügen – also das MainView mit einem Namespace umgeben.
Anmerkung zum DataContext
Optional könntest Du die DataContext-Eigenschaft hier festlegen, wir werden dies allerdings im XAML-Code machen, daher entfällt es hier weg, bzw. ist es hier optional. Es geht also an dieser Stelle hier erstmal um den Namespace und die Klasse selbst, sieh‘ Dir aber auch gern an, wie Du auch hier den Datenkontext festlegen könntest.
Mach‘ Dir hier übrigens keine Sorgen über das „MainViewModel“, dabei handelt es sich um eine Klasse, Welche wir gleich noch erstellen werden!
Namespace Views Class MainView '' entfällt durch View-seitige DataContext-Konfiguration 'Sub New() 'InitializeComponent() '' Import bei Bedarf nicht vergessen!! 'DataContext = New MainViewModel() 'End Sub End Class End Namespace
namespace Views { class MainView { //// entfällt durch View-seitige DataContext-Konfiguration //public MainView() //{ //InitializeComponent(); //// Import bei Bedarf nicht vergessen!! //DataContext = new MainViewModel(); //} } }
Schritt 3: Start-Prozess der Software anpassen (Bootstrapping)
Nun müssen wir ein wenig Feintuning betreiben, da das Programm nach unseren Änderungen mehr oder weniger ins Leere laufen würde. Das liegt daran, dass wir das MainWindow erstens umbenannt und zweitens verschoben haben (und denke an die Namespace-Änderung). Nun muss die Zeile mit der „StartupUri“ in der „Application.xaml / App.xaml“-Datei angepasst werden.
Achte bitte darauf, dass ich mein Projekt „WpfCommandsVbTutorial“ genannt hatte und Du dies durch Deinen eigenen Projektnamen ersetzen musst. Dadurch gibst Du praktisch Bescheid: „Hey, hier ist nun das Start-Fenster/View, um die Anwendung zu starten“.
<Application x:Class="Application" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfCommandsVbTutorial" StartupUri="Views/MainView.xaml"> <Application.Resources> </Application.Resources> </Application>
Schritt 4: Ein passendes ViewModel erstellen
Im nächsten und letzten Schritt müssen wir für das oben konfigurierte View nun ein passendes ViewModel erstellen. Das View ist ja wie Du vermutlich weißt (oder gelernt hast) das grafische Gegenstück (ohne Geschäftslogik) zum View. Es mag also im wahrsten Sinne des Wortes „cool“ aussehen, aber letztendlich nichts „können“.
Eine wiederverwendbare Basisklasse
Um uns nicht immer wiederholen zu müssen, machen wir hier Gebrauch von der schon mehrfach erwähnten „PropertyChangedBase“-Basisklasse aus meinem anderen Beitrag. Gehe daher also bitte hin und erstelle diese Klassen-Datei im oben angelegten „Utils“-Ordner. Einen detaillierten Guide findest Du dazu im oben verlinkten Beitrag.
Imports System.ComponentModel Imports System.Runtime.CompilerServices Namespace Utils Public MustInherit Class PropertyChangedBase Implements INotifyPropertyChanged Protected Sub NotifyOfPropertyChange(<CallerMemberName> Optional propertyName As String = Nothing) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged End Class End Namespace
using System.Runtime.CompilerServices; using System.ComponentModel; namespace Utils; public abstract class PropertyChangedBase : INotifyPropertyChanged { protected void NotifyOfPropertyChange([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; }
Das MainViewModel
Nun erstellen wir unser MainViewModel, Welches die aufbereiteten Elemente für das View beinhaltet – darunter auch passende Logik, wie z. B. für einen „Button1_Click“. Warum diese typische Ereignishandler aus Winforms (Windows Forms) Zeiten schon lange nicht mehr modern ist und nicht ins MVVM-Entwurfsmuster passt, erfährst Du in einem passenden, baldigen Beitrag.
Da wir dieses beispielhaft aufgesetzte Projekt hier wie initial erwähnt gleich noch als Projektvorlage anlegen möchten, werde ich hier auch keine großen Eigenschaften realisieren. Das MainViewModel bleibt daher also eher karg und leer. Achte auch hier bitte wieder daran, dass Du oben den korrekten Projektnamen für Deine Anwendung verwendest:
Imports WpfCommandsVbTutorial.Utils Namespace ViewModels Public Class NewMainViewModel Inherits PropertyChangedBase Sub New() End Sub End Class End Namespace
using WpfCommandsVbTutorial.Utils; namespace ViewModels { public class NewMainViewModel : PropertyChangedBase { public NewMainViewModel() { } } }
Bonus: WPF MVVM Projektvorlage erstellen
Als kleines – wie sagt man so schön – Schmankerl, erstellen wir uns aus der gemachten Arbeit nun eine Projektvorlage. Wir möchten ja schließlich vermeiden, diesen ganzen Aufwand für jedes einzelne MVVM-Projekt zu betreiben. Im Endeffekt geht dies nach getaner Arbeit sehr einfach und schnell, mit Hilfe der Visual Studio IDE.
Wenn nun alles nach Deinen Vorstellungen ist, gehe oben im Visual Studio Menü auf „Projekt->Vorlage exportieren“. Dann zeigt sich folgender Dialog und Du kannst ein paar weitere Einstellungen treffen. Wähle im ersten Schritt allerdings einfach nur „Projektvorlage“ – das ist schließlich, was wir wollen, right!?
Wenn Du auf weiter geklickt hast, kommt jetzt der nächste Schritt. Dort kannst Du wohl die beiden essenziellsten Informationen festlegen: Den Namen & die Beschreibung für das Template – wähle hier daher vor allem einen sinnvollen Namen, wie: „<C#> / <VB.NET> MVVM Projekttemplate“, je nachdem für welche Sprache Du Dich ggf. entscheidest. Lege Dir doch auch alternativ für beide Sprachen ein entsprechendes Template an?
💡 Hinweis: Hake hier am besten das Kästchen „Vorlage automatisch in Visual Studio importieren“ an, dann brauchst Du Dich darum nicht manuell kümmern.
Wenn Du nun ein neues Projekt anlegst, findest Du Dein Template ganz einfach wieder: