.NET WPF

Solo mit WPF von null zum laufenden Setup ohne Budget

Du brauchst eine kleine Windows-Anwendung für einen Auftrag, aber weder Build-Server noch Zeit für ein Architekturprojekt. Mein Vorschlag ist bewusst eng: Starte mit einem WPF-Fenster, einem ViewModel und einem lokalen Speicherort. Eine funktionierende Übergabe ist für Solo-Freelancer wertvoller als eine Infrastruktur, die erst nach dem Auftrag Nutzen bringt. Die Grenze ziehst du dort, wo Datenverlust oder eine nicht reproduzierbare Auslieferung drohen.

Ein leerer WPF-Startpunkt reicht, wenn die erste Aufgabe klein bleibt

Lege zuerst fest, was die Anwendung am Ende des Tages können soll: Text entgegennehmen, auf eine Schaltfläche reagieren und den aktuellen Zustand anzeigen. Das ist absichtlich keine Produktbeschreibung, sondern ein prüfbarer Startpunkt. Wenn diese drei Schritte funktionieren, kannst du fachliche Felder ergänzen, ohne gleichzeitig Projektanlage, Bindings und Installation debuggen zu müssen.

Verwende einen Windows-11-Rechner mit dem .NET-10-SDK, PowerShell und Git. WPF läuft nur unter Windows; deshalb würde ich für diesen Auftrag keinen plattformübergreifenden Unterbau vorbereiten, solange der Kunde ausschließlich eine Windows-Anwendung bestellt hat. Microsoft veröffentlicht .NET 10 als LTS-Version mit Support bis November 2028. Prüfe mit dotnet –list-sdks, ob ein 10er-SDK installiert ist, bevor du ein Projekt erzeugst. Fehlt es, installiere das SDK von Microsoft und prüfe den Befehl erneut.

Erzeuge mit dotnet new wpf -n SoloDesk -f net10.0 das Projekt und wechsle in den Ordner SoloDesk. dotnet run muss jetzt ein leeres Fenster öffnen. Falls schon dieser Schritt scheitert, ändere noch keinen Anwendungscode: Ein Fehler im SDK oder in der lokalen Windows-Umgebung lässt sich leichter erkennen, solange das Projekt unverändert ist. Initialisiere danach mit git init ein Repository und committe den lauffähigen Ausgangspunkt. Dieser erste Commit kostet wenige Handgriffe und gibt dir einen bekannten Rückweg.

Installiere über NuGet mit dotnet add package CommunityToolkit.Mvvm –version 8.4.0 genau eine zusätzliche Bibliothek. Der feste Paketstand macht einen späteren Neuaufbau nachvollziehbar; er ist keine Aufforderung, jede Abhängigkeit dauerhaft auf dieser Version zu halten. WPF MVVM Grundlagen fuer moderne .NET Desktop-Apps ist dafür ein nützlicher Ausgangspunkt, aber ich würde aus dem Architekturbegriff noch keine weiteren Schichten für dieses Ein-Fenster-Projekt ableiten. Jede zusätzliche Schicht braucht einen konkreten Änderungsgrund.

Notiere in einer kurzen README die benötigte SDK-Hauptversion und den Startbefehl. Das ist für eine Einzelperson keine Formalität: Nach einigen Monaten ist die eigene Entwicklungsmaschine oft anders eingerichtet, während der Kunde weiterhin denselben Quellstand erwartet. Eine lokale README und die Projektdatei sind zunächst deine reproduzierbare Build-Anleitung; ein eigener Server ist dafür nicht erforderlich.

Ein ViewModel macht den ersten Klick nachvollziehbar

Füge im Projektordner die Datei MainViewModel.cs mit folgendem Inhalt hinzu. Der Code verwendet den Quellgenerator von CommunityToolkit.Mvvm: Aus Eingabe und Status entstehen bindbare Eigenschaften, aus der Methode Speichern entsteht SpeichernCommand. Der Name der Methode beschreibt hier zunächst nur die Benutzeraktion; auf die Festplatte schreibt dieser Stand ausdrücklich noch nicht.

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace SoloDesk;
public partial class MainViewModel : ObservableObject
{
    [ObservableProperty] private string eingabe = "";
    [ObservableProperty] private string status = "Bereit";
    [RelayCommand]
    private void Speichern()
    {
        Status = string.IsNullOrWhiteSpace(Eingabe) ? "Bitte Text eingeben" : $"Übernommen: {Eingabe}";
    }
}

