Skip to content

JanJanssen-dev/MVVM_CommunityToolkit_Vorlage

Repository files navigation

WPF MVVM Template - Schritt-für-Schritt-Anleitung

Voraussetzungen

  • Visual Studio (2019 oder neuer)
  • .NET 6.0 oder höher
  • Grundlegende Kenntnisse in C# und WPF

Schritt 1: Projekt erstellen

  1. Öffnen Sie Visual Studio
  2. Klicken Sie auf "Neues Projekt erstellen"
  3. Wählen Sie "WPF-Anwendung" aus
  4. Geben Sie einen Projektnamen ein (z.B. "MyWpfApp")
  5. Wählen Sie den gewünschten Speicherort
  6. Wählen Sie .NET 6.0 (oder höher) als Framework

Schritt 2: NuGet-Package installieren

  1. Klicken Sie mit der rechten Maustaste auf das Projekt im Solution Explorer
  2. Wählen Sie "NuGet-Pakete verwalten"
  3. Wechseln Sie zum Tab "Durchsuchen"
  4. Suchen Sie nach "CommunityToolkit.Mvvm"
  5. Installieren Sie das Paket
    • Alternativ können Sie auch die Package Manager Console öffnen und eingeben:
    Install-Package CommunityToolkit.Mvvm
    

Schritt 3: Projektstruktur erstellen

Erstellen Sie folgende Ordner im Projekt durch Rechtsklick auf das Projekt → Hinzufügen → Neuer Ordner:

  1. Models
  2. ViewModels
  3. Views
  4. Services

Schritt 4: Model erstellen

  1. Rechtsklick auf den Models-Ordner → Hinzufügen → Klasse
  2. Nennen Sie die Datei "Person.cs"
  3. Fügen Sie folgenden Code ein:
using CommunityToolkit.Mvvm.ComponentModel;

namespace MyWpfApp.Models;

public partial class Person : ObservableObject
{
    [ObservableProperty]
    private string name;

    [ObservableProperty]
    private int age;

    public Person(string name, int age)
    {
        this.name = name;
        this.age = age;
    }
}

Schritt 5: ViewModelBase erstellen

  1. Rechtsklick auf den ViewModels-Ordner → Hinzufügen → Klasse
  2. Nennen Sie die Datei "ViewModelBase.cs"
  3. Fügen Sie folgenden Code ein:
using CommunityToolkit.Mvvm.ComponentModel;

namespace MyWpfApp.ViewModels;

public abstract class ViewModelBase : ObservableObject
{
    public virtual void Initialize() { }
}

Schritt 6: MainViewModel erstellen

  1. Rechtsklick auf den ViewModels-Ordner → Hinzufügen → Klasse
  2. Nennen Sie die Datei "MainViewModel.cs"
  3. Fügen Sie folgenden Code ein:
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MyWpfApp.Models;
using System.Collections.ObjectModel;

namespace MyWpfApp.ViewModels;

public partial class MainViewModel : ViewModelBase
{
    [ObservableProperty]
    private ObservableCollection<Person> people;

    [ObservableProperty]
    private Person selectedPerson;

    [RelayCommand]
    private void AddPerson()
    {
        People.Add(new Person("Neue Person", 30));
    }

    public MainViewModel()
    {
        People = new ObservableCollection<Person>();
        Initialize();
    }

    public override void Initialize()
    {
        People.Add(new Person("Max Mustermann", 25));
        People.Add(new Person("Erika Musterfrau", 30));
    }
}

Schritt 7: MainWindow.xaml anpassen

  1. Öffnen Sie die MainWindow.xaml
  2. Ersetzen Sie den gesamten Inhalt durch:
<Window x:Class="MyWpfApp.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:vm="clr-namespace:MyWpfApp.ViewModels"
        mc:Ignorable="d"
        Title="MVVM Template" Height="450" Width="800">
    
    <Window.DataContext>
        <vm:MainViewModel/>
    </Window.DataContext>

    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Button Content="Person hinzufügen" 
                Command="{Binding AddPersonCommand}"
                Margin="0,0,0,10"/>

        <ListView Grid.Row="1" 
                  ItemsSource="{Binding People}"
                  SelectedItem="{Binding SelectedPerson}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" Margin="0,0,10,0"/>
                        <TextBlock Text="{Binding Age}"/>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Window>

