Avalonia ist ein mächtiges UI-Framework, das nach dem ersten Ausprobieren schnell Lust auf mehr macht. Hat man sich für eine Migration von WinForms oder WPF hin zu Avalonia entschieden, kann man in seltenen Fällen auf Controls stoßen, die es in Avalonia nicht gibt – zumindest nicht genauso, wie vorher in der WinForms- oder WPF-Welt. Ein solches Beispiel hatte ich neulich bei einem Kunden. Konkret ging es um die für WinForms bereitgestellte ReportViewer-Komponente [1]. Der längerfristig richtige Schritt in solchen Fällen ist häufig, einen modernen Ersatz zu finden, der sich entsprechend gut in die Avalonia-Applikation einbinden lässt. In diesem konkreten Fall ist ein Wechsel der ReportViewer-Komponente ohnehin überfällig, im Kontext der Migration auf Avalonia hat diese aber zunächst eine geringere Priorität. Übergangsweise kann es in diesen Situationen hilfreich sein, solche Controls direkt in eine Avalonia-Applikation einzubinden – auch wenn das aufgrund von WinForms nur unter Windows funktioniert und damit nicht auf anderen Plattformen wie macOS oder Linux genutzt werden kann.
Cross-Platform
Tricks mit XAML-Namespaces in Avalonia
Avalonia nutzt XAML als Beschreibungssprache für UIs. XAML wurde mit WPF eingeführt und hat sich seitdem im .NET-Ökosystem gut verbreitet und etabliert. Die Grundidee von XAML ist denkbar einfach. Es nutzt XML als Basis, erweitert es aber an einigen Stellen mit zusätzlichen Features für die UI-Entwicklung. Ein gutes Beispiel solcher Features sind MarkupExtensions. Viele von uns (inkl. mir selbst) arbeiten gerne mit XAML, an einigen Stellen sorgt es aber für mehr Tipparbeit, als es sein müsste. Ein Beispiel dafür sind die teilweise vielen und langen Imports für XAML-Namespaces, die am root-Knoten geschrieben werden müssen. .NET MAUI adressiert dieses Problem im aktuellen Release [1], doch gibt es auch Wege in Avalonia? In diesem Artikel möchte ich einige Tricks zeigen, um speziell mit diesem Problem in Avalonia eleganter umzugehen.
ValueConverter in Avalonia – 3 Wege, sie in XAML einzubinden
Avalonia hat sich als modernes, Cross-Platform-UI‑Framework für .NET etabliert. Ein zentrales Element bei der Datenbindung ist dabei der ValueConverter: eine kleine Logikschicht, die Werte und Objekte zwischen ViewModel und UI umwandeln kann. In anderen auf XAML basierenden Frameworks wie WPF ist das Konzept bereits seit Jahrzehnten bekannt – in Avalonia funktioniert es grundsätzlich ähnlich und wird auch häufig genutzt. Für die Art, wie wir ValueConverter im XAML-Code einbinden, gibt es allerdings wie so oft mehrere verschiedene Wege. Diesen Artikel möchte ich nutzen, um einige davon zu zeigen. Leser können sich damit eine Meinung bilden, welche davon für sie selbst die sinnvollste ist.
Avalonia UI – Per MarkupExtension auf das Betriebssystem eingehen
Avalonia UI rendert alle Elemente selbst. Das hat insbesondere den Vorteil, dass die eigene Applikation und damit das eigene Corporate Branding auf jeder Plattform gleich aussieht. Es gibt noch einige andere Vorteile dieses Ansatzes. In diesem Artikel möchte ich aber darauf eingehen, wie man mit Stellen umgehen kann, an denen es Unterschiede zwischen den verschiedenen Betriebssystemen gibt. Es gibt i. d. R. nur Wenige, eines davon ist die Menüleiste. Für Windows und Linux wird sie typischerweise im Hauptfenster ganz oben angezeigt. Unter macOS ist das anders – dort stellt das Betriebssystem selbst eine Menüleiste am oberen Bildschirmrand bereit. Somit ist es unter macOS gar nicht notwendig, das Hauptmenü selbst zu zeichnen. Hier im Artikel beschreibe ich, wie ich das Thema bei GPXviewer 2 mittels einer MarkupExtension gelöst habe.
GPXviewer 2 – Mit Avalonia UI auf mehrere Plattformen
Es gibt eine lange Liste möglicher Frontend-Fameworks, zwischen denen .NET-Entwicklungsteams wählen können. Ein sehr spannender Kandidat ist Avalonia UI [1]. Ursprünglich als Cross-Plattform Alternative zu WPF gestartet, bietet es heute neben den bekannten Desktops von Windows, macOS und Linux auch Unterstützung für mobile Plattformen (Android, iOS), für den Browser (via WebAssembly) und als Alleinstellungsmerkmal auch für Embedded Linux. Für mein OpenSource Tool GPXviewer war für mich neben Windows insbesondere macOS relevant. Ganz einfach aus dem Grund, weil ich mittlerweile mehr Zeit auf meinem MacBook verbringe, als auf meinem Windows Laptop [2]. Daher habe ich mich entschieden, GPXviewer von WPF nach Avalonia UI zu portieren – daraus entstand dann das Projekt GPXviewer 2 [3]. In diesem und nachfolgenden Artikeln möchte ich einige Erfahrungen teilen, die ich damit sammeln durfte.
Als .NET Entwickler auf dem MacBook Pro
Vor etwas über zwei Jahren startete ich für mich persönlich ein Experiment. Ich habe mir einen MacBook Pro mit dem zu der Zeit neuen M1 Prozessor gekauft. Für mich war das tatsächlich ein großer Schritt, da ich meinen Fokus auf der .NET Plattform von Microsoft habe und damit seit über 15 Jahren bis auf wenige Linux-Ausflüge primär auf Windows unterwegs war. Der neue M1 Prozessor und dessen Balance aus Leistung und Akkulaufzeit war dabei ein Auslöser der Entscheidung für das Experiment. Es kamen aber auch andere Faktoren dazu. Immer häufiger bin ich etwa bei Kunden und im Bekanntenkreis auf macOS Benutzer gestoßen. Die Zeit war also mehr als reif, mich selbst als Entwickler mit der Plattform zu beschäftigen. Über meine Erfahrungen, Ärgernisse und warum es für mich Stand heute eine gute Entscheidung war, erfährst du mehr in diesem Artikel.
Testautomatisierung mit Avalonia
Testautomatisierung auf UI Ebene ist häufig nicht so einfach zu erreichen. Grund dafür können technische Fragestellungen sein. So gilt es, auf irgendeine Art das Rendering des UI-Frameworks abzubilden. Ebenso gilt es, mögliche Usereingaben zu emulieren oder Eingabeelemente an der UI zu identifizieren. Auch zeitliche Aspekte spielen mit rein, so ist ein „Warte eine Sekunde…“ in einem automatisierten Test i. d. R. eine eher schlechte Idee. Doch es gibt auch Herausforderungen, die aus den Testfällen selbst entstehen. Versucht man etwa, ein Drag-Drop Verhalten automatisiert zu testen, beißt man sich dabei schon mal gerne die Zähne aus. Ich persönlich versuche UI Tests daher auf einem niedrigen Level zu halten, setze sie also primär für einfach zu überblickende Geradeausfälle ein. Mit Avalonia habe ich UI Tests zuletzt bei meinem Nuget-Paket RolandK.AvaloniaExtensions [1] eingesetzt. Hier ging es mir darum, dass die dort definierten Basisklassen und Features wie die Dependency Injection einwandfrei in einer Avalonia Applikation funktionieren.
Avalonia FluentTheme zur Laufzeit wechseln
Viele moderne Applikationen bieten neben einer ansprechenden UI auch den Wechsel zwischen verschiedenen Themes an. Für gewöhnlich wird zumindest zwischen Hell und Dunkel unterschieden. Gleiches gilt für das Betriebssystem selbst – Windows und macOS bieten dem User jeweils die Wahl zwischen Hell und Dunkel. Auch Avalonia bietet mit dem FluentTheme seit Version 0.10 einen sehr einfachen Weg an, zwischen hellen und dunklen Modus zu unterscheiden. Typischerweise wird das FluentTheme in der App.xaml angegeben und bekommt als Mode entweder „Light“ oder „Dark“. In diesem Artikel wollen wir uns damit beschäftigen, wie diese Einstellung zur Laufzeit der Applikation verändert werden kann. Weiterhin schauen wir uns an, wie man unter Windows den aktuell konfigurierten Theme herausbekommt und sogar auf Änderung des aktuellen Theme reagieren kann.
Avalonia Applikationen übersetzen
In den letzten Monaten habe ich vermehrt Artikel über das Cross-Platform Framework Avalonia geschrieben. Für mich persönlich hat sich das Framework mittlerweile zum Standard für Desktop-Applikationen entwickelt. Es kann fast alles, was man braucht und läuft auf allen gängigen Desktop Plattformen (Windows, macOS, Linux) stabil. In diesem Artikel möchte ich mich mit einem Thema beschäftigen, auf das die meisten Softwareentwickler bei UIs irgendwann stoßen: Applikationen in andere Sprachen übersetzen. Auch wenn man nicht den akuten Bedarf hat, so sollte man UIs möglichst von vorne herein darauf vorbereiten, dass sie übersetzbar sind. Übersetzung im Nachhinein einzubauen kann häufig sehr schwierig sein. Insbesondere, wenn Daten von außen bereits behaftet mit einer bestimmten Sprache in der eigenen Applikation ankommen.
Das DataGrid von Avalonia
Das auf .Net basierende Cross-Plattform UI Framework Avalonia verfügt über eine große Palette an Standard-Controls. Es ist vieles dabei, was man als Entwickler typischerweise braucht. So gibt es etwa diverse Eingabeboxen, Controls für Auflistungen und welche, über die das Layout beeinflusst werden kann. Wenn ich auf Business-Applikationen schaue, fällt mir zudem noch ein sehr wichtiges ein, welches Avalonia mitbringt: Das DataGrid. Die Aufgabe eines DataGrids besteht grundsätzlich darin, eine mehr oder weniger lange Liste von Objekten in tabellarischer Form darzustellen. Jede Zeile ist ein solches Objekt, jede Spalte bezieht sich auf eine Eigenschaft. Daneben gibt es typischerweise Anforderungen wie zum Beispiel Sortierung, Filterung oder Gruppierung. Das DataGrid in Avalonia liefert diese Features entweder direkt oder bringt sie über den kleinen Umweg einer CollectionView im ViewModel mit. In diesem Artikel möchte ich mich mit den Features beschäftigen, welche das DataGrid in Avalonia im Standard mitbringt und wie man diese verwendet.