Öffne anschließend MainWindow.xaml. Ersetze das erzeugte Grid durch ein StackPanel mit einer TextBox, einem Button und einem TextBlock. Verbinde die TextBox über Text=“{Binding Eingabe, UpdateSourceTrigger=PropertyChanged}“, den Button über Command=“{Binding SpeichernCommand}“ und den TextBlock über Text=“{Binding Status}“. Gib dem Button beispielsweise den sichtbaren Text „Übernehmen“. Im Konstruktor von MainWindow.xaml.cs setzt du direkt nach InitializeComponent(); die Zeile DataContext = new MainViewModel();. Damit haben alle drei Steuerelemente dieselbe Datenquelle.

Starte erneut mit dotnet run. Tippe einen Wert ein und klicke auf „Übernehmen“: Der TextBlock muss „Übernommen:“ und deinen Wert anzeigen. Entferne den Inhalt und klicke erneut: Jetzt muss „Bitte Text eingeben“ erscheinen. Wenn das nicht passiert, prüfe zuerst die Schreibweise der Binding-Namen und den DataContext, statt weitere Klassen hinzuzufügen. XAML-Bindings sind Zeichenketten; ein Tippfehler fällt deshalb oft erst beim Ausführen auf.

Ich würde hier keinen eigenen ICommand-Typ, keinen Dependency-Injection-Container und keinen Event-Bus schreiben. Für ein Fenster mit einer Aktion schaffen diese Bausteine zusätzliche Verbindungsstellen, ohne eine bestehende Schwierigkeit zu beseitigen. Ziehe eine Abhängigkeit erst heraus, wenn du sie getrennt testen oder austauschen musst. Das ist eine Entscheidung gegen vorsorgliche Architektur, nicht gegen MVVM: Die Trennung zwischen XAML und ViewModel ist bereits vorhanden.

Eine Datei im Benutzerprofil ist der erste sinnvolle Persistenzschritt

Der sichtbare Klick ist noch keine Speicherung. Bevor du das Wort „gespeichert“ in die Oberfläche schreibst, lege einen tatsächlichen Speicherort fest. Für eine einzelne Benutzerin oder einen einzelnen Benutzer auf einem Windows-PC ist ein Unterordner von Environment.SpecialFolder.LocalApplicationData ein pragmatischer Anfang. Eine Datei neben der ausführbaren Anwendung würde ich nicht verwenden, weil Schreibrechte dort bei einer Installation fehlen können und ein Update die Ablage unübersichtlich macht.

Ergänze eine kleine Klasse TextSpeicher, deren Methode den Ordner mit Directory.CreateDirectory anlegt und den Text mit File.WriteAllTextAsync in eine Datei im Unterordner SoloDesk schreibt. Für einen einzelnen Textwert reicht eine Textdatei. Sobald mehrere benannte Werte hinzukommen, ist System.Text.Json mit einem klar benannten Datenmodell geeigneter, weil Feldnamen dann mit den gespeicherten Werten mitreisen. In beiden Fällen brauchst du eine ebenso konkrete Lademethode, die bei fehlender Datei einen leeren Anfangszustand liefert.

Ändere erst danach die Aktion im ViewModel: Sie soll den Speichervorgang abwarten und Status nur nach erfolgreichem Schreiben auf „Gespeichert“ setzen. Fange erwartbare IOException– und UnauthorizedAccessException-Fehler an der Grenze zur Datei ab und zeige eine verständliche Fehlermeldung im Status an. Ich würde eine fehlgeschlagene Speicherung nicht still wiederholen, weil die Oberfläche sonst Erfolg nahelegen könnte, während der neue Inhalt nur im Arbeitsspeicher liegt. Halte den Dateipfad in der Speicherklasse, nicht im XAML.

Setze als veränderbaren Grenzwert höchstens 1 MB pro Datendatei, solange es bei kurzen Texteingaben bleibt. Das ist kein WPF-Limit, sondern ein Anlass, vor größeren Datenmengen das Format und die Bedienung neu zu prüfen. Als Planwert reichen zunächst 3 manuelle Fälle: erstmals speichern, nach einem Neustart laden und einen Schreibfehler sichtbar machen. Den dritten Fall kannst du ohne Spezialinfrastruktur prüfen, indem du den Zielpfad für einen Test auf ein nicht beschreibbares Verzeichnis umstellst.