Schritt 8: App.xaml anpassen

  1. Öffnen Sie die App.xaml
  2. Stellen Sie sicher, dass der StartupUri-Pfad korrekt ist:
<Application x:Class="MyWpfApp.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="Views/MainWindow.xaml">
    <Application.Resources>
    </Application.Resources>
</Application>

Schritt 9: Testen

  1. Kompilieren Sie das Projekt (F6)
  2. Starten Sie die Anwendung (F5)
  3. Testen Sie den "Person hinzufügen" Button
  4. Überprüfen Sie, ob die Liste aktualisiert wird

MVVM Toolkit Funktionen im Detail

ObservableProperty - Die moderne Art der Property-Implementierung

Traditionelle Implementierung (früher)

// Früher musste man INotifyPropertyChanged manuell implementieren
public class PersonOld : INotifyPropertyChanged
{
    private string name;
    public string Name
    {
        get => name;
        set
        {
            if (name != value)
            {
                name = value;
                OnPropertyChanged(nameof(Name));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Moderne Implementierung mit dem Toolkit

// Mit dem Toolkit - viel weniger Code, gleiche Funktionalität
public partial class Person : ObservableObject  // partial ist wichtig für Source Generator
{
    [ObservableProperty]  // Generiert die komplette Property-Implementation
    private string name;  // Der Generator erstellt eine öffentliche 'Name' Property
}

Der Source Generator erzeugt im Hintergrund den gleichen Code wie in der traditionellen Implementierung. Die Vorteile sind:

  • Weniger Boilerplate-Code
  • Geringere Fehleranfälligkeit
  • Bessere Lesbarkeit
  • Automatische Implementierung von INotifyPropertyChanged

RelayCommand - Vereinfachte Command-Implementierung

Traditionelle Implementierung (früher)

public class MainViewModelOld : INotifyPropertyChanged
{
    private ICommand addPersonCommand;
    public ICommand AddPersonCommand
    {
        get
        {
            return addPersonCommand ?? (addPersonCommand = new RelayCommand(
                param => AddPerson(),
                param => CanAddPerson()
            ));
        }
    }

    private bool CanAddPerson()
    {
        return true; // Ihre Logik hier
    }

    private void AddPerson()
    {
        // Implementierung
    }
}

Moderne Implementierung mit dem Toolkit

public partial class MainViewModel : ObservableObject
{
    [RelayCommand(CanExecute = nameof(CanAddPerson))]  // Generiert Command mit Can-Execute
    private void AddPerson()
    {
        // Implementierung
    }

    private bool CanAddPerson()
    {
        return true; // Ihre Logik hier
    }
}

Vorteile:

  • Der Generator erstellt automatisch eine öffentliche AddPersonCommand Property
  • Automatische Implementation von ICommand
  • Eingebaute Unterstützung für asynchrone Commands
  • Automatische Invalidierung von CanExecute

Weitere wichtige Toolkit-Funktionen

1. ObservableObject als Basisklasse

// Basisklasse für alle ViewModels
public abstract class ViewModelBase : ObservableObject
{
    // Erbt bereits INotifyPropertyChanged
    // Bietet SetProperty und OnPropertyChanged Methoden
}

2. ObservableValidator für Validierung

public partial class Person : ObservableValidator
{
    [ObservableProperty]
    [NotifyDataErrorInfo]  // Aktiviert Validierung für diese Property
    [Required(ErrorMessage = "Name ist erforderlich")]  // Validierungsregel
    [MinLength(2, ErrorMessage = "Name muss mindestens 2 Zeichen lang sein")]
    private string name;
}

3. Messenger für MVVM-Kommunikation

// Nachricht definieren
public record PersonUpdatedMessage(Person UpdatedPerson);

// Sender
[RelayCommand]
private void UpdatePerson()
{
    // Sender muss nicht wissen, wer die Nachricht empfängt
    WeakReferenceMessenger.Default.Send(new PersonUpdatedMessage(currentPerson));
}

// Empfänger
public MainViewModel()
{
    // Registrierung für Nachrichten
    WeakReferenceMessenger.Default.Register<PersonUpdatedMessage>(this, (r, m) =>
    {
        // Nachricht verarbeiten
        UpdatePersonInList(m.UpdatedPerson);
    });
}

4. Asynchrone Commands

[RelayCommand(CanExecute = nameof(CanLoadData))]
private async Task LoadDataAsync()
{
    try
    {
        // Asynchrone Operation
        var data = await dataService.LoadDataAsync();
        People = new ObservableCollection<Person>(data);
    }
    catch (Exception ex)
    {
        // Fehlerbehandlung
    }
}

5. Property-Abhängigkeiten

public partial class PersonViewModel : ObservableObject
{
    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(FullName))]  // Aktualisiert FullName wenn sich FirstName ändert
    private string firstName;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(FullName))]
    private string lastName;

