Wednesday, 22 February 2023

Avalonia and Cross Platform .NET UI

A big advantage of .NET Core is the ability to write code once that can run on both Windows and Linux. This of course has been done before with Java but for those who use C# this was a huge step forward.

Unfortunately the existing UI Frameworks, Winforms and WPF were not supported on Linux so applications like the SuperMarketPlanner, although they were upgraded to .NET Core, were only able to run on Windows. 

Fortunately though .NET Core, now being open source is building a larger ecosystem and one such addition is Avalonia. This is a modern UI framework that can target both Windows and Linux. 

I initially played around before deciding to follow the excellent tutorial to build a Music Store App.

The framework is similar to WPF. We have a markup file to describe how the UI components are arranged, the View. A ViewModel is then setup to define how to bind the view to properties and commands.

As an example I setup a simple test that used a button to load and display an image. The view below shows the Button and Image elements, with the Image Bound to the Photo property:

<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:AvaloniaTestApp.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AvaloniaTestApp.Views.MainWindow"
Icon="/Assets/avalonia-logo.ico"
Title="AvaloniaTestApp">
<Design.DataContext>
<vm:MainWindowViewModel/>
</Design.DataContext>
<StackPanel>
<Button Content="Load Image" Command="{Binding LoadImageCommand}" HorizontalAlignment="Right" VerticalAlignment="Top" />
<Image Stretch="Uniform" Width="400" Source="{Binding Image}"/>
</StackPanel>
</Window>


And the ViewModel code sets up the property to the bitmap. There is a method to load the image from a file that's called from a button command (see the Music Store App demo for how to setup the command)

private Bitmap? _image;
// Property that's bound to the View
public Bitmap? Image
{
get { return _image; }
private set => this.RaiseAndSetIfChanged(ref _image, value);
}
// This can be called for example from a button command
public async Task LoadImage()
{
await using (var imageStream = new FileStream(@"Path\To\Image", FileMode.Open))
{
Image = await Task.Run(()=> Bitmap.DecodeToWidth(imageStream, _imageWidth));
}
}


And here is the application running on Windows...


And Ubuntu...




No comments:

Post a Comment