Developing Desktop Apps for Windows: XAML Islands vs WinUI vs UWP

Universal Windows Platform overview and WinUI logo

Recently we had the opportunity to work developing a desktop application for Windows and macOS from scratch with the goal of having the same UI/UX and features within each of the applications deliver for each platform. In this article we will focus on the Windows application as we found super interesting that there are many options available to tackle it, although with different challenges and constraints between them.

As the Windows and .NET ecosystem is continuously evolving there are several options to choose from when you are starting something from scratch, and you need to understand which is the best fit for your scenario. As this might be a bit confusing for someone at the beginning of their journey, we decided to create this article to give you an overview of the alternatives at this moment and what is coming next.

Before starting, let me mention the team that work on this, contributing as well as giving feedback: Ignacio Boada, Matías Nicolás Gesualdi, Gabriela Gutierrez, Mauro Krikorian, Sebastian Rial and Juan Pablo Tomasi.

Terminology for “desktop applications”

There is an important terminology difference that you need to understand as part of this kind of development between what is call Desktop Apps and Windows Apps.

  • Desktop Apps are applications that are built with frameworks previous to UWP, including WPF, WinForms, or any other Win32 application, and its name is meant to reflect the fact that they only run on Desktop devices.
  • Windows Apps are instead applications that were built using UWP, and the name means that it runs on any Windows device, but it must be remarked though, that UWP is only compatible with Windows 10 versions.

What is WPF?

Windows Presentation Foundation (WPF) is a GUI (Graphical User Interface) Framework for creating desktop applications with .NET Framework. WPF was released back in 2006 as a successor to WinForms. It introduced the XAML language for declaratively defining the UIs. It also introduced the use of Direct3D for rendering. This allows Windows to offload some graphics tasks to the GPU, reducing the workload on the computer’s CPU. It did succeed, although partially because many developers thought that WinForms remained the easiest way to develop a desktop application if you did not care too much about the UI. For more information on WPF, you can read the official documentation.

.NET Core 3.0 has added support for Desktop Applications, so now it is possible to create WPF applications using .NET Core instead of .NET Framework. Here is the link to the official open source “WPF with .NET Core” repository.

What is UWP?

UWP (Universal Windows Platform) is a platform for creating client applications for Windows, that can run on Desktop, mobile, and other devices like Xbox and HoloLens. It is specific for Windows 10 and Windows 10 Mobile (and other variants like Windows 10 S). It uses the Windows Runtime (WinRT) APIs, that are distributed together with Windows updates to take your applications to the next level. For more information about UWP, you can read the official documentation.

WPF vs UWP

So, what are the differences between WPF and UWP? They are both quite similar and quite different. They are similar in that they use XAML to define the UI views, and even the name of most controls is the same, although there are some differences, among others:

  • Compiled data bindings in UWP
  • Importing namespaces in XAML
  • Multi Binding in WPF

A full list of considerations when porting WPF to WinRT XAML can be found in this article but generally speaking, the XAML code made for WPF won’t work in UWP as is, and vice-versa.

They are different in that UWP was conceived mostly for mobile devices, then adapted for desktop ones. This results in many differences, some of which we have encountered in our development, and will be covered in this post.

The .NET Framework 4.6 and .NET Core 5 high level architecture diagram
.NET Architecture

Benefits of WPF compared to UWP

  • It is a more stable framework proven by time. Although now UWP also has some years under its belt, it is in current active development, so it has changed a lot in those years.
  • It has more resources, 3rd party components, and developers working on it. WPF has had more adoption when it was released compared to UWP.
  • It also runs on other lower Windows versions like Windows Vista, Windows 7, any version of Windows 10, or even Windows XP, compared to UWP that only runs on Windows 10.
  • Full access to any resource the user has permissions for. UWP instead runs in a containerized environment that restricts access for certain resources (i.e., File system, Global registry, etc.).
  • Powerful styling support and ability to extend the markup language.
  • Can use any deployment method. UWP applications must always be packaged in an MSIX package and installed through sideloading or the Microsoft Store.

The Disadvantages of WPF compared to UWP

  • It is less performant. WPF is a part of .NET Framework and therefore runs managed code. UWP instead uses .NET native. UWP also has support for new types of bindings that are resolved at compile time instead of runtime, increasing performance.
  • Although it is still supported, no new features are expected for WPF in comparison to UWP that is currently in development.
  • Does not have support for modern looking UIs like UWP.
  • Cannot be easily deployed to Microsoft Store.

But the purpose of this article is to also show some of the issues we had when dealing with these technologies, so that it can serve as a more comprehensive analysis.

Our Case