    public string FullName => $"{FirstName} {LastName}";
}

Best Practices für die Toolkit-Nutzung

  1. Konsistente Namensgebung

    • Private Felder in lowerCamelCase
    • Öffentliche Properties in UpperCamelCase
    • Commands enden immer mit "Command"
  2. Strukturierung

    • Gruppieren Sie zusammengehörige Properties und Commands
    • Nutzen Sie Regionen für bessere Übersicht
    • Dokumentieren Sie komplexe Abhängigkeiten
  3. Performance

    • Vermeiden Sie unnötige Property-Updates
    • Nutzen Sie WeakReference für Events und Messenger
    • Berücksichtigen Sie UI-Thread-Synchronisation
  4. Fehlerbehandlung

    • Implementieren Sie Try-Catch in Commands
    • Nutzen Sie das Validation-Framework
    • Loggen Sie Fehler für Debugging

Häufige Fehler und Lösungen

  1. Compiler-Fehler bei [ObservableProperty]

    • Prüfen Sie, ob die Klasse als partial deklariert ist
    • Prüfen Sie, ob das CommunityToolkit.Mvvm NuGet-Paket installiert ist
  2. Property wird nicht in UI aktualisiert

    • Prüfen Sie, ob die Property das [ObservableProperty] Attribut hat
    • Prüfen Sie das Output-Fenster auf Binding-Fehler
  3. Command funktioniert nicht

    • Prüfen Sie, ob der Command-Name im Binding mit "Command" endet
    • Beispiel: Methode AddPerson wird zu AddPersonCommand

Nächste Schritte

Nach erfolgreichem Aufsetzen des Templates können Sie:

  1. Weitere Models hinzufügen
  2. Neue ViewModels für verschiedene Ansichten erstellen
  3. Services für Datenzugriff implementieren
  4. Die UI mit weiteren Controls erweitern

Erweiterung des Projekts

Neues Fenster hinzufügen

  1. View erstellen
    • Rechtsklick auf den Views-Ordner → Hinzufügen → Fenster
    • Nennen Sie es z.B. "DetailWindow.xaml"
    • Grundlegendes XAML-Template:
<Window x:Class="MyWpfApp.Views.DetailWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:vm="clr-namespace:MyWpfApp.ViewModels"
        mc:Ignorable="d"
        Title="Detail Ansicht" Height="300" Width="400">
    
    <Window.DataContext>
        <vm:DetailViewModel/>
    </Window.DataContext>

    <Grid Margin="10">
        <!-- Hier Ihr Content -->
    </Grid>
</Window>
  1. ViewModel erstellen
    • Rechtsklick auf den ViewModels-Ordner → Hinzufügen → Klasse
    • Nennen Sie es "DetailViewModel.cs"
    • Grundlegendes Code-Template:
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace MyWpfApp.ViewModels;

public partial class DetailViewModel : ViewModelBase
{
    [ObservableProperty]
    private Person currentPerson;

    public DetailViewModel()
    {
        Initialize();
    }

    public override void Initialize()
    {
        // Initialisierungscode hier
    }
}

Neue Commands hinzufügen

  1. Einfache Commands
    • Fügen Sie im ViewModel eine neue Methode mit dem [RelayCommand] Attribut hinzu:
[RelayCommand]
private void SavePerson()
{
    // Speicherlogik hier
}
  • Das Command kann dann im XAML verwendet werden:
<Button Content="Speichern" Command="{Binding SavePersonCommand}"/>
  1. Commands mit Parametern
[RelayCommand]
private void EditPerson(Person person)
{
    var detailWindow = new DetailWindow
    {
        DataContext = new DetailViewModel { CurrentPerson = person }
    };
    detailWindow.ShowDialog();
}
  • Verwendung im XAML mit CommandParameter:
