16. Jun 2025
Lesedauer 16 Min.
UIs für Linux
Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.

Es gibt eine lange Liste von UI-Frameworks für C#/.NET. Allein Microsoft hat mit Windows Forms, WPF, WinUI,
.NET MAUI und ASP.NET Core Blazor eine lange Liste im Programm. Wollen wir eine Applikation für Windows entwickeln, haben wir somit die Qual der Wahl. Unter macOS wird die Microsoft-Liste bereits kleiner, mit .NET MAUI und ASP.NET Core Blazor stehen von den oben Genannten aber noch zwei Frameworks zur Auswahl.In diesem Artikel möchten wir uns damit beschäftigen, wie wir UIs für Linux entwickeln können. Hier purzeln mit Ausnahme von ASP.NET Core Blazor alle anderen genannten Microsoft-Frameworks aus der Liste. Selbst bei ASP.NET Core Blazor kann noch etwas fehlen, da es einen Browser benötigt, innerhalb dessen der HTML-Content gerendert werden kann. ASP.NET Core Blazor Hybrid als Container ist hierfür eine gängige Variante, unter Linux kann diese jedoch nicht verwendet werden. Wir müssten uns somit selbst um einen Container für die ASP.NET-Core-Blazor-Applikation kümmern oder ein entsprechendes Open-Source-Projekt suchen.ASP.NET Core Blazor basiert auf Webtechnologie. Ist damit Webtechnologie der einzige Weg, wie wir in C# eine Benutzeroberfläche für Linux bauen können? Glücklicherweise nicht. Mit Avalonia existiert ein seit mehr als elf Jahren gereiftes UI-Framework für C# / .NET, das neben Windows und macOS auch Linux klar im Fokus hat. Seit Version 11 werden zudem Android, iOS und Browser (via WASM) unterstützt.Avalonia wurde bereits im dotnetpro-Artikel „Das bessere WPF“ von Fabian Hügle [1] vorgestellt. Daher beschäftigen wir uns in diesem Artikel nur kurz mit allgemeinen Informationen zu Avalonia: Avalonia ist ein von der Community getriebenes Open-Source-Projekt. Es hat eine hohe Ähnlichkeit zu WPF, nutzt XAML, präferiert das MVVM-Pattern und setzt auf ein eigenes Rendering aller Controls. All diese Punkte führen dazu, dass wir uns bei der Arbeit mit Avalonia schnell wohlfühlen, wenn wir bereits WPF-Kenntnisse oder Erfahrung mit XAML mitbringen.Der Ansatz des eigenen Renderings führt dazu, dass es aus technischer Sicht einfach ist, Avalonia auf weitere Plattformen zu bringen – das Framework benötigt nur eine Canvas, in die gerendert werden kann. Hervorgehoben wird dieser Umstand, weil es ein völlig anderer Weg ist als etwa bei .NET MAUI. .NET MAUI nutzt die Controls und damit auch das Rendering der jeweiligen Plattform. Neue Plattformen zu unterstützen ist damit für .NET MAUI deutlich aufwendiger.WPF verfolgt einen ähnlichen Ansatz wie Avalonia – alle Controls werden selbst gerendert. Anders als bei Avalonia wurde in WPF nie die Unterstützung für weitere Betriebssysteme eingebaut. Möchte man eine WPF-Applikation für weitere Plattformen fit machen, könnte das Produkt Avalonia XPF spannend sein [2], siehe auch den Kasten Avalonia XPF.
.NET MAUI und ASP.NET Core Blazor eine lange Liste im Programm. Wollen wir eine Applikation für Windows entwickeln, haben wir somit die Qual der Wahl. Unter macOS wird die Microsoft-Liste bereits kleiner, mit .NET MAUI und ASP.NET Core Blazor stehen von den oben Genannten aber noch zwei Frameworks zur Auswahl.In diesem Artikel möchten wir uns damit beschäftigen, wie wir UIs für Linux entwickeln können. Hier purzeln mit Ausnahme von ASP.NET Core Blazor alle anderen genannten Microsoft-Frameworks aus der Liste. Selbst bei ASP.NET Core Blazor kann noch etwas fehlen, da es einen Browser benötigt, innerhalb dessen der HTML-Content gerendert werden kann. ASP.NET Core Blazor Hybrid als Container ist hierfür eine gängige Variante, unter Linux kann diese jedoch nicht verwendet werden. Wir müssten uns somit selbst um einen Container für die ASP.NET-Core-Blazor-Applikation kümmern oder ein entsprechendes Open-Source-Projekt suchen.ASP.NET Core Blazor basiert auf Webtechnologie. Ist damit Webtechnologie der einzige Weg, wie wir in C# eine Benutzeroberfläche für Linux bauen können? Glücklicherweise nicht. Mit Avalonia existiert ein seit mehr als elf Jahren gereiftes UI-Framework für C# / .NET, das neben Windows und macOS auch Linux klar im Fokus hat. Seit Version 11 werden zudem Android, iOS und Browser (via WASM) unterstützt.Avalonia wurde bereits im dotnetpro-Artikel „Das bessere WPF“ von Fabian Hügle [1] vorgestellt. Daher beschäftigen wir uns in diesem Artikel nur kurz mit allgemeinen Informationen zu Avalonia: Avalonia ist ein von der Community getriebenes Open-Source-Projekt. Es hat eine hohe Ähnlichkeit zu WPF, nutzt XAML, präferiert das MVVM-Pattern und setzt auf ein eigenes Rendering aller Controls. All diese Punkte führen dazu, dass wir uns bei der Arbeit mit Avalonia schnell wohlfühlen, wenn wir bereits WPF-Kenntnisse oder Erfahrung mit XAML mitbringen.Der Ansatz des eigenen Renderings führt dazu, dass es aus technischer Sicht einfach ist, Avalonia auf weitere Plattformen zu bringen – das Framework benötigt nur eine Canvas, in die gerendert werden kann. Hervorgehoben wird dieser Umstand, weil es ein völlig anderer Weg ist als etwa bei .NET MAUI. .NET MAUI nutzt die Controls und damit auch das Rendering der jeweiligen Plattform. Neue Plattformen zu unterstützen ist damit für .NET MAUI deutlich aufwendiger.WPF verfolgt einen ähnlichen Ansatz wie Avalonia – alle Controls werden selbst gerendert. Anders als bei Avalonia wurde in WPF nie die Unterstützung für weitere Betriebssysteme eingebaut. Möchte man eine WPF-Applikation für weitere Plattformen fit machen, könnte das Produkt Avalonia XPF spannend sein [2], siehe auch den Kasten Avalonia XPF.
Avalonia XPF
Neben Avalonia selbst stellt das Avalonia-Team auch das kostenpflichtige Produkt Avalonia XPF bereit. Avalonia XPF ist ein Fork von WPF, der Unterbau wurde hier aber durch die Rendering-Logik von Avalonia ersetzt. Dadurch wird erreicht, dass auch WPF-Applikationen plötzlich Cross-Plattform-fähig sein können.
Zurück zum Thema Linux: Wenn wir von Linux-UIs sprechen, ist die Welt deutlich komplexer als etwa unter Windows oder macOS. Unter Linux unterscheiden wir beispielsweise mehrere Distributionen (etwa Ubuntu oder Fedora), mehrere Desktop-Umgebungen (zum Beispiel Gnome, KDE Plasma oder Xfce) und mehrere Display-Server-Protokolle (X11, Wayland). Und das ist noch nicht einmal alles, soll aber für diesen Artikel genügen, um einen Eindruck zu bekommen. Wir müssen uns an dieser Stelle also damit auseinandersetzen, was davon unsere konkrete Applikation unterstützen soll. Das Avalonia-Team selbst tätigt dazu folgende Aussagen:
- Offiziell unterstützt werden die Distributionen Debian 9+, Ubuntu 16.04+ und Fedora 30+.
- Weitere Voraussetzung ist .NET, etwa eine aktuelle .NET-Core-Version.
Entwickeln unter Linux
Bevor wir uns Avalonia genauer anschauen, stellt sich die Frage, wie wir überhaupt entwickeln. Dabei geht es zunächst um die Wahl des Betriebssystems, auf dem entwickelt wird. Für Linux-UIs bietet sich an dieser Stelle entsprechend auch eine Linux-Umgebung für die Entwicklung an. „Wie entwickelt man C# unter Linux?“, wird sich der eine oder andere Leser sicher fragen. Ganz einfach, tatsächlich ist es Stand 2025 nicht einmal mehr so selten, dass C#-Entwickler ein Linux verwenden. Es gibt zwei sehr bekannte Entwicklungsumgebungen für C#, die hervorragend unter Linux laufen:- Rider von JetBrains,
- Visual Studio Code von Microsoft.

