Comparison of technologies for writing window applications

Once in one chat there was an idea to compare different technologies for writing window applications. In particular, for Windows. Using the means of the C# language. That is, of course, you can do this with C++, Python, Rust, JS and other languages, but we are sharpists, we are more interested in what we can use ourselves without changing the programming language.

So, windowed applications in C#. They can be written using:

  • Windows Forms

  • WPF

  • WinUI

  • .NET MAUI

  • Avalonia

  • Uno Platform

Such a variety of frameworks is due to the fact that the language evolved, approaches changed, technologies improved. And at some point, third-party developers joined the development and Avalonia and Uno were added to Microsoft's tools.

So, in order to start writing window applications, you need to decide which technology to use. But, in addition, it would be good to understand where the application will be used – only on Windows or is there a need to run on MacOS and Linux. Or maybe also on IOS and Android?

In general, in this article we will try to understand the capabilities of these frameworks and compare them. We will compare them according to the following criteria:

  • Platforms where the application can be launched

  • Complexity of development

  • Resources consumed

  • Launch speed

For the sake of correctness of comparison, we will use the same logic for all applications. Moreover, we will try to make them as similar to each other as possible.

Part 1. Logic

So, in order not to compare empty applications without any functionality, and at the same time not to overload with unnecessary content, I implemented the simplest version of the game Tic Tac Toe. The logic of the game itself is simple: there are two players X and O. Player X starts, then O goes. The first one to collect three of his symbols in a row wins, if after the last move there is no winner, then it's a draw. The game is played from one device, in turns.

The entire logic code is almost identical from application to application, with the exception of some features in different frameworks. For example, Avalonia does not have a built-in mechanism for displaying pop-up windows (Message Box) and in order for it to appear, you need to install an additional package. Not difficult, but a nuance. In some frameworks, you cannot directly count buttons from the interface and you need to come up with workarounds.

It is also important to note that everywhere except WinForms it is good to use the MVVM pattern, but for the simplest application I did not bother and everything works directly.

Part 2. User interface

UI development in C# applications does not differ much from each other. The only one that stands out is WinForms, where interface elements are usually placed manually in a graphical editor, which in turn is very friendly to people without experience.

It is also possible to place elements manually in WPF, but it is still recommended to build the interface manually by setting parameters in the page code.

The lack of interactive display of the obtained result in MAUI and WinUI complicates the process of developing the appearance – you can see the result only after assembly and launch. Yes, there is such a function as Hot Reload, when changes will appear after a hot reboot without restarting the entire application, but it is also not without nuances. Firstly, during a hot reboot, the state of the application is lost (if it is not saved separately somewhere), for example, during the game there are already several crosses and zeros, then after Hot Reload they are “forgotten”. Secondly, it happens that the changes are not displayed and you still have to restart the application. Not a bug, but a feature, as they say.

In general, you can get used to everything, and even then it becomes difficult to return to WinForms – you want to configure everything yourself.

Ultimately, the interface of the finished applications turned out to be like this:

  • Windows Forms

    WinForms

    WinForms

WPF

WPF

WinUI

WinUI

MAUI

MAUI

  • Avalonia

    Avalonia

    Avalonia

  • Uno Platform

    Uno Platform

    Uno Platform

I didn't try hard to make them identical, because there was no such task, and the color of the buttons will not affect subsequent measurements. Moreover, you can notice that some of them are similar to each other. This is the result of using WinUI components in them. MAUI additionally uses branded fonts and MaterialDesign – that's why the buttons have rounded corners. And WinUI also pulls the main theme from the system, and that's why everything except WinForms and WPF is dark. Accordingly, in the first two, you need to separately add processing of the light/dark theme.

Part 3. Platforms

Here we come to the global capabilities of all frameworks. So, the table below shows what platforms the application can be built for.

Frameworks

Windows

MacOS

Linux

Android

iOS

WinForms

+

WPF

+

WinUI

+

.NET MAUI

+

+

+

+

Avalonia

+

+

+

+

+

Uno Platform

+

+

+

+

+

As we can see, which is quite logical, all six frameworks allow writing windowed applications for Windows. But there are differences. They are hidden both in the cross-platform nature of MAUI, Avalonia and Uno, and in the components that are used to render the graphical interface.

We won't go too deep into the details, but it's worth knowing that MAUI Avalonia uses WinUI components, while Uno can implement a windowed application in both the WPF and WinUI variants. This can be useful in some cases.

In addition, the native WinForms, WPF and WinUI frameworks differ technically:

– Windows Forms uses GDI+ to render the interface

– WPF uses DirectX for rendering

