RO EN

Desktop UI in .NET: WPF, WinForms, WinUI 3, Avalonia or Uno Platform?

Desktop UI in .NET: WPF, WinForms, WinUI 3, Avalonia or Uno Platform? ✨ Imagine generată cu AI
Doru Bulubașa
19 October 2026
39 views

The production planning application is 12 years old. It is written in WPF, runs on 200 stations in the factory, and does exactly what it needs to. No one complains about it. Then, in a meeting, someone says the phrase that triggers a one-year project: “WPF is a dead technology, right? And it would be good if it also worked on the designers’ Macs.”

Next come the predictable questions. Do we rewrite it in WinUI 3, since it is the “new WPF”? Do we switch to Avalonia so it runs everywhere? Do we make it web-based and get rid of the problem? Or do we leave it alone?

This is the second article in the series about choosing UI technology. In the first article we talked about the web. Now we stay on desktop and in the .NET ecosystem: WinForms, WPF, WinUI 3, Avalonia, and Uno Platform. The variant with web technologies on desktop, namely Electron, Tauri, and Blazor Hybrid, has a separate article, the next in the series.

The first question: Windows only or something else too?

On the web, the decision started from where the UI state lives. On desktop, the first question is simpler and more brutal: does the application need to run outside of Windows? The answer divides the list into two families:

  • Windows only — WinForms, WPF, and WinUI 3. They use Windows components directly, integrate perfectly with the operating system, and have years (or decades) of accumulated experience behind them.
  • Cross-platform — Avalonia and Uno Platform run on Windows, macOS, and Linux, and both can also reach mobile and browsers via WebAssembly. They draw the interface themselves, usually through Skia, instead of using the native controls of each system.

.NET MAUI also often comes up in discussions. It works on Windows and macOS but not on Linux, and its design is primarily intended for mobile. We cover it in the article about mobile applications, where it belongs.

If the honest answer is “Windows only, and it will stay that way,” don’t start with Avalonia just because it sounds more modern. If the answer is “maybe, someday,” it’s worth structuring the code so that porting remains possible. We will see below that this depends more on architecture than on the framework.

The myth “WPF is dead”

WPF and WinForms are not dead. They are stable, and the difference is important. Both are open source, part of .NET, and receive improvements with every version. They won’t get revolutions anymore, but a technology that no longer changes radically is exactly what you want for an application that must run for another ten years.

In the last two .NET versions, WPF received concrete things:

  • Fluent theme, introduced in .NET 9, which brings the Windows 11 look (rounded corners, Mica, light and dark mode) without external libraries. In .NET 10, more and more controls received Fluent styles, although Microsoft explicitly says support is still a work in progress.
  • Short syntax for Grid, a much-anticipated community contribution.
  • A common clipboard API with WinForms, without BinaryFormatter, plus performance optimizations in fonts, dynamic resources, and XAML parsing.
<!-- App.xaml: Fluent theme, following system setting -->
<Application x:Class="Planificare.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             ThemeMode="System">
</Application>

<!-- .NET 10: short definitions for rows and columns -->
<Grid ColumnDefinitions="2*, *, Auto" RowDefinitions="Auto, *, 40" />

WinForms also received attention: asynchronous APIs for forms and controls in .NET 9, experimental support for dark mode, improvements for the designer and accessibility. For an internal application with forms and tables, WinForms remains the fastest way to go from zero to something functional, especially thanks to the visual designer.

// .NET 9+: update the UI asynchronously from code, without BeginInvoke and callbacks
private async void btnImport_Click(object sender, EventArgs e)
{
    var result = await _importService.ImportaComenziAsync();
    await lblStatus.InvokeAsync(() => lblStatus.Text = $"Imported: {result.Count}");
}

Their limits are known and will not disappear: they run only on Windows, WinForms layout is rigid compared to XAML, and support for high DPI monitors still requires attention for old applications. But “it’s old” is not a reason to rewrite. “We can no longer deliver what the business asks” is.

WinUI 3: the native future on Windows, with asterisks

WinUI 3 is the framework Microsoft recommends for new Windows applications. It is part of the Windows App SDK, so it no longer depends on the operating system version, as was the case with UWP. It offers modern Fluent controls, performant rendering, and integration with everything Windows 11 means. And Microsoft uses it in its own products.

In 2026, the project moved visibly. Windows App SDK moved to the 2.x line, with monthly releases, and WinUI development moved to GitHub as part of the open source plan announced in 2025. If you followed WinUI in past years and gave up because of the slow pace, it’s worth looking again.

