Cross-Plattform GUI mit C# und Avalonia

Es existieren einige Ansätze, um mit C# gleichzeitig für mehrere Plattformen eine GUI zu entwickeln. Xamarin.Forms etwa ist hier ein sehr bekanntes Beispiel. Etwas unbekannter ist das Framework Avalonia [1]. Hierbei handelt es sich ebenfalls um ein Framework, bei dem die Oberflächen mit Xaml beschrieben und mittels des MVVM-Patterns mit der C#-Logik gekoppelt werden können. Anders als Xamarin.Forms setzt Avalonia auf einen komplett eigenen Rendering-Stack und nicht auf die Standard-Steuerelemente des jeweiligen Betriebssystems. Dadurch ist Optik und Verhalten der Applikationen auf allen Plattformen maximal gleich. Ich persönlich finde diesen Ansatz schon länger sehr spannend und habe mich in den letzten Monaten mehr mit Avalonia beschäftigt. Um für das Framework ein Gefühlt zu bekommen, habe ich eine kleine Applikation zum Senden/Empfangen von TCP/IP-Nachrichten damit gebaut [2]. Das Vorschaubild dieses Artikels ist ein Screenshot dieser Applikation.

Erster Eindruck

Mit entsprechendem Xaml-Background gelingt der Einstieg in Avalonia relativ schnell. Viele Konzepte und Steuerelemente sind sehr ähnlich denen aus WPF. Man stößt lediglich hin und wieder auf Features, welche in Avalonia nicht oder anderes umgesetzt sind. Ein Beispiel ist das x:Name Attribut. In WPF sorgt es dafür, dass im Code-Behind direkt auf das Steuerelement zugegriffen werden kann. In Avalonia muss stattdessen das Attribut Name gesetzt und im Code-Behind nach dem Control mit genau diesem Namen gesucht werden. Nachfolgendes Code-Beispiel zeigt genau das [Code-Beispiel 1]. Ein anderes größeres Beispiel ist das Styling. Hier hat Avalonia mehrere Konzepte aus CSS wie etwa die Selektoren eingebaut.

Im Großen und Ganzen haben mich diese Themen niemals zu sehr aufgehalten. Die wichtigsten Punkte zum Einstieg sind auf der Homepage von Avalonia beschrieben [3]. Ein Regelmäßiger Blick darauf hilft ungemein. Von der Cross-Plattform Fähigkeit bin ich sehr positiv überrascht. Die oben verwiesene Applikation läuft ohne Anpassungen bei mir auf Windows 10 und Ubuntu 20.04.

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TcpCommunicator.TestGui;assembly=TcpCommunicator.TestGui"
        xmlns:localViews="clr-namespace:TcpCommunicator.TestGui.Views;assembly=TcpCommunicator.TestGui"
        mc:Ignorable="d"
        MinWidth="200" MinHeight="200"
        Width="800" Height="550"
        Icon="Assets/Logo.ico"
        d:DataContext="{x:Static local:DesignData.MainWindowVM}"
        x:Class="TcpCommunicator.TestGui.MainWindow"
        Title="TcpCommunicator - Testing UI">

    <!-- ... -->

    <local:DialogHostControl Name="CrtlDialogHost" />

    <!-- ... -->

</Window>
public MainWindow()
{
    AvaloniaXamlLoader.Load(this);

    // Register view services
    var ctrlDialogHost = this.FindControl<DialogHostControl>("CrtlDialogHost");
    this.ViewServices.Add(new ConnectionConfigControlService(ctrlDialogHost));
    this.ViewServices.Add(new MessageBoxControlService(ctrlDialogHost));

    // Load initial main view model
    this.ViewModel = new MainWindowViewModel();
    this.DataContext = this.ViewModel;

    // ...
}

Code-Beispiel 1: Zugriff auf ein Control aus dem Code-Behind

Controls und DataBinding