– WinUI uses DirectX/DirectComposition for rendering

There is also a difference in the components, libraries and approaches used. In other words, all the technologies presented in the article have many differences with similar functionality even within the same target platform.

Part 4. Resources and startup speed

Now we have come to the part of the “research” where numbers come into play. In this part, we will look at how much space applications take up in RAM. We will compare in two modes: in Visual Studio in Debug mode and in the task manager running the published application (Release configuration).

WinForms

WPF

WinUI

.NET MAUI

Avalonia

Uno Platform

Debug (Mb)

15

80

80

132

82

62

Release

(Mb)

6.1

19.6

26.2

54.4

32.6

13.6

These values ​​are obtained from Visual Studio when debugging each application individually.

But I decided to check the release build more thoroughly. In addition, the task was to check the startup speed of each application. It is clear that all of them with such meager functionality should not load for a long time, but there are differences in speed.

To collect the data, I wrote a PowerShell script that launches the application, waits for it to load (become responsive), waits an additional couple of seconds in case of incomplete loading, and shuts down the application. And so on 100 times in a row for each application. Along the way, the script collects data on the amount of memory used and calculates the startup speed.

During the tests, I noticed that some applications take up a significant amount of memory. I looked for information about what this could be related to and found an option where the responsibility was placed on delayed garbage collection. Doubtful, but ok – I added a forced GC call to the script.

