SOUTHWORKS Dev Team
December 10, 2020
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.
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.
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.
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.
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:
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.
Benefits of WPF compared to UWP
The Disadvantages of WPF compared to UWP
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.
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 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.
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.
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)
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.
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.
Another difference between UWP and WPF or XAML Islands is the way windows are controlled. Controlling windows in UWP is more restricted.
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.
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.
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.
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.
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.
We also have this table taken from the official WinUI GitHub site.
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.
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.
Thanks to Mauro Krikorian
Originally published by Nicolás Bello Camilletti for SOUTHWORKS on Medium 10 December 2020