Entwickeln unter Linux mit JetBrains Rider (Bild 1)
Autor
Visual Studio Code ist als Alternative ebenso möglich. Das Avalonia-Team stellt die Extension Avalonia for Visual Studio Code bereit, beschreibt diese dabei aber selbst so: „While functional, the development experience is not as rich as what you’ll find in Rider or Visual Studio.“
Entwickeln unter Windows und WSL
Entscheiden wir uns für Windows als Betriebssystem bei der Entwicklung, so haben wir auch hier die Möglichkeit, unsere Avalonia-Applikation aus Visual Studio heraus auf Linux auszuführen und zu debuggen. Das Zauberwort an dieser Stelle ist WSL (Windows Subsystem for Linux) beziehungsweise WSLg (Windows Subsystem for Linux GUI). WSLg wurde in der dotnetpro bereits von Tam Hanna vorgestellt [5]. WSLg ermöglicht die Ausführung von Linux-Anwendungen mit grafischer Bedienoberfläche und bindet diese in den Windows-Desktop ein.Visual Studio erlaubt es, Applikationen innerhalb des WSL zu starten und zu debuggen. Machen wir das mit einer Avalonia-Applikation, so sorgt WSLg dafür, dass die Applikation in WSL hochfährt und in einem Fenster auf dem Windows-Desktop dargestellt wird. Bild 2 zeigt, wie das für die Applikation MessageCommunicator aussieht, ein Open-Source-Projekt auf Basis von Avalonia. Die Applikation kann dabei ähnlich einer nativen Windows-Applikation bedient werden. Auch der Debugger aus Visual Studio heraus funktioniert wie gewohnt. Somit erhalten wir eine ideale Grundlage zur Entwicklung von Linux-Applikationen unter Windows.
Avalonia-Applikation MessageCommunicator, ausgeführt innerhalb der WSL 2 unter Windows (Bild 2)
Autor
Sollte man sich auf der Entwicklungsmaschine für Windows entscheiden, so dürfen an dieser Stelle einige Hinweise nicht fehlen. Linux macht verschiedene grundlegende Dinge anders als Windows:
- Pfade werden durch Slashes (/) getrennt, nicht durch Backslashes (\).
- Zeilenumbrüche innerhalb von Textdateien werden mit Line Feed ausgeführt, nicht mit Carriage Return + Line Feed.
- Pfadangaben sind Case-sensitiv.
Beispielapplikation für diesen Artikel
Eine Beispielapplikation, die Sie in den Downloads zum Artikel finden, hilft uns dabei, einen tieferen Einblick in die Entwicklung mit Avalonia zu bekommen. Wir bauen dazu ein kleines Monitoring-Tool für einen Temperatursensor. Die Logik ist sehr einfach: Wir haben einen Sensor, von dem wir im Sekundentakt Temperaturwerte auslesen. Wir visualisieren den zuletzt gelesenen Wert und die historischen Werte anhand einer Tabelle. Damit das Tool einfach selbst getestet werden kann, binden wir keinen echten Temperatursensor an. Stattdessen generieren wir die Werte im Hintergrund zufällig. Das Endergebnis ist in Bild 3 zu sehen.
Screenshot der Beispielapplikation für diesen Artikel (Bild 3)
Autor
Mit dem richtigen Template starten
Avalonia stellt mehrere Templates bereit, mit denen wir starten können. Diese werden vom Avalonia-Team in einem separaten GitHub-Repository gepflegt [6]. Mit folgendem Befehl in der Kommandozeile werden sie installiert:
dotnet new install Avalonia.Templates
Zum Zeitpunkt dieses Artikels erhalten wir folgende Templates:
- Avalonia .NET App,
- Avalonia .NET MVVM App,
- Avalonia Cross Platform Application.
Compiled Bindings
Anders als WPF unterscheidet Avalonia zwischen Compiled Binding und Reflection Binding. Für beide Varianten werden gleichnamige Markup Extensions bereitgestellt. Die Markup Extension <span class="_4_Kursiv-im-Kasten">Binding</span> nutzt die Variante, die als Standard in der XAML-Datei oder global in der <span class="_4_Kursiv-im-Kasten">.csproj</span>-Datei hinterlegt ist. Per Default wird Letztere durch die Avalonia-Templates so eingestellt, dass Compiled Binding der Standard ist.
Ein MVVM-Framework auswählen
MVVM (Model-View-ViewModel) ist bei XAML-Frameworks nach wie vor das beliebteste Pattern; Gleiches gilt auch für Avalonia. Bei der Auswahl des MVVM-Frameworks sind folgende zwei Varianten für Avalonia-Projekte am häufigsten anzutreffen.- CommunityToolkit.Mvvm,
- ReactiveUI.
für Commands mit. Eine Besonderheit des CommunityToolkit.Mvvm ist die Nutzung von Roslyn Source Generators, um typischen Boilerplate-Code rund um INotifyPropertyChanged-Implementierungen zu generieren.
ViewModel und Service erstellen
Zuerst erstellen wir den Service, der für die Ermittlung der Temperaturdaten zuständig ist. Hierzu definieren wir das Interface IMeasurementService und die Implementierung RandomMeasurementService. Letztere simuliert den Temperatursensor, damit wir in diesem Beispiel nicht von einem physischen Sensor abhängig sind. Listing 1 enthält den Code aus dem Beispiel.Listing 1: Service zur Ermittlung von Werten vom simulierten Temperatursensor
TemperatureMeasurement.cs<br/>public record TemperatureMeasurement(DateTimeOffset <br/> TimeStamp, double TemperatureInDegrees);<br/>IMeasurementService.cs<br/>public interface IMeasurementService<br/>{<br/> IAsyncEnumerable&lt;TemperatureMeasurement&gt; <br/> StartMeasurement(<br/> CancellationToken cancellationToken);<br/>}<br/>RandomMeasurementService.cs<br/>public class RandomMeasurementService : <br/> IMeasurementService<br/>{<br/> public async IAsyncEnumerable&lt;<br/> TemperatureMeasurement&gt; StartMeasurement(<br/> [EnumeratorCancellation] CancellationToken <br/> cancellationToken)<br/> {<br/> var random = new Random();<br/> while (!cancellationToken.IsCancellationRequested)<br/> {<br/> yield return new TemperatureMeasurement(<br/> DateTimeOffset.UtcNow,<br/> 16 + random.NextDouble() * 16);<br/> <br/> try<br/> {<br/> await Task.Delay(1000, cancellationToken)<br/> .ConfigureAwait(false);<br/> }<br/> catch (OperationCanceledException)<br/> {<br/> break;<br/> }<br/> }<br/> }<br/>}
Ein besonderer Hinweis gilt an dieser Stelle dem ConfigureAwait(false) bei Task.Delay in der Methode StartMeasurement. ConfigureAwait(false) ist hier nicht zwingend notwendig, dafür aber gute Praxis, um die Hintergrundverarbeitung aus dem UI-Thread herauszuhalten. Avalonia ist ähnlich wie andere UI-Frameworks single-threaded und setzt auch einen SynchronizationContext ein. Das await würde damit automatisch nach der Wartezeit im UI-Thread weitermachen. Innerhalb des ViewModels oder allgemein in der View-Logik ist das zwar meist das gewünschte Verhalten, bei Hintergrundverarbeitung wie hier aber nicht. ConfigureAwait(false) unterdrückt den Rücksprung in den UI-Thread und macht standardmäßig mit einem Thread aus dem ThreadPool weiter.Dann erstellen wir das ViewModel unserer Applikation, hier einfach MainWindowViewModel genannt. Listing 2 zeigt, wie es mithilfe des CommunityToolkit.Mvvm gebaut wird. Wir erben von ObservableObject und definieren die Klasse als partial, damit der Roslyn Source Generator des Toolkits arbeiten kann. Der Member _currentMeasurement wird mit dem Attribut ObservableProperty markiert. Daraufhin generiert das CommunityToolkit.Mvvm automatisch die zugehörige Eigenschaft mit trigger für das NotifyPropertyChanged-Ereignis. Ähnlich bei der Methode StartMeasurementAsync: Das Attribut ReleayCommand sorgt hier dafür, dass das CommunityToolkit.Mvvm das zugehörige Command generiert. Das Command ist dabei vom Typ AsyncRelayCommand. Dieser Typ sorgt dafür, dass das Command nur einmal parallel laufen kann und dass wir es abbrechen können.
Listing 2: Das ViewModel für unsere Beispielapplikation
MainWindowViewModel.cs<br/>public partial class MainWindowViewModel(<br/> IMeasurementService measurementService) : <br/> ObservableObject<br/>{<br/> [ObservableProperty]<br/> private TemperatureMeasurement? _currentMeasurement;<br/> <br/> public ObservableCollection&lt;TemperatureMeasurement&gt;<br/> Measurements { get; } = new();<br/> [RelayCommand]<br/> private async Task StartMeasurementAsync(<br/> CancellationToken cancellationToken)<br/> {<br/> await foreach (var actMeasurement in <br/> measurementService.StartMeasurement(<br/> cancellationToken))<br/> {<br/> this.CurrentMeasurement = actMeasurement;<br/> <br/> this.Measurements.Add(actMeasurement);<br/> if (this.Measurements.Count &gt; 20)<br/> {<br/> this.Measurements.RemoveAt(<br/> this.Measurements.Count - 1);<br/> }<br/> }<br/> }<br/>}
Zuletzt weisen wir das ViewModel noch unserer View zu. Es gibt viele Wege dafür; der für dieses Beispiel einfachste verläuft über die App.axaml.cs. Hier modifizieren wir die Methode OnFrameworkInitializationCompleted so, dass dort das ViewModel erzeugt und dem MainWindow zugewiesen wird.
App.axaml.cs
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is
IClassicDesktopStyleApplicationLifetime desktop)
{
var mainWindow = new MainWindow();
mainWindow.DataContext =
new MainWindowViewModel(
new RandomMeasurementService());
desktop.MainWindow = mainWindow;
}
base.OnFrameworkInitializationCompleted();
}
}
Die Benutzeroberfläche definieren
Im nächsten Schritt können wir uns mit der Benutzeroberfläche selbst beschäftigen. Da die Applikation klein ist, reicht uns als einzige View ein Hauptfenster. Darin definieren wir das Layout und die Inhalte.In Listing 3 sehen wir den XAML-Code. Hervorzuheben ist hier das x:DataType in der achten Zeile. Damit sagen wir dem Compiler, welches ViewModel dieser View zugrunde liegt. Erst mit dieser Information funktionieren die Compiled Bindings, mit denen wir uns weiter oben im Artikel beschäftigt haben.Listing 3: Inhalt der MainWindow.axaml, zuständig für das Layout der Beispielapplikation
&lt;Window xmlns="https://github.com/avaloniaui"<br/> xmlns:x="http://schemas.microsoft.com/<br/> winfx/2006/xaml"<br/> xmlns:d="http://schemas.microsoft.com/<br/> expression/blend/2008"<br/> xmlns:mc="http://schemas.openxmlformats.org/<br/> markup-compatibility/2006"<br/> xmlns:local=<br/> "clr-namespace:HappyCoding.TemperatureViewer"<br/> mc:Ignorable="d" d:DesignWidth="800" <br/> d:DesignHeight="450"<br/> x:Class=<br/> "HappyCoding.TemperatureViewer.MainWindow"<br/> x:DataType="local:MainWindowViewModel"<br/> Title="Temperature Measurement"&gt;<br/> &lt;DockPanel&gt;<br/> <br/> &lt;!-- Menu bar --&gt;<br/> &lt;Menu DockPanel.Dock="Top"&gt;<br/> &lt;MenuItem Name="MnuStartMeasuring" <br/> Header="Start Measuring"<br/> Command="{Binding Path=<br/> StartMeasurementCommand}" /&gt;<br/> &lt;MenuItem Name="MnuStopMeasuring"<br/> Header="Stop Measuring"<br/> Command="{Binding Path=<br/> StartMeasurementCommand.Cancel}"<br/> IsEnabled="{Binding Path=<br/> StartMeasurementCommand.<br/> CanBeCanceled}" /&gt;<br/> &lt;/Menu&gt;<br/> <br/> &lt;Grid Margin="15"<br/> ColumnDefinitions="300,10,*"&gt;<br/> <br/> &lt;!-- Current temperature --&gt;<br/> &lt;HeaderedContentControl Grid.Column="0" <br/> Header="Current"&gt;<br/> &lt;TextBlock Name="TxtCurrentTemperature" <br/> Text="{Binding CurrentMeasurement.<br/> TemperatureInDegrees, <br/> StringFormat='{}{0:F2}°'}" <br/> HorizontalAlignment="Center" <br/> VerticalAlignment="Center"<br/> FontSize="40"/&gt;<br/> &lt;/HeaderedContentControl&gt;<br/> <br/> &lt;GridSplitter Grid.Column="1"<br/> HorizontalAlignment="Stretch" <br/> VerticalAlignment="Stretch" /&gt;<br/> <br/> &lt;!-- Details --&gt;<br/> &lt;HeaderedContentControl Grid.Column="2" <br/> Header="Details"&gt;<br/> &lt;Border Classes="GridBorder"&gt;<br/> &lt;DataGrid ItemsSource="{Binding Measurements}"<br/> CanUserResizeColumns="True"<br/> CanUserSortColumns="False"<br/> IsReadOnly="True"&gt;<br/> &lt;DataGrid.Columns&gt;<br/> &lt;DataGridTextColumn Header="Timestamp"<br/> Binding="{Binding TimeStamp, <br/> StringFormat=<br/> 'yyyy-MM-dd hh:mm:ss.fff zzz'}"<br/> Width="275"/&gt;<br/> &lt;DataGridTextColumn Header="Temperature" <br/> Binding="{Binding TemperatureInDegrees, <br/> StringFormat='{}{0:F2}°'}"<br/> Width="125"/&gt;<br/> &lt;/DataGrid.Columns&gt;<br/> &lt;/DataGrid&gt;<br/> &lt;/Border&gt;<br/> &lt;/HeaderedContentControl&gt;<br/> &lt;/Grid&gt;<br/> &lt;/DockPanel&gt;<br/>&lt;/Window&gt;
Auch die beiden MenuItems sind einen Blick wert. Hier sehen wir, wie wir auf einfache Weise mit dem AsyncRelayCommand des CommunityToolkit.Mvvm arbeiten können. Die erste Schaltfläche Start Measuring bindet direkt gegen das Command und würde die Verarbeitung damit starten. Das Command sorgt im Hintergrund automatisch dafür, dass diese Schaltfläche deaktiviert ist, solange die Verarbeitung im Hintergrund läuft. Die zweite Schaltfläche Stop Measuring bindet direkt gegen die Methode Cancel des AsyncRelayCommand. Mehr als diese beiden Schaltflächen brauchen wir nicht, um den asynchronen Vorgang im Hintergrund starten und bei Bedarf wieder abbrechen zu können.Die Anzeige des zuletzt erhaltenen Temperaturwerts ist sehr einfach über ein zentriertes TextBlock-Element gehalten. Spannender dürfte die Anzeige der Historie sein, da wir dafür das bei Avalonia mitgelieferte DataGrid nutzen. Wir binden das DataGrid an die ObservableCollection aus dem ViewModel. Dadurch reagiert das DataGrid sofort auf neue oder gelöschte Einträge. Die Spalten des DataGrids können wir direkt an die Eigenschaften in der Datenquelle binden und dabei auch die bekannte Binding-Syntax nutzen (Listing 4).
Listing 4: Inhalte der Applikation – Anzeige der aktuellen Temperatur und der Historie
CurrentTemperatureView.axaml<br/>&lt;UserControl xmlns="https://github.com/avaloniaui"<br/> xmlns:x="http://schemas.microsoft.com/<br/> winfx/2006/xaml"<br/> xmlns:d="http://schemas.microsoft.com/<br/> expression/blend/2008"<br/> xmlns:mc="http://schemas.openxmlformats.<br/> org/markup-compatibility/2006"<br/> xmlns:local="clr-namespace:HappyCoding.<br/> TemperatureViewer"<br/> mc:Ignorable="d" d:DesignWidth="800" <br/> d:DesignHeight="450"<br/> x:Class="HappyCoding.TemperatureViewer.<br/> Views.CurrentTemperatureView"<br/> x:DataType="local:MainWindowViewModel"&gt;<br/> &lt;TextBlock Text="{Binding CurrentMeasurement.<br/> TemperatureInDegrees, <br/> StringFormat='{}{0:F2}°'}" <br/> HorizontalAlignment="Center" <br/> VerticalAlignment="Center"<br/> FontSize="40"/&gt;<br/>&lt;/UserControl&gt;<br/>DetailsView.axaml<br/>&lt;UserControl xmlns="https://github.com/avaloniaui"<br/> xmlns:x="http://schemas.microsoft.com/<br/> winfx/2006/xaml"<br/> xmlns:d="http://schemas.microsoft.com/<br/> expression/blend/2008"<br/> xmlns:mc="http://schemas.openxmlformats.<br/> org/markup-compatibility/2006"<br/> xmlns:local="clr-namespace:HappyCoding.<br/> TemperatureViewer"<br/> mc:Ignorable="d" d:DesignWidth="800" <br/> d:DesignHeight="450"<br/> x:Class="HappyCoding.TemperatureViewer.<br/> Views.DetailsView"<br/> x:DataType="local:MainWindowViewModel"&gt;<br/> &lt;Border Classes="GridBorder"&gt;<br/> &lt;DataGrid ItemsSource="{Binding Measurements}"<br/> CanUserResizeColumns="True"<br/> CanUserSortColumns="False"<br/> IsReadOnly="True"&gt;<br/> &lt;DataGrid.Columns&gt;<br/> &lt;DataGridTextColumn Header="Timestamp"<br/> Binding="{Binding <br/> TimeStamp}"<br/> Width="225"/&gt;<br/> &lt;DataGridTextColumn Header="Temperature" <br/> Binding="{Binding <br/> TemperatureInDegrees, <br/> StringFormat='{}{0:F2}°'}"<br/> Width="125"/&gt;<br/> &lt;/DataGrid.Columns&gt;<br/> &lt;/DataGrid&gt;<br/> &lt;/Border&gt;<br/>&lt;/UserControl&gt;
Das mitgelieferte DataGrid
Wie bereits angesprochen liefert Avalonia im Standard ein DataGrid. Darauf soll an dieser Stelle gesondert eingegangen werden, da es für professionelle Applikationen ein nahezu unverzichtbares Feature ist. Man darf hier kein DataGrid wie von den großen Komponentenherstellern erwarten, bekommt aber immerhin einen guten Basisumfang an Features. So unterstützt es etwa Virtualisierung, damit nur der sichtbare Teil des DataGrids gerendert wird.Eine einfache Anwendung des DataGrids sehen wir im Beispiel in den Downloads zum Artikel. Man kann es gegen eine beliebige Auflistung binden und Spalten definieren (oder automatisch erzeugen lassen). Es gibt mitgelieferte Spaltentypen für Text, für Booleans und eine DataGridTemplateColumn, bei der die UI-Elemente in den Zellen selbst per Template bestimmt werden können.Gruppier- und Filterfunktionen werden vom DataGrid nicht direkt als Features angeboten, dafür aber über die Klasse DataGridCollectionView bereitgestellt. Letztere wird im ViewModel erzeugt; das DataGrid bindet anschließend dagegen.Ein weiteres Feature, die Alternating Row Colors, können wir nicht direkt am DataGrid aktivieren. Stattdessen nutzen wir dafür das mächtige, an CSS angelehnte Styling-System von Avalonia. Es reicht, den Style aus Listing 5 dafür zu definieren.Listing 5: DataGrid-Style für Alternating Row Colors
&lt;Style Selector="DataGridRow:nth-child(odd)"&gt;<br/> &lt;Setter Property="Background" Value="#20AAAAAA"/&gt;<br/>&lt;/Style&gt;
Avalonia DevTools
Features wie Hot Reload oder als Alternative einen visuellen Designer, wie wir ihn etwa noch aus den guten alten Windows-Forms-Zeiten kennen, fehlen in Avalonia. Mit Avalonia Accelerate (siehe den Kasten Avalonia Accelerate) arbeitet das Avalonia-Team zum Zeitpunkt der Abfassung dieses Artikels genau an diesen Punkten [7].Avalonia Accelerate
Avalonia selbst ist Open Source und damit frei verfüg- und verwendbar, auch für kommerzielle Projekte. Darauf aufbauend bietet das Avalonia-Team seit April 2025 mit dem Produkt Avalonia Accelerate diverse Erweiterungen an, die unter einer Lizenzgebühr verkauft werden.
Im Open-Source-Paket von Avalonia werden schon seit geraumer Zeit integrierte DevTools angeboten, die über das NuGet-Paket Avalonia.Diagnostics bereitgestellt werden. Die beschriebenen Templates binden das NuGet-Paket direkt mit ein. Ein Tastendruck auf [F12] genügt, um das DevTools-Fenster zu öffnen. Die DevTools helfen bei mehreren Aufgaben; so lassen sich Logical Tree und Visual Tree prüfen, ob sie die erwarteten Inhalte aufweisen. Der Logical Tree bildet dabei die Baumstruktur aus den XAML-Dateien ab. Der Visual Tree zeigt, was tatsächlich am Bildschirm gerendert wird – aufgrund von Templating in der Regel deutlich mehr. Bild 4 zeigt, wie die DevTools im vorgestellten Beispiel aussehen.