Automatisiere anschließend den Teil, der ohne Fenster prüfbar ist. Ein MSTest-Projekt kann etwa testen, was bei einer fehlenden Datei geladen wird und ob ein gespeicherter Wert nach erneutem Laden identisch ist. Lass Tests in einem temporären Verzeichnis arbeiten, damit sie keine echten Benutzerdaten verändern. Für die kleine erste Version würde ich keine GUI-Testautomatisierung beginnen: Sie verlangt zusätzlich stabile Fensterzustände und eine passende Ausführungsumgebung, während die Dateiregeln bereits isoliert testbar sind.

Eine Ordnerauslieferung schlägt MSIX beim ersten Einzelauftrag

Jetzt stehen zwei reale Wege zur Übergabe an: Ordnerauslieferung und MSIX. Die Ordnerauslieferung gewinnt, wenn der Kunde die Anwendung auf wenigen bekannten Windows-Rechnern selbst starten kann; sie kostet dich eine klare Anleitung und die Prüfung der benötigten Laufzeit. MSIX gewinnt, wenn eine geregelte Installation, Identität und Updates ausdrücklich gefordert sind; es kostet zusätzlich Arbeit an Paketierung, Signierung und dem Verteilweg. Ich würde beim ersten Einzelauftrag die Ordnerauslieferung wählen, weil die zweite Variante ohne solchen Bedarf mehr offene Übergabefragen erzeugt als löst.

Erzeuge sie aus dem Projektordner mit dotnet publish -c Release -r win-x64 –self-contained false. Der Schalter –self-contained false hält das Paket von einer mitgelieferten .NET-Laufzeit frei; dafür muss auf dem Zielrechner die passende .NET Desktop Runtime 10 installiert sein. Prüfe diese Voraussetzung auf dem Zielgerät oder einem vergleichbar eingerichteten Windows-Rechner, statt allein vom Entwicklungsrechner auf die Übergabe zu schließen. Falls der Kunde keine Laufzeit installieren kann, ist –self-contained true eine mögliche Alternative, deren größeres Paket du vorab tatsächlich übertragen und starten solltest.

Kopiere den Inhalt des Publish-Ordners als Einheit; eine einzelne EXE herauszugreifen ist bei dieser Ausgabe keine verlässliche Übergabe. Füge eine kurze Startanleitung, die Runtime-Voraussetzung und den Speicherort der Benutzerdaten hinzu. Gib keine privaten Testdaten mit. Nach einem Neustart der Anwendung muss der zuletzt gespeicherte Wert wieder erscheinen. Erst dieser Versuch auf der veröffentlichten Ausgabe prüft den Weg, den der Kunde benutzt; dotnet run prüft dagegen deinen Entwicklungsstand.

WPF-Ratschlaege von 2024 sprengen dein Solo-Budget benennt eine berechtigte Kostengefahr; ich ziehe die Grenze jedoch nicht bei jedem Werkzeug, sondern bei Werkzeugen ohne Aufgabe in der nächsten Lieferung. Git, NuGet und ein lokaler Testlauf verdienen ihren Platz, weil sie Quellstand, Abhängigkeit und Verhalten überprüfbar machen. Eine Cloud-Pipeline würde ich erst einrichten, wenn wiederholte Releases oder weitere Mitwirkende den manuellen Build tatsächlich zum Engpass machen.

Notiere zum Schluss Ergebnisse statt Eindrücke. Ein Protokolleintrag wie „0 Abstürze bei 10 Starts“ ist nur dann ein gemessener Wert, wenn du diese Starts wirklich durchgeführt hast; er sagt außerdem noch nichts über das Laden der Datei aus. Schreibe dafür einen eigenen Befund auf. So bleiben beobachtetes Verhalten, geplante Grenzwerte und Herstellerangaben voneinander getrennt.

Der nächste Auftrag beginnt mit einem überprüfbaren Ausgangspunkt

Öffne als Erstes PowerShell und prüfe mit dotnet –list-sdks deine lokale Installation. Erzeuge dann das leere WPF-Projekt und committe den Zustand, in dem dotnet run ein Fenster öffnet. Wenn dieser Ausgangspunkt feststeht, ist der nächste Schritt eindeutig: ViewModel einfügen, drei Bindings setzen und den ersten Klick prüfen. Alles Weitere braucht einen konkreten Fehler oder eine konkrete Kundenanforderung.