Softwarearchitektur und UML
Der Dialog funktioniert – noch. Im Code-Behind stehen zweitausend Zeilen: Klick-Handler lesen Textfelder aus, rechnen, schreiben in die Zeichnung und schalten Knöpfe aktiv oder inaktiv. Jede neue Anforderung macht ihn fragiler, und testen lässt er sich nur, indem jemand klickt. MVVM räumt hier auf.
Drei Rollen
- View: die Oberfläche in XAML. Sie zeigt an und nimmt Eingaben entgegen. Das ViewModel kennt sie nur über Bindungen.
- ViewModel: Zustand und Befehle der Oberfläche als normale C#-Klasse – ohne Bezug zu Fenstern, Textfeldern oder Farben.
- Model: Fachlogik und Daten, hinter Schnittstellen auch der Zugriff auf Datenbank, Geräte oder CAD.
Die Verbindung leistet die Datenbindung von WPF. Ändert sich eine Eigenschaft im ViewModel, meldet es das über INotifyPropertyChanged, und die Oberfläche zieht nach. Ein Knopf löst kein Click-Ereignis mehr aus, sondern einen Befehl, ein ICommand. Der Gewinn: Das ViewModel lässt sich ohne Oberfläche testen, und die Oberfläche lässt sich umbauen, ohne die Logik anzufassen.
Das ViewModel
Ein kleines Beispiel: Der Anwender gibt eine Breite ein, und der Knopf „Zeichnen“ ist nur bei gültiger Eingabe aktiv.
public class ParameterViewModel : INotifyPropertyChanged
{
private readonly IZeichnungsDienst _zeichnung;
private string _breite = "100";
public ParameterViewModel(IZeichnungsDienst zeichnung)
{
_zeichnung = zeichnung;
ZeichnenBefehl = new RelayCommand(Zeichne, () => IstGültig);
}
public event PropertyChangedEventHandler PropertyChanged;
public string Breite
{
get => _breite;
set
{
if (_breite != value)
{
_breite = value;
Melde(nameof(Breite));
ZeichnenBefehl.MeldeÄnderung(); // Knopf neu bewerten
}
}
}
public bool IstGültig => double.TryParse(Breite, out var wert) && wert > 0;
public RelayCommand ZeichnenBefehl { get; }
private void Zeichne() => _zeichnung.ZeichneRechteck(double.Parse(Breite));
private void Melde(string name) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public class RelayCommand : ICommand
{
private readonly Action _ausführen;
private readonly Func<bool> _darfAusführen;
public RelayCommand(Action ausführen, Func<bool> darfAusführen = null)
{
_ausführen = ausführen;
_darfAusführen = darfAusführen;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => _darfAusführen?.Invoke() ?? true;
public void Execute(object parameter) => _ausführen();
public void MeldeÄnderung() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
Den RelayCommand schreibt man einmal und verwendet ihn überall. Wer sich die Wiederholung sparen will, nimmt das Paket CommunityToolkit.Mvvm: Mit den Attributen [ObservableProperty] und [RelayCommand] erzeugt ein Source-Generator diesen Code beim Übersetzen. Das Paket läuft auch unter dem .NET Framework 4.8 und damit in Add-ins für ältere AutoCAD-Versionen.
Die View bindet nur
<StackPanel Margin="12">
<Label Content="Breite (mm)" Target="{Binding ElementName=BreiteFeld}" />
<TextBox x:Name="BreiteFeld"
Text="{Binding Breite, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="Zeichnen" Command="{Binding ZeichnenBefehl}" Margin="0,8,0,0" />
</StackPanel>
Im Code-Behind bleibt eine Zeile: DataContext = new ParameterViewModel(dienst);. UpdateSourceTrigger=PropertyChanged sorgt dafür, dass der Knopf schon beim Tippen reagiert und nicht erst, wenn der Anwender das Feld verlässt.
MVVM im CAD-Add-in
Besonders lohnt sich MVVM in Paletten und Dialogen von CAD-Add-ins. Das ViewModel ruft dort nicht die AutoCAD-API auf, sondern eine Schnittstelle wie IZeichnungsDienst. Erst deren Umsetzung arbeitet mit AutoCAD und kümmert sich um das, was dort gilt:
- Eine Palette lebt länger als jede Zeichnung. Die Umsetzung holt das aktive Dokument deshalb bei jedem Aufruf frisch über
MdiActiveDocumentund prüft es aufnull. - Klicks laufen außerhalb jedes Befehls, im Anwendungskontext. Wer die Zeichnung direkt ändert, sperrt sie mit
LockDocument, sonst gibt esErrorStatus.LockViolation. Oft besser ist es, einen Befehl mitSendStringToExecutezu starten. Er erscheint in der Kommandozeile, hat einen eigenen Undo-Schritt und lässt sich wiederholen.
Im Unit-Test bekommt das ViewModel eine Attrappe der Schnittstelle. So prüfen Sie Eingaben, Befehle und Zustände in Millisekunden, ohne dass AutoCAD läuft. Die Trennung hilft auch beim Wechsel der Plattform: Mit einer anderen Umsetzung des Dienstes lässt sich dasselbe ViewModel in BricsCAD oder Revit weiterverwenden.
Wie weit man gehen sollte
MVVM ist ein Werkzeug, kein Dogma. Code, der nur die Darstellung betrifft – den Fokus setzen, eine Animation starten –, darf im Code-Behind bleiben. Meldungsfenster und Dateidialoge ruft das ViewModel über einen eigenen Dienst auf, damit Tests nicht an einem Fenster hängen bleiben. Und für Eingabeprüfungen, die direkt am Feld erscheinen sollen, gibt es INotifyDataErrorInfo.
Typische Fehler
- Das ViewModel kennt Steuerelemente, Farben oder
MessageBox. Dann ist es nicht mehr ohne Oberfläche testbar. PropertyChangedvergessen oder mit einem falsch geschriebenen Namen auslösen.nameofhilft.CanExecuteChangednie auslösen. Der Knopf bleibt grau, obwohl die Eingabe stimmt.- Listen als
List<T>statt alsObservableCollection<T>binden – neue Einträge erscheinen nicht. - Im CAD-Add-in eine Zeichnung im ViewModel zwischenspeichern, die längst geschlossen ist.
- Bindungsfehler übersehen. Sie erscheinen nur im Ausgabefenster von Visual Studio, nicht als Ausnahme.
So helfen wir
- Schulung: Die WPF-Schulung führt von XAML über Datenbindung bis MVVM. Muster und Architektur vertieft die Schulung „C# & .NET (Fortgeschrittene)“.
- Modernisierung: Wir bauen gewachsene WinForms- oder WPF-Oberflächen schrittweise auf MVVM um, auch in CAD-Add-ins: Benutzeroberflächen bei Cadida.
- Ihr Projekt: Vereinbaren Sie ein Erstgespräch.
Quellen: Ebi F. Eilber, AutoCAD .NET API (Fachbuch, erscheint voraussichtlich 2027), Kapitel „GUI-Programmierung“; Microsoft Learn: Datenbindung in WPF; Microsoft Learn: MVVM Toolkit.