[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

$apps = @(
"PATH_TO_TicTacToeUno.exe",
"PATH_TO_TicTacToeMAUIApp.exe",
"PATH_TO_TicTacToeWPF.exe",
"PATH_TO_TicTacToeAvalonia.Desktop.exe",
"PATH_TO_TicTacToeWinUI.exe",
"PATH_TO_TicTacToeWinForms.exe"
)

$results = @{}

foreach ($app in $apps) {
$appName = [System.IO.Path]::GetFileNameWithoutExtension($app)
$times = @()
$memoryUsages = @()

for ($i = 1; $i -le 100; $i++) {
Write-Host "Starting $appName (Iteration $i of 100)..."
$start = Get-Date
$process = Start-Process $app -PassThru

# Ждем, пока главное окно приложения не станет отзывчивым
while (-not $process.MainWindowHandle) {
Start-Sleep -Milliseconds 100
}

$end = Get-Date
$duration = ($end - $start).TotalSeconds
$times += $duration

# Измеряем использование памяти
Start-Sleep -Seconds 2 # Даем приложению время полностью загрузиться
$memory = (Get-Process -Id $process.Id).PrivateMemorySize64 / 1MB
$memoryUsages += $memory

# Закрываем приложение
Stop-Process $process.Id -Force
while (-not $process.HasExited) {
Start-Sleep -Milliseconds 100
}

# Освобождение ресурсов и сборка мусора
$process.Dispose()
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

# Ждем немного перед следующим запуском
Start-Sleep -Seconds 5
}

$averageTime = ($times | Measure-Object -Average).Average
$averageMemory = ($memoryUsages | Measure-Object -Average).Average
$results[$appName] = @{
Time = $averageTime
Memory = $averageMemory
}
}

# Вывод результатов
Write-Host "`nResults after 100 launches:"
$results.GetEnumerator() | Sort-Object {$_.Value.Time} | ForEach-Object {
Write-Host ("{0,-30} : StartUp Time: {1:N3} seconds, Memory Usage: {2:N2} Mb" -f $_.Key, $_.Value.Time, $_.Value.Memory)
}

I would also like to note that I ran the script several times with some changes, in particular I took memory data from both process.WorkingSet64 and process.PrivateMemorySize64, and also duplicated the tests in a Python script to be on the safe side. In total, there were about 500-600 launches of each application, which generally gives a fairly clear picture.

Also, before showing the results, I will make a reservation regarding the WinUI application – it caused the most trouble, oddly enough, since it launched reliably in VS and even tests were carried out in an already finished and installed application, but in fact it did not launch. An error with Windows.ui.xaml.dll, caused for some unknown reason, did not give me peace for several days. Several tests were conducted with this “feature”. Nevertheless, globally, after the problem was solved, nothing in the numbers changed dramatically.

So, below are screenshots from PowerShell:

  1. 1 launch:

PowerShell - 1 run

PowerShell – 1 run

  1. 5 launches

    PowerShell - 5 launches

    PowerShell – 5 launches

  2. 10 launches:

PowerShell - 10 launches

PowerShell – 10 launches

  1. 100 launches:

PowerShell - 100 Launches

PowerShell – 100 Launches

  1. 10 Python runs:

Python - 10 runs

Python – 10 runs

The results are interesting. There are differences in startup speed, but essentially at the level of error – they all start quickly. Moreover, when you watch the script execution process live, it is noticeable that the first 3-5 times the UI loads longer than the subsequent ones. This is due to caching at the OS level. True, this is more relevant for ClickOnce assemblies, but it is also noticeable on WInUI, which I launched without installation.

The situation with memory is much more interesting. Here, there is a clear leader – unexpectedly – Uno Platform, and a clear outsider – WPF.

The most recent test, in which everything worked and did not crash – 100 iterations in PowerShell. There is also the minimum value of the resources involved – on average 1.46 MB. This is also most likely related to some caching, because in other tests it consistently required ~ 8 MB.

But always, in all tests, WPF ate up the most memory. Most likely, this is due to the fact that “shared resources” used by other applications at the same time in the system are taken into account. At least, this is why I made a change to the script to get data from PrivateMemorySize64, but the tests showed that there was no difference. Is there a mistake here? Yes, it is quite possible. But the Python script also showed the same data, so I am still inclined to think that WPF is indeed the most resource-intensive framework.

But I assumed that it would be MAUI. And in terms of resources used, MAUI was second only to WPF in terms of memory used. At the same time, MAUI had the lowest startup speed in all tests.

Part 5. Development complexity

In fact, it is impossible to estimate the complexity of development in each specific framework. When it, the framework, is new to you, it is difficult. Over time, it becomes much easier. However, as in any other business. Therefore, it is impossible to say unequivocally that it is more difficult to develop a desktop application in MAUI than in WinUI, for example.

But WinForms is definitely the easiest of the presented frameworks to master. There are two explanations for this – the graphical editor (designer) and the lack of need to use the MVVM pattern and an infinite number of bindings.

But again, it all depends on the complexity of the project. In my test project, everything was equally easy. Even taking into account that I had no experience with Uno and WinUI until recently.

Part 6. Assembly and deployment

A few words about how difficult it is to prepare a project for publication and use it later.

Overall, the process shouldn't be too difficult anywhere. In practice, there are many nuances. In particular, WinUI unexpectedly presented unpleasant surprises. I found many different solutions to the problem, but none of them helped and, ultimately, I launched the test application from the Release folder. The rest of the applications were assembled and installed without problems.

Additionally, you should keep in mind that the application requires a certificate. That is, yes, you can not sign the application at all, but it will be more difficult to distribute – Windows on third-party computers will swear at installation. Therefore, it is worth signing the application with at least a test certificate. But a full certificate costs money, and in the current conditions you also need to find where to buy it.

Of course, these are all nuances and there are probably some ways to solve the issue, and the issue itself is not of primary importance. But it is worth knowing about it.

Part 7. Summary

Well, it's worth summing up our small comparative study. For whom was it done? For those who were not involved in developing windowed applications, but would like to understand what current technologies are. And we have considered these technologies. Very superficially, of course, but the task of writing an encyclopedia on how to write a windowed application with all the subtleties was not set. But it is quite possible to draw a conclusion about where to start rolling into the world of developing applications for Windows, and then other platforms.

My vision, based on what I had previously and what I have now, is this:

– It is worth starting with the base – with Windows Forms. Yes, the technology is not new, it can even be considered obsolete. But it is alive and functional. Many techniques can be worked out on it, and it is quite possible to assemble a really complex application with a graphical interface. What's more, it has been done, done a lot and for a long time.

– The second step is to study WPF. This technology will allow you to develop an understanding of the MVVM pattern, interface design in XAML and will open the way to more modern stacks.

– From here on the path is open to everything. I was pleasantly surprised by the capabilities of the Uno Platform, I didn't expect it. In addition to the fact that you can write an application for any current OS, including mobile, this technology also, as it turns out, is very careful with resources. Not that this is very critical, but when the project is large and you need to fight for performance, an application that consumes fewer resources will look preferable in my opinion.

Another important feature is the ability to assemble the Uno app design in Figma and import the interface code directly into the project as XAML code! None of the other frameworks reviewed have this.

And finally, a few words about the size of the application on disk.

PowerShell - Size of folders with installers

PowerShell – Size of folders with installers

Installers are located in folders here. WinForms – expectedly, the “lightest” application. MAUI – no comments. Although no, I will clarify that the MAUI assembly does not have builds for other platforms, that is, this is a Windows application.

That's all for now. Draw your own conclusions, try developing windowed applications and good luck!

PS the source code of all projects is available at the link: https://github.com/algmironov/WinAppFrameworksComparison

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *