Skip to content

nostdahl.com

A technology journal
Article archive
← All articles
Programming

Avalonia: One UI Codebase from a Windows Desktop to a Raspberry Pi Without a Desktop

Bjørn T. Nøstdahl

~6 min read

Our products have screens in odd places: on a safe deposit terminal in a bank, on a hub in a back office, on a service engineer's laptop, and increasingly in a browser tab for someone who just wants to look. For years that meant three user interfaces: VCL on Windows, web for the browser, and something embedded for the devices. Two years ago we started moving the new ones to Avalonia, and it has turned out to be the most flexible UI framework I have used. This article is about what that flexibility looks like and what it costs.

What Avalonia is

Avalonia is an open-source, MIT-licensed UI framework for .NET. If you have written WPF, you will feel at home: XAML for layout, data binding, styles, MVVM. The difference is where it runs. One codebase targets Windows, macOS and Linux desktops, iOS and Android, WebAssembly in the browser, and embedded Linux with no windowing system at all.

It does this by not using native controls. Every button, list and text box is drawn by Avalonia itself through Skia, the same graphics library behind Chrome and Flutter. That is the opposite of the choice the Lazarus LCL makes, which I compared in the Delphi or Lazarus article: the LCL gives you a real native button on each platform and small layout differences to chase; Avalonia gives you pixel-identical output everywhere and no native look. For a kiosk, a terminal or a tool, identical is what you want.

Flexibility one: no desktop required

This is the one that sold me. An Avalonia application can run on a Linux board with no X11, no Wayland and no desktop environment, drawing directly to the framebuffer or through DRM, with touch input from the kernel. The board boots, the application starts, and the screen shows your UI. There is nothing else running.

For a device with a touchscreen, that removes an entire layer. No window manager to configure, no desktop session to keep alive, no browser in kiosk mode to babysit. The same binary that a developer runs in a window on a Windows laptop runs full-screen on the device, with the platform chosen at startup. I built a native dashboard for a Raspberry Pi in Lazarus last year and liked it; Avalonia does the same job with the team's C# skills and without a desktop on the Pi at all.

Flexibility two: styling like a stylesheet

Avalonia's styling system uses selectors that look a lot like CSS: by control type, by class, by name, by pseudo-class such as pressed or disabled, and by position in the tree. A style file can restyle an entire application. Our terminal UI and our desktop tool are the same views with two style files: large touch targets, high contrast and no hover states on the device; compact and mouse-friendly on the desktop. The view models and the XAML do not know which one they are running under.

xml
<Style Selector="Button.primary:pointerover">
  <Setter Property="Background" Value="{DynamicResource AccentHover}" />
</Style>
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
  <Setter Property="Background" Value="{DynamicResource AccentSelected}" />
</Style>

Control themes go further and replace the entire template of a control, which is how you make a list box look like a row of cards without writing a new control.

Flexibility three: draw what you need

When a control does not exist, you draw it. Override the render method, get a drawing context, and put lines, text and geometry on screen; for anything heavier, drop down to Skia directly. Our telemetry chart for device status is a few hundred lines of custom drawing, and it runs identically on the terminal, the desktop and in the browser, because the drawing code does not care which one it is on.

Flexibility four: the browser, for free

The same application compiles to WebAssembly and runs in a browser tab. We use it for demos and for a read-only remote view, not as the main web product, because the download is large and startup is slower than a web page should be. But it costs nothing to add, and showing a customer the real terminal UI in a browser link has ended more than one argument about what the device will look like.

Flexibility five: UI tests without a screen

Avalonia ships a headless platform. Tests create windows, click buttons and read what is on screen with no display attached, which means they run in the same Azure Pipelines agents as everything else. For a team that already runs Playwright against the React frontends, having the native UI testable in the same pipeline closed a gap that had bothered me since the DevSecOps article.

Where it costs you

Nothing is free, and I want to be as honest about this as I was about Lazarus.

  • It does not look native. On a desktop, some users notice. On a device or a kiosk, nobody cares, and that is where we use it most.
  • Accessibility and platform integration lag behind native toolkits on some targets. If screen readers are a requirement, test early.
  • Mobile is younger than desktop. It works, but expect to find the edges yourself.
  • The designer is a previewer, not a form designer. Coming from Delphi, you will miss dropping components on a form. You write XAML, and the previewer shows it.
  • Software rendering on a small board is fine for forms and charts, not for animation. Know what your board can do before promising a fluid interface.
  • Binary size and startup are acceptable on a device and a desktop, and merely tolerable in the browser.

How it compares

AvaloniaFireMonkey (Delphi)LCL (Lazarus)WPF
LanguageC#Object PascalObject PascalC#
Desktop targetsWindows, macOS, LinuxWindows, macOS, LinuxWindows, macOS, Linux and moreWindows only
MobileiOS, AndroidiOS, AndroidLimitedNo
BrowserWebAssemblyNopas2js, separatelyNo
Embedded Linux without a desktopYes, framebuffer and DRMNoWith a display serverNo
RenderingOwn controls via SkiaOwn controls, GPUNative widgetsOwn controls, DirectX
StylingSelectors, control themesStylesPer widget setStyles, templates
Headless UI testsBuilt inThird-partyLimitedThird-party
LicenceMITCommercialOpen sourceMIT

If the team writes Pascal, FireMonkey and the LCL remain good answers, and I keep using both. If the team writes C# and the product has to run on a device, a desk and a browser from one codebase, I have not found anything that matches Avalonia.

Questions for you

  • Have you run a UI framework on a board with no desktop? What did you have to give up?
  • Native look or identical look: which do your users actually notice, and on which devices?
  • For those who moved from WPF or Delphi to Avalonia, what did you miss most in the first month, and did it last?

Email me at bjorn.nostdahl@nostdahl.com. I would especially like to hear from anyone running it on hardware smaller than a Raspberry Pi.

Continue exploring
← All articlesMore in Programming →