Eine WPF-Anwendung kann für einen einzelnen Auftraggeber hervorragend funktionieren und trotzdem mehr Wartungsarbeit erzeugen, als ein Freelancer bezahlen kann. Seit 2024 haben sich vor allem die .NET-Laufzeiten und die Möglichkeiten für ein zeitgemäßes Erscheinungsbild verändert. Meine Position: Nicht die modernste Architektur ist heute der beste Standard für Solo-Projekte, sondern eine WPF-App, deren Updates, Tests und Auslieferung eine Person zuverlässig beherrscht.
Ein unterstütztes .NET ist wichtiger als der nächste Architekturumbau
WPF in .NET: Moderne Desktop Apps entwickeln ist ein guter Ausgangspunkt für die Oberfläche; für ein kleines Kundenprojekt würde ich heute jedoch zuerst die Laufzeit und ihren Supportzeitraum festlegen, weil davon Updates und künftige Sicherheitskorrekturen abhängen. Im Oktober 2026 ist .NET 10 die naheliegende Wahl für einen neuen Auftrag: Microsoft führt es als LTS-Version mit Support bis November 2028. .NET 8 erreicht laut Microsoft bereits im November 2026 sein Supportende; ein neues Projekt darauf zu starten verschiebt die nächste Migration deshalb praktisch an den Projektbeginn.
Der ältere Rat „Nimm einfach die .NET-Version, die auf deinem Rechner installiert ist“ ist damit unbrauchbar, weil eine lokale Installation nichts über den Wartungszeitraum beim Kunden sagt. Prüfe vor dem ersten Commit die Zielumgebung: Läuft dort Windows in einer unterstützten Version? Darf die Anwendung eine eigene Laufzeit mitbringen? Wer installiert Updates? WPF bleibt an Windows gebunden; diese Einschränkung ist kein Fehler, solange der Auftrag tatsächlich nur Windows-Arbeitsplätze umfasst.
Ich würde keinen Plattformwechsel allein wegen eines neuen .NET-Releases beginnen, weil ein Wechsel zu einer anderen UI-Technik weder bestehende XAML-Ansichten noch die Bedienabläufe des Kunden automatisch verbessert. Stattdessen würde ich das Projekt auf net10.0-windows ausrichten, NuGet-Pakete gezielt aktualisieren und die zwei oder drei wichtigsten Bedienwege nach jedem Upgrade durchspielen. Auch ein neues SDK ersetzt keine Prüfung auf einem Rechner ohne Entwicklungswerkzeuge: Der eigene PC verbirgt fehlende Dateien und Laufzeitannahmen besonders zuverlässig.
Ein Solo-Projekt braucht dafür keine eigens betriebene CI-Infrastruktur. Git hält den freigegebenen Stand fest; ein reproduzierbarer dotnet publish-Befehl und ein kurzer Testplan reichen zunächst weiter als eine Pipeline, die niemand wartet. Automatisierung lohnt sich, sobald derselbe manuelle Fehler wiederholt auftritt – nicht schon deshalb, weil größere Teams sie ebenfalls verwenden.
Das neue WPF-Design macht einen kompletten Neuanstrich seltener sinnvoll
Ein weiterer veralteter Ratschlag lautet, WPF müsse für einen aktuellen Windows-Eindruck grundsätzlich mit einer umfangreichen eigenen Control-Bibliothek ausgestattet werden. Seit .NET 9 gibt es ein integriertes, optionales Fluent-Theme. Es kann die Ausgangslage verbessern, ohne dass ein Freelancer sofort Buttons, Eingabefelder und Zustände selbst gestalten muss. Das Theme macht eine bestehende Anwendung allerdings nicht von allein konsistent: Eigene Styles, Drittanbieter-Controls und fest codierte Farben müssen weiterhin geprüft werden.
Ich würde deshalb nicht zuerst sämtliche XAML-Ressourcen austauschen. Ein globaler Style-Wechsel kann Kontraste, Fokusmarkierungen oder Dialoggrößen verändern, obwohl die betroffenen Ansichten vorher benutzbar waren. Besser ist eine kleine Probe mit einem typischen Fenster: Tabelle, Formular, Fehlermeldung und Tastaturbedienung. Erst wenn diese Kombination mit dem neuen Theme funktioniert, sollte es in weitere Ansichten wandern.
Der Maßstab ist dabei nicht ein Screenshot, sondern eine erledigte Aufgabe. Setze beispielsweise 3 Sekunden als veränderbaren Zielwert für den Start auf dem Kundenrechner; der Wert ist keine allgemeine WPF-Kennzahl. Miss mit einer Stoppuhr vom Doppelklick bis zur bedienbaren Ansicht und notiere Rechner, Build und Datenbestand. Zeigt eine solche Messung bei dir 4 Sekunden, ist das ein lokaler Messwert und kein Beweis, dass WPF generell langsam startet. Für Ursachen helfen der Visual-Studio-Profiler oder PerfView eher als weitere Design-Pakete, weil beide tatsächliche Laufzeitkosten sichtbar machen.
Zur Oberflächenprüfung gehören außerdem Tastaturfokus und Kontrast. Microsoft Accessibility Insights for Windows und die Windows-UI-Automation-Schnittstelle können konkrete Probleme zeigen, die ein hübsches Theme verdeckt. Plane dafür anfangs 5 feste Bedienfälle als anpassbares Prüfpensum ein: Starten, Daten öffnen, bearbeiten, Fehler anzeigen und per Tastatur speichern. Diese kleine Auswahl ist für eine Person wiederholbar; eine lange Checkliste, die vor jedem Release liegen bleibt, schützt den Kunden nicht.
MVVM spart erst Arbeit, wenn die Trennung klein genug bleibt
WPF MVVM: Saubere Architektur fuer .NET Desktop Apps beschreibt eine sinnvolle Trennung zwischen Ansicht und Verhalten; ich würde daraus aber keine Pflicht zu einer eigenen Infrastruktur für jedes Dialogfenster ableiten, weil deren Pflege bei einem Freelancer direkt von der Lieferzeit abgeht. Der Rat „kein Code-behind unter irgendeinem Umstand“ ist zu streng. Ein Event-Handler, der ein Fenster schließt oder den Fokus setzt, ist oft klarer als ein zusätzlicher Dienst samt Interface, Registrierung und Test-Doubles.
Geschäftsregeln gehören dagegen nicht in Klick-Handler: Sie müssen auch ohne Fenster testbar sein, weil Fehler bei Berechnung oder Speicherung teurer sind als eine unschöne Zeile UI-Code. Für Properties und Commands nimmt CommunityToolkit.Mvvm wiederkehrenden Schreibaufwand ab. Dependency Injection mit Microsoft.Extensions.DependencyInjection kann sinnvoll werden, wenn mehrere ViewModels dieselben Dienste benötigen; für ein Fenster mit einer lokalen Datei ist ein Konstruktor mit expliziten Abhängigkeiten häufig leichter nachzuvollziehen.
Hier hat sich weniger das MVVM-Prinzip geändert als der Preis seiner Umsetzung. Source Generators des MVVM Toolkit erzeugen Benachrichtigungscode aus Attributen; ältere Anleitungen mit großen Mengen handgeschriebenem INotifyPropertyChanged-Boilerplate sind deshalb kein guter Standard mehr. Der generierte Code bleibt allerdings Teil des Builds. Wer Attribute einsetzt, sollte im Team – auch wenn dieses Team nur aus einer Person besteht – verstehen, welche Property und welcher Command daraus entstehen.
Für Daten würde ich bei einem überschaubaren lokalen Bestand zuerst SQLite prüfen, weil eine eingebettete Datenbank keinen separaten Server verlangt. System.Text.Json passt eher zu wenigen, einfachen Einstellungen, weil bei konkurrierenden Änderungen und Abfragen eine einzelne JSON-Datei schnell umständlich wird. Diese Entscheidung gehört hinter eine kleine Schnittstelle, falls sich der Speicher später ändert. Sie rechtfertigt aber keine generische Repository-Schicht über jeder einzelnen Tabelle.
Teste mit NUnit oder MSTest vor allem Regeln und Zustandswechsel ohne WPF-Fenster. Ein Speichern-Command sollte etwa bei ungültigen Eingaben deaktiviert bleiben und nach einem erfolgreichen Speichervorgang den passenden Zustand zeigen. Solche Tests sind auf dem eigenen Rechner schnell ausführbar; GUI-Automation für jeden Klick wäre für diesen Rahmen zu teuer, weil Fensterpositionen und Timing zusätzliche Pflege verlangen. Einen manuellen Test auf dem Zielsystem ersetzen Unit-Tests dennoch nicht.
Die Auslieferung entscheidet, ob eine kleine App klein bleibt
Beim Deployment stehen sich für einen Solo-Freelancer zwei konkrete Optionen gegenüber. MSIX gewinnt, wenn der Kunde Paketinstallation, geregelte Updates und eine vertrauenswürdige Signatur bereitstellen kann; es kostet Vorbereitung bei Zertifikaten, Paketidentität und Verteilung. Ein selbstenthaltendes ZIP mit veröffentlichter EXE gewinnt bei wenigen betreuten Arbeitsplätzen und direkter Übergabe; es kostet dich einen eigenen Update- und Rücknahmeprozess. „Self-contained“ bedeutet, dass die benötigte .NET-Laufzeit mitgeliefert wird, nicht dass Signatur, Berechtigungen oder Updates erledigt sind.
Für eine erste betreute Übergabe würde ich den Publish-Schritt unter Windows in PowerShell so festhalten. Voraussetzung ist das installierte .NET-10-SDK:
dotnet new wpf -n SoloApp -f net10.0 dotnet restore .\SoloApp\SoloApp.csproj dotnet publish .\SoloApp\SoloApp.csproj ` -c Release -r win-x64 --self-contained true ` -p:PublishSingleFile=true -p:PublishReadyToRun=false ` -o .\dist
Der Befehl erzeugt einen Release-Build für 64-Bit-Windows. PublishSingleFile=true vereinfacht die Übergabe, garantiert aber nicht, dass im Ausgabeverzeichnis ausschließlich eine Datei liegt. Liefere nur die tatsächlich benötigten Artefakte aus dist aus und teste genau diese Dateien auf einem frischen Windows-System ohne Visual Studio. So fällt auf, ob eine lokale Konfigurationsdatei, ein Schreibrecht oder eine externe Abhängigkeit fehlt.
Ich würde keine unsignierte EXE öffentlich zum Download anbieten, weil fehlende Signatur und Reputation bei Windows SmartScreen Misstrauen und Warnungen auslösen können. Auch eine Authenticode-Signatur garantiert keine warnungsfreie Installation; sie macht aber den Herausgeber prüfbar und schützt die Integrität der signierten Datei. Reicht das Budget nicht für einen belastbaren öffentlichen Vertriebsweg, ist die betreute Übergabe an einen bekannten Auftraggeber ehrlicher als ein halb gepflegtes Download-Portal.
Halte für jede Übergabe Version, Datum, SHA-256-Prüfsumme per PowerShell Get-FileHash und einen Rückweg zur vorherigen Ausgabe fest. Bewahre keine Kundendaten im Programmordner auf, weil ein Austausch des Ordners sonst zugleich zum Datenverlust führen kann. Ein Update ist erst abgeschlossen, wenn die neue Version mit vorhandenen Daten startet und sich die vorige Version bei Bedarf wiederherstellen lässt. Diese Prüfung kostet Zeit, aber erheblich weniger als eine ungeplante Fernwartung nach Feierabend.
Der erste nächste Schritt ist ein Publish auf einem sauberen Windows-System
Lege heute eine kleine Testkopie deiner App an, veröffentliche sie mit einem dokumentierten Befehl und starte ausschließlich das Ergebnis auf einem Windows-System ohne Entwicklungsumgebung. Notiere jeden fehlenden Schritt bis zur benutzbaren Ansicht. Diese Liste bestimmt, ob du als Nächstes am Theme, an Tests oder an der Verteilung arbeitest – statt Zeit in eine Architektur zu stecken, deren dringendstes Problem niemand gemessen hat.