We wanted the modern-looking UI, but several doubts arose about accessing OS resources, as we would most likely need extensive access to the file system and the global registry. Then, instead of going directly with UWP, we decided to try an alternative that would give us unrestricted access to those resource and a modern-looking UI.

XAML Islands

XAML Islands is basically a way to add UWP UI controls into desktop applications. Our intent was to do everything we could in UWP, but have the base application be a desktop application so that we would not have troubles when trying to access restricted resources. Everything went well at first, but soon we started finding several limitations. Here is a list of “limitations and workarounds” in the official XAML Islands documentation. Although most of times it is hard to explore deeper into all limitations just by looking such kind of lists.

For example, we thought it would be good to use UWP file pickers in some parts of the UI, but it turns out that XAML Islands do not support open dialogs so pickers will fail. There is a workaround explained here using the IInitializeWithWindow pattern, but this starts showing how many things become harder to implement.

As another example, we wanted to be able to open our application from the system tray and hide it when closed. This is easy to do in WPF, you just hide the Window and the corresponding taskbar icon when initializing the app. But when initializing an application using XAML Islands, a taskbar icon from a process that was not even part of our application appears, and it would not go away until we showed a window. We did a non-elegant work-around of creating an additional transparent window and then removing it just to make that icon go away. We also had to find workarounds for other features like theme changes.

Figuring out how to…

The main problem with XAML Islands is not only that it has so many limitations, but more importantly, that there is very little documentation other than the official one. This caused us to have to spend hours or days for every one of these workarounds we had to do.

Furthermore, we had seen in this official documentation that some Windows APIs can be called from a desktop application. This would be helpful as some APIs from the Windows Runtime API were more appropriate for our use than the .NET ones, but when we tried to use them, most of the Windows APIs require a package identity, and this package identity would place the restrictions like making it a full UWP app, partially defeating the purpose of not using UWP.

This may change in the future, as it seems that starting from Windows 10 May 2020 Update (version 2004), desktop applications are allowed to have a package identity without having to be placed in a sandbox (More information).

If you decide to go with XAML Islands, other than the official documentation, this post from the Windows Developer blog can prove very useful.

At this point, we thought it may be better to try just going full UWP instead.

Full UWP desktop apps using the FullTrustProcessLauncher API

One of the first things we noticed when starting our prototype in UWP is that the systray icon does not exist in UWP. For this, and other requirements that would potentially need a desktop application, we looked for an alternative, and found the FullTrustProcessLauncher API. This API basically allows a desktop application to be launched from another UWP application, and vice-versa.

To be able to use the FullTrustProcessLauncher API, the runFullTrust restricted capability must be added to the manifest.

In our case, our desktop application only contained the taskbar component with a menu to open the UWP one.

Desktop applications can be uploaded to the Microsoft Store, but keep in mind that doing so will require a more complicated (and onerous) process than with UWP apps because it contains the runFullTrust restricted capability. (More information)

One of the biggest differences between UWP and desktop apps, the Application Lifecycle

Since UWP aims to create applications for both desktop and mobile devices, its lifecycle process must take into consideration the battery restrictions on mobile devices. As such, UWP applications normally cannot run indefinitely hidden in the background as desktop apps can. We say normally because Microsoft added in its Windows version 1703, a set of APIs to allow extended or indefinite execution in the background, but that has other restrictions.

In UWP, if you close an application, it directly goes to the “Not Running” state, which means the process is killed, and you lose any non-saved data. If you do not close it, but instead minimize it, or change focus to another application, then after a few seconds the application will be suspended. When an application is suspended, there is the possibility that the system may terminate its process if it needs the resources, or if it is drawing too much battery. Therefore, it is needed to save the application state before the application goes into the suspended state. UWP apps can have a handler function set to its Suspended event, where they can save the state. This execution is limited to 5 seconds (10 for mobile devices), so Microsoft recommends partially saving application data while it is still running so that smaller operations are needed on the suspended event. If the handler of the Suspended event does not finish within the time limit, the application will be terminated.

App lifecycle flow: First, not running, then it’s activated and start a cycle running background, foreground and/or suspended
The Application Lifecycle

Executing code from the background in UWP

The UWP way to do background processing is using Background Tasks. These tasks are classes that are registered to execute code when certain conditions are met, even if the application is not currently running at that time. Once a task is registered, it will last until unregistered, until the application is uninstalled or reinstalled, but it is recommended to re-register them on application launch.

There are several different triggers that allow for many conditions for starting execution, but there is another limitation, which is resources. Background tasks can run up to 10 minutes if the app is running in the foreground, or 30 seconds otherwise. This can be a limitation for many desktop applications that need to do intensive background processing. The execution time can be extended indefinitely by setting the extendedExecutionUnconstrained capability in the manifest.

Be aware that adding the extendedExecutionUnconstrained restricted capability will prevent the application from being deployable to the Microsoft Store.

