Fluent-Theming in WPF unter .NET 10
- Matt
Eine interne WPF-Anwendung, die ich pflege, sah neben allem anderen auf einem Windows-11-Rechner aus wie von 2010. Graue Rahmen, harte Kanten, die alten Aero2-Formen. Bisher hätte das ein eigenes Ressourcenwörterbuch oder eine fremde Bibliothek bedeutet. Seit .NET 9 liegt ein Fluent-Design im Framework selbst, und unter .NET 10 deckt es genug Steuerelemente ab, dass ich es an einem Nachmittag eingeschaltet habe.
Einschalten
Der bequeme Weg ist eine einzige Eigenschaft. In der App.xaml:
<Application x:Class="Meine.App"
ThemeMode="System">
</Application>
ThemeMode kennt vier Werte: Light, Dark, System und None. System folgt der Windows-Einstellung, None ist die Vorgabe und bleibt beim alten Aero2. Die Eigenschaft gibt es sowohl auf Application als auch auf Window, und das Fenster gewinnt: Steht ein Fenster auf None, erbt es wieder vom Anwendungswert. So kann ein einzelner Dialog dunkel bleiben, während der Rest der App der Systemeinstellung folgt.
Wer lieber im Code umschaltet, kommt an einer Warnung nicht vorbei:
Application.Current.ThemeMode = ThemeMode.Dark;
Das erzeugt WPF0001, weil die API als experimentell markiert ist. Man muss die Kennung im Projekt bewusst unterdrücken, sonst bricht der Build. Genau diese Warnung ist der ehrlichste Hinweis auf den Zustand der Sache.
Was .NET 10 dazugelegt hat
Unter .NET 9 waren spürbar Steuerelemente ungestylt und fielen optisch aus dem Bild. .NET 10 hat unter anderem GroupBox, GridSplitter, Hyperlink, Frame und NavigationWindow nachgezogen. Der Akzentfarbwechsel greift jetzt zur Laufzeit: Ändere ich in Windows die Akzentfarbe, zieht die laufende Anwendung mit, ohne Neustart. Und im Kontrastmodus schaltet das Framework von selbst auf die Kontrastvariante um, Hell und Dunkel spielen dann keine Rolle mehr.
Der Haken
Der Frame hat mich einen Abend gekostet. Unter Fluent zeigt er in .NET 10 die Navigationsleiste wieder standardmäßig an, was gegenüber .NET 9 eine sichtbare Änderung ist. Wer Frame als reinen Inhaltsbehälter benutzt, bekommt plötzlich Zurück-Pfeile geschenkt.
Das ist symptomatisch. Die API trägt nicht ohne Grund das Experimental-Attribut. Zwischen den Versionen gibt es Formänderungen und Brüche, und wer die Optik pixelgenau festnagelt, sollte damit rechnen, nach dem nächsten Update nachzubessern. Für eine interne Anwendung nehme ich das hin. In einem Produkt mit Designabnahme würde ich noch beim eigenen Wörterbuch bleiben.
Wenn du ohnehin gerade ein altes WPF-Projekt auf .NET 10 hebst, ist der Moment danach der richtige, um ThemeMode="System" zu setzen und zu schauen, wie viel schon von allein passt. Die Details stehen in der Fluent-Dokumentation im dotnet/wpf-Repository.