Von den Controls her sind gefühlt alle wesentlichen Bestandteile eines GUI-Frameworks enthalten. In obiger Applikation verwende ich etwa einige Textboxen, Dropdowns, DataGrids und noch andere Standard-Controls. Das DataGrid ist dabei schon etwas hervorzuheben, besonders da andere GUI-Frameworks ein solches von Haus aus nicht unterstützen (z. B. UWP oder WPF in den ersten Versionen).

DataBindings arbeiten im Wesentlichen auch sehr ähnlich denen aus WPF. Das MVVM-Muster lässt sich gut umsetzen. Einige Unterschiede gibt es dann aber doch. So kann etwa bei einer Bindung auf andere Controls im Pfad mit #[ControlName] begonnen werden. Im nachfolgenden Beispiel wird vom Kontextmenü-Button aus mit dem Pfad #LstProfiles.SelectedItem.CanStop direkt auf das DataGrid mit Namen LstProfiles zugegriffen [Code-Beispiel 2].

<DataGrid Name="LstProfiles" IsReadOnly="True"
          Items="{Binding Profiles}" 
          SelectedItem="{Binding Path=SelectedProfile, Mode=TwoWay}">
    <DataGrid.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Start profile" 
                      Icon="{local:VectorIconExtension Icon={StaticResource ResourceKey=IconStart}}"
                      IsEnabled="{Binding Path=#LstProfiles.SelectedItem.CanStart}"
                      Command="{Binding Path=#LstProfiles.SelectedItem.Command_Start}" />
            <MenuItem Header="Stop profile"
                      Icon="{local:VectorIconExtension Icon={StaticResource ResourceKey=IconStop}}"
                      IsEnabled="{Binding Path=#LstProfiles.SelectedItem.CanStop}"
                      Command="{Binding Path=#LstProfiles.SelectedItem.Command_Stop}" />
        </ContextMenu>
    </DataGrid.ContextMenu>
    <!-- ... -->
</DataGrid>

Code-Beispiel 2: Binding auf eine Eigenschaft eines anderen Elements

Zur Programmierung mit MVVM-Pattern hat Avalonia einige Klassen speziell für das MVVM-Framework ReactiveUI eingebaut [4]. In obiger Beispielapplikation verwende ich dieses Framework auch und hatte dabei keinerlei Probleme. Insgesamt fühlen sich die beiden Frameworks gut integriert an.

Weitere Informationen

Wie oben schon erwähnt eignet sich die Homepage von Avalonia und die dortige Dokumentation für einen Einstieg sehr gut [1]. Die Dokumentation ist zwar noch nicht vollständig, konnte aber in meinem Fall die meisten Fragen beantworten. Eine sehr lange Liste weiterer Informationsquellen und Beispielprojekte wird auf Github im Projekt Awesome-Avalonia gepflegt [5]. Ein Blick auf diese Projektseite offenbart viele interessante Projekte, darunter ganze Applikationen oder auch allgemeingültige Libraries.

Verweise

  1. Offizielle Website von Avalonia
    https://avaloniaui.net/
  2. Testtool für TCP/IP Kommunikation basierend auf Avalonia
    https://github.com/RolandKoenig/MessageCommunicator
  3. Offizielle Dokumentation von Avalonia
    https://avaloniaui.net/docs
  4. Offizielle Website von ReactiveUI
    https://www.reactiveui.net/
  5. Sammlung verschiedener Ressourcen zu Avalonia
    https://github.com/AvaloniaCommunity/awesome-avalonia

Ebenfalls interessant

  1. Custom Window Chrome mit Avalonia
    https://www.rolandk.de/wp-posts/2021/05/custom-window-chrome-mit-avalonia/
  2. Markdown-Dokumente mit Avalonia rendern
    https://www.rolandk.de/wp-posts/2021/08/markdown-dokumente-mit-avalonia-rendern/
  3. PropertyGrid mit Avalonia
    https://www.rolandk.de/wp-posts/2020/08/propertygrid-mit-avalonia/
  4. Das DataGrid von Avalonia
    https://www.rolandk.de/wp-posts/2022/10/das-datagrid-von-avalonia/

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.