Avalonia DevTools am gezeigten Beispiel (Bild 4)
Autor
Noch ein Hinweis an alle Leserinnen und Leser, die Hot Reload oder einen Designer vermissen. In den DevTools können Werte von Eigenschaften direkt live geändert werden. Damit sieht man sofort, welche Änderungen zu welchen Effekten auf der Benutzeroberfläche führen.Neben der Anzeige der Baumstrukturen bieten die DevTools auch weitere Features wie ein Tracking der von den Controls ausgelösten Events. Performance-relevante Kennzahlen wie Rendering-Zeit und Layout-Zeit können ebenfalls eingeblendet werden. Unterm Strich sind die DevTools auf jeden Fall mehr als nur einen Blick wert.
Testautomatisierung mit Avalonia.Headless
Um die Beispielapplikation ordentlich abzuschließen, fehlen noch automatisierte Tests. Hierfür gibt uns Avalonia mit Avalonia.Headless ein besonderes Werkzeug in die Hand. Avalonia.Headless ermöglicht es, eine Avalonia-Applikation innerhalb von Tests auszuführen, ohne dafür den Overhead des Renderings zu benötigen.In Listing 6 sehen wir als Beispiel einen Test auf Basis des bekannten Test-Frameworks xUnit und Avalonia.Headless. Hierin erstellen wir zunächst einen Mock des IMeasurementService, öffnen dann das Hauptfenster und simulieren einen Klick auf den Button Start Measurement. Anschließend prüfen wir, ob der erwartete Temperaturwert auf dem UI angezeigt wird.Listing 6: Testautomatisierung mit Avalonia.Headless am Beispiel des aktuellen Messwerts
apublic class MainWindowTests<br/>{<br/> [AvaloniaFact]<br/> public void StartTemparatureMeasurement()<br/> {<br/> // Arrange<br/> var testData = new[]<br/> {<br/> new TemperatureMeasurement(<br/> DateTimeOffset.UtcNow, 10.5)<br/> };<br/> <br/> var mockedMeasurementService = <br/> Substitute.For&lt;IMeasurementService&gt;();<br/> mockedMeasurementService.StartMeasurement(<br/> Arg.Any&lt;CancellationToken&gt;())<br/> .Returns(_ =&gt; testData.ToAsyncEnumerable());<br/> <br/> var mainWindow = new MainWindow();<br/> mainWindow.DataContext = new <br/> MainWindowViewModel(mockedMeasurementService);<br/> mainWindow.Show();<br/> <br/> // Act<br/> var mnuStartMeasuring = mainWindow.GetControl<br/> &lt;MenuItem&gt;("MnuStartMeasuring");<br/> mnuStartMeasuring.SimulateClick();<br/> <br/> // Assert<br/> var txtCurrentTemperature = mainWindow.GetControl<br/> &lt;TextBlock&gt;("TxtCurrentTemperature");<br/> Assert.Equal("10.50°", <br/> txtCurrentTemperature.Text);<br/> }<br/>}
Im Beispiel nutzen wir zusätzlich das NuGet-Paket Avalonia.Headless.XUnit, das sich um das allgemeine Setup des Test-Frameworks und um den Umgang mit dem UI-Thread kümmert. Das Attribut AvaloniaFact kommt aus diesem Paket und markiert Tests, die mit UI-Bestandteilen ausgeführt werden sollen.
Weitere Features
In diesem Artikel haben wir nur einen Bruchteil der Features von Avalonia behandeln können. Speziell für Linux bietet Avalonia noch ein weiteres spannendes Feature an: Es ist möglich, unter Linux direkt in den Framebuffer zu rendern. Dadurch können UI-Applikationen bereitgestellt werden, die keine Desktop-Umgebung wie Gnome oder KDE benötigen. Dieses Feature ist insbesondere für Entwicklungen auf Embedded Linux relevant.Mit diesem und weiteren Features werden wir uns in ausschließlich online veröffentlichten Folgeartikeln beschäftigen, siehe Kasten Sie möchten gerne mehr erfahren?Sie möchten gerne mehr erfahren?
Das freut uns, denn ab Erscheinen des vorliegenden Artikels veröffentlichen wir auf unserer Webseite in unregelmäßigen Abständen zusätzlich Beiträge, die den Artikel fortsetzen und ausschließlich online zur Verfügung stehen. Derzeit geplant sind Beiträge zu Themen wie:
Sie möchten gerne mehr erfahren?
Das freut uns, denn ab Erscheinen des vorliegenden Artikels veröffentlichen wir auf unserer Webseite in unregelmäßigen Abständen zusätzlich Beiträge, die den Artikel fortsetzen und ausschließlich online zur Verfügung stehen. Derzeit geplant sind Beiträge zu Themen wie:
Fazit
Avalonia gibt uns alle notwendigen Werkzeuge in die Hand, um Benutzeroberflächen nicht nur für Linux, sondern auch für andere Plattformen zu entwickeln. Insbesondere Entwickler mit Erfahrung in XAML-basierten Frameworks finden sich schnell zurecht. Aber auch der Umstieg von anderen Frameworks wie dem nach wie vor verbreiteten Windows Forms ist kein Ding der Unmöglichkeit – hier muss allerdings Zeit zum Erlernen der XAML-Syntax und des MVVM-Patterns hinzugerechnet werden.Avalonia gibt uns alle notwendigen Werkzeuge in die Hand, um Benutzeroberflächen nicht nur für Linux, sondern auch für andere Plattformen zu entwickeln. Insbesondere Entwickler mit Erfahrung in XAML-basierten Frameworks finden sich schnell zurecht. Aber auch der Umstieg von anderen Frameworks wie dem nach wie vor verbreiteten Windows Forms ist kein Ding der Unmöglichkeit – hier muss allerdings Zeit zum Erlernen der XAML-Syntax und des MVVM-Patterns hinzugerechnet werden.Gerade der hier im Artikel in den Fokus gerückte Support für Linux dürfte für viele Leserinnen und Leser spannend sein. Linux spielt insbesondere in der Embedded-Welt eine sehr große Rolle. Avalonia kann hier als mächtiges UI-Framework mit ebenso mächtigem .NET-Unterbau punkten. Gerade der hier im Artikel in den Fokus gerückte Support für Linux dürfte für viele Leserinnen und Leser spannend sein. Linux spielt insbesondere in der Embedded-Welt eine sehr große Rolle. Avalonia kann hier als mächtiges UI-Framework mit ebenso mächtigem .NET-Unterbau punkten.Das Avalonia-Team stellt sich aus Sicht des Autors sinnvoll für die Zukunft auf. Man erweitert das Open-Source-Paket mit Avalonia Accelerate um kommerzielle Zusatzkomponenten, die für professionelle Entwickler zusätzliche Produktivität und Features liefern. Auch XPF kann gerade bei einer größeren Migration eines WPF-Projekts spannend sein. Ganz nebenbei finanzieren diese Produkte die Weiterentwicklung am Open-Source-Paket Avalonia selbst, das auch für kommerzielle Projekte kostenlos zur Verfügung steht.Das Avalonia-Team stellt sich aus Sicht des Autors sinnvoll für die Zukunft auf. Man erweitert das Open-Source-Paket mit Avalonia Accelerate um kommerzielle Zusatzkomponenten, die für professionelle Entwickler zusätzliche Produktivität und Features liefern. Auch XPF kann gerade bei einer größeren Migration eines WPF-Projekts spannend sein. Ganz nebenbei finanzieren diese Produkte die Weiterentwicklung am Open-Source-Paket Avalonia selbst, das auch für kommerzielle Projekte kostenlos zur Verfügung steht.Zum Abschluss dieses Artikel sei noch auf den App-Showcase auf der Website von Avalonia [8] verwiesen. Darin listet das Avalonia-Team eine längere Liste von Applikationen, die auf Basis von Avalonia realisiert wurden. Hier bekommt man einen guten Eindruck darüber, was mit Avalonia möglich ist.Zum Abschluss dieses Artikel sei noch auf den App-Showcase auf der Website von Avalonia [8] verwiesen. Darin listet das Avalonia-Team eine längere Liste von Applikationen, die auf BasisFussnoten
- Fabian Hügle, Das bessere WPF, dotnetpro 1/2024, Seite 34 ff., http://www.dotnetpro.de/A2401Avalonia
- Avalonia – Cross-platform WPF, https://avaloniaui.net/xpf
- Avalonia – Running on Raspberry Pi, http://www.dotnetpro.de/SL2506-07Avalonia1
- Avalonia – Set Up an Editor, http://www.dotnetpro.de/SL2506-07Avalonia2
- Tam Hanna, Grafisches UI für WSL, dotnetpro 2/2022, Seite 29 ff., http://www.dotnetpro.de/A2202WSLUI
- Avalonia-Templates auf GitHub, http://www.dotnetpro.de/SL2506-07Avalonia3
- Avalonia Accelerate, https://avaloniaui.net/accelerate
- Avalonia – App Showcase, https://avaloniaui.net/showcase