<Button Content="Bearbeiten" 
        Command="{Binding EditPersonCommand}" 
        CommandParameter="{Binding SelectedPerson}"/>
  1. Commands mit Can-Execute
[RelayCommand(CanExecute = nameof(CanSavePerson))]
private void SavePerson()
{
    // Speicherlogik hier
}

private bool CanSavePerson()
{
    return CurrentPerson != null && !string.IsNullOrEmpty(CurrentPerson.Name);
}

Fenster öffnen und Daten übergeben

  1. Fenster aus Command öffnen
[RelayCommand]
private void OpenDetail()
{
    var detailViewModel = new DetailViewModel();
    var detailWindow = new DetailWindow
    {
        DataContext = detailViewModel
    };
    
    if (SelectedPerson != null)
    {
        detailViewModel.CurrentPerson = SelectedPerson;
    }
    
    detailWindow.ShowDialog(); // Modal
    // oder
    detailWindow.Show(); // Non-Modal
}
  1. Daten zwischen ViewModels austauschen
// Event für Datenaustausch definieren
public event Action<Person> PersonUpdated;

[RelayCommand]
private void SavePerson()
{
    // Änderungen speichern
    PersonUpdated?.Invoke(CurrentPerson);
    // Fenster schließen
    Application.Current.Windows
        .OfType<Window>()
        .SingleOrDefault(x => x.DataContext == this)
        ?.Close();
}

Beispiel: Komplettes Detail-Fenster

  1. DetailWindow.xaml
<Window x:Class="MyWpfApp.Views.DetailWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:vm="clr-namespace:MyWpfApp.ViewModels"
        mc:Ignorable="d"
        Title="Person bearbeiten" Height="300" Width="400">
    
    <Grid Margin="20">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        
        <TextBox Text="{Binding CurrentPerson.Name, UpdateSourceTrigger=PropertyChanged}"
                 Margin="0,0,0,10"/>
                 
        <TextBox Grid.Row="1" 
                 Text="{Binding CurrentPerson.Age, UpdateSourceTrigger=PropertyChanged}"
                 Margin="0,0,0,10"/>
        
        <StackPanel Grid.Row="3" 
                    Orientation="Horizontal" 
                    HorizontalAlignment="Right">
            <Button Content="Speichern" 
                    Command="{Binding SavePersonCommand}"
                    Margin="0,0,10,0"/>
            <Button Content="Abbrechen" 
                    Command="{Binding CancelCommand}"/>
        </StackPanel>
    </Grid>
</Window>
  1. DetailViewModel.cs
public partial class DetailViewModel : ViewModelBase
{
    [ObservableProperty]
    private Person currentPerson;

    public event Action<Person> PersonUpdated;

    [RelayCommand]
    private void SavePerson()
    {
        if (CurrentPerson != null)
        {
            PersonUpdated?.Invoke(CurrentPerson);
            CloseWindow();
        }
    }

    [RelayCommand]
    private void Cancel()
    {
        CloseWindow();
    }

    private void CloseWindow()
    {
        Application.Current.Windows
            .OfType<Window>()
            .SingleOrDefault(x => x.DataContext == this)
            ?.Close();
    }
}

Projekt als Visual Studio Template exportieren

Vorbereitung des Projekts

  1. Projekt bereinigen

    • Führen Sie einen Clean (Bereinigen) des Projekts durch
    • Löschen Sie alle bin und obj Ordner
    • Entfernen Sie nicht benötigte Dateien
  2. Template-Parameter vorbereiten

    • Öffnen Sie die AssemblyInfo.cs und ändern Sie die Assembly-Attribute
    • Ersetzen Sie projektspezifische Namen durch Parameter:
      • $safeprojectname$ (wird durch den Projektnamen ersetzt)
      • $safesolutionname$ (wird durch den Lösungsnamen ersetzt)
      • Beispiel: Ändern Sie "MyWpfApp" zu "$safeprojectname$" in Namespaces