The asterisks remain real, however, and must be known before starting:

  • There is no visual XAML designer. You work with Hot Reload and Live Preview. For a team used to the WPF or WinForms designer, the difference is noticeable.
  • There is no official DataGrid. For business applications full of tables, this is a serious problem. There are alternatives from the community and commercial suites, but it is a decision you must make from the start.
  • Many open issues remain on GitHub, and some common WPF scenarios still require workarounds.
  • Windows only. If macOS or Linux ever appear on the list, WinUI won’t help you.

The practical conclusion: WinUI 3 is a good choice for a new Windows application, consumer or utility type, that must look and behave like a Windows 11 application. For a business application with dozens of table screens, or for an existing working WPF application, the migration cost is hard to justify today.

Avalonia: cross-platform XAML, with a WPF flavor

Avalonia is probably the most mature .NET cross-platform desktop framework. The syntax and concepts are very close to WPF: XAML, styles, data binding, templated controls. However, it is not a 1:1 copy, and the differences matter when porting.

The fundamental difference from WPF and WinUI is that Avalonia draws every pixel itself, via Skia. The advantage: the application looks identical on Windows, macOS, and Linux, and layout bugs do not depend on the operating system. The disadvantage: it does not use native controls, so it will not automatically look like a macOS application on a Mac, unless you choose or build a suitable theme.

Avalonia 12, released in April 2026, was the first major release after almost three years. A few relevant things:

  • targets .NET 10, and compiled bindings are enabled by default, so binding errors appear at compile time, not in production;
  • large performance gains in rendering and startup on Android with NativeAOT;
  • native accessibility on Linux, a rare thing in the .NET world;
  • integrated navigation pages, useful for mobile scenarios.

A detail not to miss is licensing. The framework remains open source under the MIT license, and you can use it free and commercially. However, with version 12, the offer for tooling and premium components changed: the Community level is now for non-commercial use only, and for commercial use, advanced tools and premium controls are paid. Check exactly what you use before budgeting the project.

There is also Avalonia XPF, a separate commercial product that runs existing WPF applications on macOS and Linux with minimal changes. For the scenario in the introduction, a large WPF application that must also reach Mac, it is the only option that avoids rewriting, and the license cost must be compared with the cost of a manual port.

Uno Platform: WinUI, but everywhere

Uno Platform starts from a different idea: it implements the WinUI API on all platforms. You write XAML and C# like in WinUI 3, and the application runs on Windows, macOS, Linux, iOS, Android, and in the browser via WebAssembly. On Windows, it can even use WinUI, and on other platforms it draws the interface via Skia, like Avalonia.

The 6.x series consolidated the Skia-based approach. Version 6.6, from August 2026, brought Native AOT publishing on all desktop and mobile platforms, an optional Vulkan backend for rendering, less repetitive XAML code, and wider coverage of WinUI APIs. The core is open source, and around it is Uno Platform Studio, with commercial design and productivity tools.

Uno is the natural choice in two situations: you already have code or experience in WinUI/UWP and want to reach other platforms, or you want a single codebase covering desktop, mobile, and web. The price is that you depend on how complete and correct the WinUI reimplementation is on each platform, and for less common scenarios it’s worth checking in advance.

The same screen, three frameworks

The best way to see how close these frameworks are is a concrete example. Let’s take a small screen with a list of orders and a reload button. First, the ViewModel, written with CommunityToolkit.Mvvm:

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;

public partial class ComenziViewModel(IComenziService service) : ObservableObject
{
    public ObservableCollection<Comanda> Comenzi { get; } = [];

    [ObservableProperty]
    private bool seIncarca;

    [RelayCommand]
    private async Task ReincarcaAsync()
    {
        SeIncarca = true;
        Comenzi.Clear();
        foreach (var c in await service.GetComenziAsync())
            Comenzi.Add(c);
        SeIncarca = false;
    }
}

This code runs unchanged in WPF, WinUI 3, Avalonia, and Uno. Differences appear only in XAML.

WPF

<StackPanel>
    <Button Content="Reload" Command="{Binding ReincarcaCommand}" />
    <ListBox ItemsSource="{Binding Comenzi}" DisplayMemberPath="Numar" />
</StackPanel>

Avalonia (with compiled bindings)

