- Visual Studio (2019 oder neuer)
- .NET 6.0 oder höher
- Grundlegende Kenntnisse in C# und WPF
- Öffnen Sie Visual Studio
- Klicken Sie auf "Neues Projekt erstellen"
- Wählen Sie "WPF-Anwendung" aus
- Geben Sie einen Projektnamen ein (z.B. "MyWpfApp")
- Wählen Sie den gewünschten Speicherort
- Wählen Sie .NET 6.0 (oder höher) als Framework
- Klicken Sie mit der rechten Maustaste auf das Projekt im Solution Explorer
- Wählen Sie "NuGet-Pakete verwalten"
- Wechseln Sie zum Tab "Durchsuchen"
- Suchen Sie nach "CommunityToolkit.Mvvm"
- Installieren Sie das Paket
- Alternativ können Sie auch die Package Manager Console öffnen und eingeben:
Install-Package CommunityToolkit.Mvvm
Erstellen Sie folgende Ordner im Projekt durch Rechtsklick auf das Projekt → Hinzufügen → Neuer Ordner:
- Models
- ViewModels
- Views
- Services
- Rechtsklick auf den Models-Ordner → Hinzufügen → Klasse
- Nennen Sie die Datei "Person.cs"
- 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;
}
}
- Rechtsklick auf den ViewModels-Ordner → Hinzufügen → Klasse
- Nennen Sie die Datei "ViewModelBase.cs"
- Fügen Sie folgenden Code ein:
using CommunityToolkit.Mvvm.ComponentModel;
namespace MyWpfApp.ViewModels;
public abstract class ViewModelBase : ObservableObject
{
public virtual void Initialize() { }
}
- Rechtsklick auf den ViewModels-Ordner → Hinzufügen → Klasse
- Nennen Sie die Datei "MainViewModel.cs"
- 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));
}
}
- Öffnen Sie die MainWindow.xaml
- 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>
- Öffnen Sie die App.xaml
- 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>
- Kompilieren Sie das Projekt (F6)
- Starten Sie die Anwendung (F5)
- Testen Sie den "Person hinzufügen" Button
- Überprüfen Sie, ob die Liste aktualisiert wird
// 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));
}
}
// 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
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
}
}
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
// Basisklasse für alle ViewModels
public abstract class ViewModelBase : ObservableObject
{
// Erbt bereits INotifyPropertyChanged
// Bietet SetProperty und OnPropertyChanged Methoden
}
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;
}
// 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);
});
}
[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
}
}
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}";
}
-
Konsistente Namensgebung
- Private Felder in lowerCamelCase
- Öffentliche Properties in UpperCamelCase
- Commands enden immer mit "Command"
-
Strukturierung
- Gruppieren Sie zusammengehörige Properties und Commands
- Nutzen Sie Regionen für bessere Übersicht
- Dokumentieren Sie komplexe Abhängigkeiten
-
Performance
- Vermeiden Sie unnötige Property-Updates
- Nutzen Sie WeakReference für Events und Messenger
- Berücksichtigen Sie UI-Thread-Synchronisation
-
Fehlerbehandlung
- Implementieren Sie Try-Catch in Commands
- Nutzen Sie das Validation-Framework
- Loggen Sie Fehler für Debugging
-
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
- Prüfen Sie, ob die Klasse als
-
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
-
Command funktioniert nicht
- Prüfen Sie, ob der Command-Name im Binding mit "Command" endet
- Beispiel: Methode
AddPerson
wird zuAddPersonCommand
Nach erfolgreichem Aufsetzen des Templates können Sie:
- Weitere Models hinzufügen
- Neue ViewModels für verschiedene Ansichten erstellen
- Services für Datenzugriff implementieren
- Die UI mit weiteren Controls erweitern
- 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>
- 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
}
}
- 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}"/>
- 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}"/>
- Commands mit Can-Execute
[RelayCommand(CanExecute = nameof(CanSavePerson))]
private void SavePerson()
{
// Speicherlogik hier
}
private bool CanSavePerson()
{
return CurrentPerson != null && !string.IsNullOrEmpty(CurrentPerson.Name);
}
- 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
}
- 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();
}
- 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>
- 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 bereinigen
- Führen Sie einen Clean (Bereinigen) des Projekts durch
- Löschen Sie alle bin und obj Ordner
- Entfernen Sie nicht benötigte Dateien
-
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
-
-
Projekt exportieren
- Öffnen Sie Visual Studio 2022
- Klicken Sie auf "Projekt" → "Vorlage exportieren..."
- Oder in Deutsch: "Projekt" → "Vorlage exportieren..."
-
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"
Das Template wird automatisch in folgenden Ordner installiert:
%USERPROFILE%\Documents\Visual Studio 2022\Templates\ProjectTemplates
-
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
-
Projekt konfigurieren
- Geben Sie einen Projektnamen ein
- Wählen Sie Speicherort
- Wählen Sie die Framework-Version
- Klicken Sie auf "Erstellen"
Sie können das Template später anpassen:
-
Template-Dateien finden
- Navigieren Sie zu: %USERPROFILE%\Documents\Visual Studio 2022\Templates\ProjectTemplates
- Finden Sie Ihre .zip Template-Datei
-
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
-
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
-
-
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>
-
Template weitergeben
- Speichern Sie die .zip Datei
- Andere Entwickler können sie in ihren Templates-Ordner kopieren
-
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
-
Ä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
-
Version kontrollieren
- Fügen Sie eine Versionsnummer zur Template-Beschreibung hinzu
- Dokumentieren Sie Änderungen
- Informieren Sie Ihr Team über Updates
Um das Template für neue Projekte zu verwenden:
- Öffnen Sie Visual Studio 2022
- Wählen Sie "Neues Projekt erstellen"
- Suchen Sie nach Ihrem Template
- Folgen Sie dem Projekt-Erstellungs-Assistenten