Another issue to watch out for is that background tasks may be limited by the system in their memory usage, as they are expected to be lightweight. In the official documentation, there are guidelines for creating background tasks, but overall, they seem to be aimed at doing minor tasks.

Windows Control

Another difference between UWP and WPF or XAML Islands is the way windows are controlled. Controlling windows in UWP is more restricted.

Package limitations

One last issue we will mention in this post is the limitations that are inherent in requiring an MSIX package. Another feature we wanted to have was to add a shell extension to, for example, provide additional functionality to the file explorer context menu, and add overlay icons. In a desktop application, we could do this by using Shell extension handlers. The problem is that to register these extensions, we needed to modify the global registry, and this is not allowed in packaged applications.

After some investigations, it seems that it is possible to do things like adding shell extensions, but the way to do it is using the package manifest schema. The problem with using the package manifest schema is that the official documentation is quite scarce, and documentation other than the official one is almost non-existent. So, it is a thing to have in consideration, that features which need the global registry to be modified (for example, if another application needs to access these modifications) are possible but doing it can take a long time.

The “Best” option

So, what is the best option for creating a desktop application with a modern UI in Windows? For now, it depends on the needs of the application, and choosing one option can make big compromises on leaving the other one, as shown before, but that may change soon with Project Reunion, especially with WinUI 3 and the latest changes in .NET 5.0.

What is WinUI?

Windows UI Library (WinUI) is a native user experience (UX) framework. This framework was created to allow developers to use the latest UI controls without having to update Windows like with UWP.

WinUI 3

As stated in this blog post, Microsoft noticed that many developers wanted to have mostly UWP controls in a desktop application, just like us. This is reasonable, because it is usually desirable that the looks of all the views of the application are consistent, which does not happen if you make some controls with UWP and some with a desktop framework like WPF.

As such, Microsoft decided that for its next iteration of WinUI, that is, WinUI 3.0, they would decouple all the UI elements from UWP and make them also available for Desktop applications by installing a NuGet package.

Architecture diagram showing WinUI 2 on top of UWP. Win32 apps can consume it using Xaml Islands
Current State of Ecosystem

Architecture diagram showing WinUI 3on top of UWP and Win32 apps without needing XAML Islands
Planned State after WinUI 3 release

Overall, this means that there will be no need for XAML islands to be able to use modern UI controls in a desktop application. Since WinUI will be compatible with .NET Framework 5, you can just create a desktop application with .NET Framework 5 and then just include the WinUI NuGet package. Furthermore, as we have said, many of UWP Windows APIs can still be used in desktop apps. Additionally, you also have the CsWinRT project which also enables you to use to use WinRT APIs adding the projections support for C#.

So, after WinUI 3 release, the choice will be between if your application needs the .NET features, in which case you go with a desktop app with .NET Framework 5 and WinUI 3, or if your application can do with the UWP Windows APIs, in which case you can go UWP with WinUI 3. This means the unification of the UI for desktop and non-desktop applications, at least in the Windows ecosystem. Here is the Windows UI Library Roadmap, that can be checked for news about its releases.

.NET 5.0 is the first release in the .NET unification journey, merging both .NET Framework 4 and .NET Core in a single platform.

A high level architecture diagram showing that .NET 5 us a unified platform targeting all the platforms and runs everywhere
.NET unified platform

We also have this table taken from the official WinUI GitHub site.

Table showing the feature comparison between WinUI 3, UWP and WPF where WinUI 3 shows to support everything
Feature Comparison between WinUI 3, UWP and WPF

As can be seen from this image, one of the objectives of WinUI 3 is to support all the features of both UWP and WPF.

Conclusion

If you aim to deploy your application in the Microsoft Store, or your application is more oriented toward mobile devices, then your best bet may be going with UWP, that is receiving updates regularly and has current support for modern UI controls.

If your application needs features that are more suited for desktop devices, like heavy background processing, lots of process intercommunication, global registry modification, and not having to worry about the UWP application’s lifecycle, then it may be better to go with WPF, that ensures you will not have any restriction when implementing the mentioned features. Additionally, when WinUI 3 gets to a production-ready state, it will be easy to adapt the code to it and have the same modern UI look anyway.

Furthermore, if you need support for older Windows versions, like Windows 7, then WPF is your only option between these two.

Finally, we aimed to summarize everything in a very simple way we recommend it according to your scenario needs.

Table showing a visual summary of the conclusions
A high-level summary of the features and their support in each platform (green is supported, yellow is supported with constrains or not clear yet and red is not supported)

Thanks to Mauro Krikorian

Originally published by Nicolás Bello Camilletti for SOUTHWORKS on Medium 10 December 2020