<StackPanel x:DataType="vm:ComenziViewModel">
    <Button Content="Reload" Command="{Binding ReincarcaCommand}" />
    <ListBox ItemsSource="{Binding Comenzi}">
        <ListBox.ItemTemplate>
            <DataTemplate x:DataType="m:Comanda">
                <TextBlock Text="{Binding Numar}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</StackPanel>

WinUI 3 and Uno Platform

<StackPanel>
    <Button Content="Reload" Command="{x:Bind ViewModel.ReincarcaCommand}" />
    <ListView ItemsSource="{x:Bind ViewModel.Comenzi}">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="m:Comanda">
                <TextBlock Text="{x:Bind Numar}" />
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackPanel>

The lesson is more important than syntax differences: portability depends on architecture, not on the framework. If the logic is in ViewModels and services, moving to another framework means rewriting views, not the application. If the logic is in code-behind, in 300-line click handlers, any migration becomes a complete rewrite.

How to modernize a WPF or WinForms application without rewriting it

Back to the planning application from the introduction. A complete rewrite is almost always the most expensive and riskiest option. There is a step-by-step path, each with its own value:

  1. Move from .NET Framework to .NET 10. You gain performance, long-term support, modern C#, and access to the entire current ecosystem. For WPF and WinForms, migration is usually much simpler than it seems, and Microsoft modernization tools automate much of it.
  2. Introduce a generic host and dependency injection. With Microsoft.Extensions.Hosting you get configuration, logging, and DI just like in ASP.NET Core. The code becomes testable, and services can be shared with a possible backend or a web version.
  3. Remove logic from code-behind. Screen by screen, move code into ViewModels and services. This is the longest step, but it makes all other options possible later.
  4. Modernize the look. Fluent theme in WPF or dark mode in WinForms changes user perception a lot, with little effort.
  5. Add new features with new technologies. A new screen can be built as a Blazor component hosted in the existing application via BlazorWebView. Details come in the next article.
  6. Only then decide if cross-platform is needed. With separated logic, porting views to Avalonia or Uno is a manageable project. If that is not feasible either, Avalonia XPF remains the fallback option.

After each step, the application remains in production and functional. It’s the same idea as the strangler fig pattern from the microservices world: you replace piece by piece, not all at once.

Decision table

Situation First choice Serious alternative
Existing WPF application, Windows only WPF on .NET 10, modernized gradually —
Existing WinForms application, internal tool WinForms on .NET 10 Gradual migration to WPF
New internal tool, fast, Windows only WinForms or WPF Blazor Server (internal web)
New application, native Windows 11 look WinUI 3 WPF with Fluent theme
New business application, many tables, Windows only WPF WinUI 3 + commercial control suite
Desktop on Windows, macOS, and Linux Avalonia Uno Platform
Desktop, mobile, and web from the same codebase Uno Platform Avalonia
Large WPF application that must reach Mac Avalonia XPF Gradual port to Avalonia
Existing experience in UWP/WinUI, need cross-platform Uno Platform Avalonia

The most common mistakes I see

  • Rewriting because the technology is “old.” A working WPF application does not have a technical problem. At most, it has a perception problem, which can be solved much cheaper.
  • WinUI 3 for applications full of tables, without checking. The lack of an official DataGrid usually appears in the third week of the project, not the first day.
  • Treating Avalonia as identical to WPF. Concepts transfer, code not always. Styles, some controls, and some APIs are different. Plan time for learning.
  • Cross-platform “just in case.” If all users are on Windows, a cross-platform framework adds complexity without benefit. Clean architecture keeps the option open at a much lower cost.
  • Logic in code-behind. It is the most expensive mistake in the long term, regardless of framework, because it blocks any future migration.

Summary

  • WinForms and WPF are stable, not dead. On .NET 10, with the Fluent theme and modern C#, they remain excellent choices for Windows business applications.
  • WinUI 3 is the native direction on Windows and moved a lot in 2026, but the lack of a designer and an official DataGrid matters in business applications.
  • Avalonia is the most mature cross-platform desktop option, with syntax close to WPF. The framework is free, but commercial tooling has become paid for commercial use.
  • Uno Platform brings the WinUI API to all platforms, including mobile and web.
  • MVVM and separation of logic matter more than the chosen framework because they decide how expensive the next migration will be.

In the next article in the series, we stay on desktop but change approach: desktop applications built with web technologies. Electron, Tauri, WebView2, and Blazor Hybrid, through which the Blazor components from the first article can reach an existing WPF or WinForms application. When it’s worth delivering a web page as a desktop application and when not.