Export als Template

  1. Projekt exportieren

    • Öffnen Sie Visual Studio 2022
    • Klicken Sie auf "Projekt" → "Vorlage exportieren..."
    • Oder in Deutsch: "Projekt" → "Vorlage exportieren..."
  2. Export-Assistenten durchlaufen

    • Wählen Sie "Projektvorlage"
    • Wählen Sie Ihr Projekt aus der Liste
    • Geben Sie Template-Informationen ein:
      • Name: "WPF MVVM Template"
      • Beschreibung: "MVVM Template mit Community Toolkit"
      • Icon (optional)
      • Vorschaubild (optional)
    • Wählen Sie "Automatisch importieren"

Template installieren

Das Template wird automatisch in folgenden Ordner installiert:

%USERPROFILE%\Documents\Visual Studio 2022\Templates\ProjectTemplates

Template verwenden

  1. Neues Projekt erstellen

    • Öffnen Sie Visual Studio 2022
    • Klicken Sie auf "Neues Projekt erstellen"
    • Suchen Sie nach "WPF MVVM Template"
    • Oder filtern Sie nach "MVVM" in der Projektvorlagensuche
  2. Projekt konfigurieren

    • Geben Sie einen Projektnamen ein
    • Wählen Sie Speicherort
    • Wählen Sie die Framework-Version
    • Klicken Sie auf "Erstellen"

Template anpassen

Sie können das Template später anpassen:

  1. Template-Dateien finden

    • Navigieren Sie zu: %USERPROFILE%\Documents\Visual Studio 2022\Templates\ProjectTemplates
    • Finden Sie Ihre .zip Template-Datei
  2. Template bearbeiten

    • Erstellen Sie eine Kopie der .zip Datei
    • Entpacken Sie die Kopie
    • Bearbeiten Sie die Dateien
    • Erstellen Sie eine neue .zip Datei
    • Ersetzen Sie die alte .zip im Templates-Ordner

Tipps für Template-Erstellung

  1. Parametrisierung Sie können folgende Parameter in Ihren Dateien verwenden:

    • $safeprojectname$ - Projektname
    • $safesolutionname$ - Lösungsname
    • $username$ - Aktueller Benutzername
    • $userdomain$ - Domäne des Benutzers
    • $guid1$ - Wird durch eine neue GUID ersetzt
    • $time$ - Aktuelle Uhrzeit
    • $year$ - Aktuelles Jahr
  2. VSTemplate-Datei Die .vstemplate Datei steuert das Template-Verhalten:

    <?xml version="1.0" encoding="utf-8"?>
    <VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
      <TemplateData>
        <Name>WPF MVVM Template</Name>
        <Description>MVVM Template mit Community Toolkit</Description>
        <ProjectType>CSharp</ProjectType>
        <LanguageTag>C#</LanguageTag>
        <PlatformTag>Windows</PlatformTag>
        <ProjectTypeTag>Desktop</ProjectTypeTag>
        <ProjectTypeTag>MVVM</ProjectTypeTag>
      </TemplateData>
      <TemplateContent>
        <Project File="MyWpfApp.csproj" ReplaceParameters="true">
          <!-- Projektdateien hier -->
        </Project>
      </TemplateContent>
    </VSTemplate>

Gemeinsame Nutzung des Templates

  1. Template weitergeben

    • Speichern Sie die .zip Datei
    • Andere Entwickler können sie in ihren Templates-Ordner kopieren
  2. Team-Nutzung

    • Legen Sie das Template in einem geteilten Netzwerkordner ab
    • Oder fügen Sie es Ihrer Quellcodeverwaltung hinzu
    • Dokumentieren Sie die Installation und Verwendung

Template aktualisieren

  1. Änderungen vornehmen

    • Erstellen Sie ein neues Projekt aus dem Template
    • Nehmen Sie Ihre Änderungen vor
    • Exportieren Sie das Projekt erneut als Template
    • Ersetzen Sie die alte Version im Templates-Ordner
  2. Version kontrollieren

    • Fügen Sie eine Versionsnummer zur Template-Beschreibung hinzu
    • Dokumentieren Sie Änderungen
    • Informieren Sie Ihr Team über Updates

Template wiederverwenden

Um das Template für neue Projekte zu verwenden:

  1. Öffnen Sie Visual Studio 2022
  2. Wählen Sie "Neues Projekt erstellen"
  3. Suchen Sie nach Ihrem Template
  4. Folgen Sie dem Projekt-Erstellungs-Assistenten

About

Beispiel Anleitung zum erstellen eines TemplateProjektes für MVVM mit VS 2022 in einem WPF Projekt.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages