Move your first steps with .NET Core 3.0 for desktop development

Warning! This post is dedicated to highly experimental technologies 😃

If you're into development in the Microsoft ecosystem, I'm sure you're familiar with .NET Core. It's a new framework, built from scratch, to bring all the goodies of the .NET Framework into the new modern world. Unlike the full .NET Framework, which has its roots deeply integrated into Windows, .NET Core is cross-platform, lightweight and easily extensible.

Until today, .NET Core has always been focused on supporting these new requirements. As such, its primary workload has always been web or back-end applications. Thanks to .NET Core, you can build web applications that can be hosted also on Linux servers; or that can be deployed inside a Docker container and easily scaled in complex micro services architectures.

At BUILD 2018 we have announced the next major release of .NET Core, 3.0, which is, without any doubts, the biggest and most ambitious release since the first version. On top of .NET Core 3.0, in fact, we'll be able to build new workloads:

As you can see from the image, for the first time .NET Core will support not just web and back-end applications, but also desktop ones which, until today, have always been part only of the full traditional .NET Framework.

Disclaimer! This doesn't mean that WPF and Windows Forms will become cross-platform and you'll be able to run a Windows desktop application, as it is, also on Linux and MacOS. The UI piece of the two frameworks still has a dependency on the Windows rendering system and, as such, it can't run on platforms which use instead a different visual rendering system.

Which are the main benefits of running a desktop application on top of .NET Core? Essentially performance and side-by-side support.

Performance improvements.

One of the key investments in .NET Core is around performances. Startup time is much faster and most of the APIs have been rewritten to be fully optimized. If you take a look at the popular TechEmpower website you will notice how, for most of the workloads, ASP.NET Core is much faster than many other popular web development frameworks like NodeJS.

Side-by-side support

One of the biggest blockers for enterprises to adopt newer versions of the .NET Framework is that it can be installed only at system level and it automatically comes with newer version of Windows. This means that if you have an application which targets .NET Framework 4.5 and you want to update it to take advantage of some of the improvements delivered by .NET Framework 4.7, you are forced to update all the applications (or, at least, to make sure they still run well) at the same time. The reason is that you can't install the .NET Framework 4.7 side-by-side with .NET Framework 4.5, but you have to update the existing 4.5 installation. This isn't a nightmare only for enterprises, but a big blocker also for Microsoft. If you look at the recent history of .NET Framework, you will notice how every upgrade brings mainly fixes and minor improvements. The reason is that, since we need to make sure to maintain backward compatibility, the team can't be agile and evolve the framework with changes that, potentially, can break older apps. Checking new code into the .NET Framework requires a long validation and testing period. You can read some thoughts from the team on this in the following article.

.NET Core, instead, can run side-by-side, with two different approaches:

  • You can embed the runtime inside the application. This way you'll be able to deploy the app on any machine, even without the runtime installed, and make sure it will target the specific .NET Core version you have used to build it.
  • You can install multiple .NET Core runtimes on the same machine. Unlike with the .NET Framework, you can have on the same machine .NET Core 1.0, .NET Core 2.0, .NET Core 3.0 and whatever .NET Core version will ship in the future. This means that if you deploy an application which runs on .NET Core 2.0, it will effectively leverage the .NET Core 2.0 runtime and not another runtime in backward compatibility mode.

Additionally, you will be able to leverage many of the benefits of the .NET Core ecosystem, like the opportunity to use the command line tools to create and build your projects or to use the improved .csproj format. In the end, .NET Core 3.0 will bring some specific benefits for desktop development, like a better support to high DPI screens or the opportunity to leverage all the UWP APIs.

Let's build our first project

.NET Core 3.0 is still at an experimental stage right now. This means that you won't find a public release, neither beta or stable, but you will have to download one of the daily builds.

You can get the latest one on the GitHub repository, specifically here. This page lists all the latest bits, including the SDK for all the supported platforms. In my case, since I have a Windows 10 PC, I downloaded the Windows x64 installer.

Once you have downloaded and installed the SDK, the easiest way to create a new project is to use the CLI. Visual Studio, in fact, doesn't support creating Windows Forms or WPF projects based on .NET Core yet.
First open File Explorer and create a folder to host your project. Then open a command prompt of your choice, move to the folder you have just created and then execute the following command:

dotnet new wpf

if you want to create a WPF application or:

dotnet new winforms

if you want to create a Windows Forms application. The CLI will create, inside the folder, an empty application using the standard template plus the project file.
For example, this is how the folder looks like when you create a WPF project:

If you want to try the application, you just need to execute the following command:

dotnet run

The project will be compiled and the template application will start:

If you want to start changing the code, you can open the .csproj file with Visual Studio. I strongly suggest you to install Visual Studio Preview. The coding experience is still far from perfect, but the preview version of Visual Studio 15.9 is able to handle .NET Core 3 projects much better than the stable 15.8 release.

The project will look like any other .NET Core project: you will have a Dependencies section, followed by the files of the application. If you expand the dependencies, you will notice how the WPF / Windows Forms framework is nothing more than a NuGet library called Microsoft.DesktopUI.App.

This configuration should help you to understand why WPF or Windows Forms application won't run on other operating systems, unlike ASP.NET Core. The core of the project is indeed a .NET Core application, but the Microsoft.DesktopUI.App library contains specific Windows features.

Right now Visual Studio doesn't fully support yet Windows applications running on top of .NET Core and many features are still missing. For example, you don't have a visual editor for the XAML files. Additionally, the debugging experience is a bit unstable. Sometimes you will see weird errors, sometimes the debugger will crash. For example, if you launch the template application and you press the Exit button, the debugger will hang for a while and then, after a few seconds, it will report the following error:

However, most of the times you should be able to compile and at least launch your application without facing too many issues.

Checking the compatibility of an existing app

As an experiment, I've decided to try to migrate to .NET Core a WPF application I often use for my demos called ExpenseIt. It's part of the official WPF samples collection from Microsoft on GitHub and it's a sample LOB application.

You can find the official WPF repository at Once you have downloaded / cloned the repository, you can find the application under

The first step is to check if the application can be easily ported to .NET Core. For this task we can use a precious tool called .NET Portability Analyzer, which is available as a Visual Studio extension or as a command line tool.
In this post we're going to use the Visual Studio extension, so make sure to install it from the Marketplace.

Then open the ExpenseItDemo.csproj project under the Sample Applications/ExpenseIt/ExpenseItDemo folder of the repository you have previously downloaded / cloned. First we need to add another project to the solution, which contains a custom component leveraged by the main application. Right click on the solution, choose Add -> Existing project and look for the EditBoxControlLibrary.csproj file under the Sample Applications/ExpenseIt/EditBoxControlLibrary folder.

Just to make sure that everything is working as expected, try to compile and run the application. You should see the main window posted in the previous image.

Now we are ready to verify if this application can be indeed ported. Right click on the ExpenseItDemo project and choose Portability Analyzer Settings. Thanks to this window, you'll be able to choose which frameworks of the .NET family you want to target:

In our case we want our application to target .NET Core 3, so make sure to check only 3.0 under the .NET Core section. In my case, I've chosen also to enable .NET Standard, to check eventually how much code I would be able to move to a .NET Standard library.
Press OK, then right click again on the project and choose Analyze Project Portability (with project reference). This way the tool will analyze not only the ExpenseItDemo project, but also the EditBoxControlLibrary one, since it's referenced.

At the end of the process a window will popup with the list of all the reports that you have generated since you have started to use the tool. Open the most recent one and let's take a look at the outcome. Let's start from the first part:

Here we have a summary, which immediately tells us how much code we can reuse. As you can see, our application is perfectly compliant for .NET Core 3.0. The coverage of supported APIs is 100%, both for the main application and the library. This means that we can start our migration without fear!

We can also see that, if we would like to move some code to a .NET Standard library, we can do it but only partially. We would be able to move 33,33 % of the code from the EditControlLibrary project and 53,49 % from the main application.
If we scroll down the report, we can quickly see which are the APIs that aren't supported by .NET Standard:

As you can see, we're basically talking about all the APIs which are deeply rooted into the Windows ecosystem, like visual controls (Button, ComboBox, etc.) or event handlers. .NET Standard libraries, in fact, are meant to work with all the various frameworks of the .NET ecosystem, including cross-platform (Xamarin) or web (ASP.NET Core) and, as such, they can't support platform specific APIs.

However, for our purpose it isn't a blocker. What matter to us is that we get 100% compatibility with .NET Core 3.0, so let's move on!

Migrating the application

The plan of the team is to provide, once .NET Core will ship, some tools to easily migrate an existing Windows Forms or WPF project to .NET Core. However, right now we have to do it in the manual way.
Let's start from the template project we have created before. As first step, let's change the assembly name and the default namespace, in order to match the same of the original application. This way, we won't have to change any namespace in XAML or in code, but we can reuse the existing files as they are.

Right click on the original ExpenseItDemo project and let's look at its configuration:

The easiest way to achieve the same settings is to create a new WPF project inside a folder which name is ExpenseItDemo. This way, when you create the base app with the command dotnet new wpf, it will automatically get the right assembly name and default namespace. Otherwise, if you have created the project in a folder with a different name, just right click on it, choose Properties and make sure to configure the two fields with the value ExpenseItDemo. The main difference from the starting project is that, as Target framework, you will see .NET Core 3.0:

Now, in Solution Explorer, delete the existing App.xaml and MainWindow.xaml files, including the corresponding code-behind files.
Let's start to copy over the content of the EditBoxControlLibrary. For the sake of simplicity, while migrating to .NET Core we will merge everything inside the same project, instead of keeping two different projects. Copy the following content from the original project:

  • The Themes folder
  • The EditBox.cs file
  • The EditBoxAdorner.cs file

Now, in Solution Explorer, expand the Themes folder, right click on the Generic.xaml file and:

  • Set Page as Build action
  • Remove the keyword MSBuild:Compile from the Custom tool field

This is how the configuration should look like:

If you try to compile the project now, you'll get an error since we have deleted the App.xaml file and, as such, the application doesn't have an entry point anymore.
Let's move to the folder which contains the original ExpenseItDemo project and, this time, copy the following files over the new project:

  • The Validation folder
  • The App.cs / App.xaml files
  • The CreateExpenseReportDialogBox.xaml / CreateExpenseReportDialogBox.cs files
  • The ExpenseReport.cs file
  • The LineItem.cs file
  • The LineItemCollection.cs file
  • The MainWindow.xaml / MainWindow.cs files
  • The ViewChartWindow.xaml / ViewChartWindow.cs files
  • The Watermark.png image

The next step is to right click on each XAML file we have just added and to repeat the same steps we have done before in the Properties panel: set Page as Build action and remove the keyword MSBuild:Compile from the Custom tool field.
The only exception is the App.xaml file, which Build action must be set to Application definition, since it's the entry point of our application.

Lastly, you need to change the properties also of the image we have imported, the Watermark.png file. In this case, you need to set the Build action to Content and the Copy to Output Directory field to Copy if newer.

Now rebuild the project and launch the debugging. The application will normally start. We have fully migrated our solution to .NET Core 3.0!

Some issues, here and there

As anticipated at the beginning of the post, .NET Core 3.0 is still in an early stage and, as such, you can experience some quirks. Here are the two biggest ones I've found so far during the migration of the project.

The first one is about the debugging experience, which isn't perfect yet. Do you remember the error that appeared when we tried to close the base template app? You will get a similar behavior in the ExpenseIt app when you choose a name from the list and you press the Create expense report button. However, it isn't an application problem. If you launch the application without attaching the debugger (by pressing CTRL+F5 in Visual Studio) and you repeat the same steps, the Create expense report window will appear without issues.

The second one is about the CLI, which still doesn't support all the APIs exposed by WPF and Windows Forms. If you try to build or run this project from the command line (using dotnet build or dotnet run) you will get the following error:

PS C:\Users\mpagani\Desktop\WPF> dotnet run
C:\Users\mpagani\Desktop\WPF\App.xaml(22,46): error MC3087: Cannot set content property 'XmlSerializer' on element 'XmlDataProvider'. 'XmlSerializer' has incorrect access level or its assembly does not allow access. Line 22 Position 46. [C:\Users\mpagani\Desktop\WPF\WPF.csproj]

The build failed. Please fix the build errors and run again.

However, also in this case, it isn't an application problem. If you compile the project through the Visual Studio / MSBuild toolchain, as you have seen, the application builds and starts just fine. Thanks to Oren Novotny for pointing this!

Deploying the application

The best way to try the side-by-side support is to publish the application as self-contained. This means that we'll be able to distribute it on any machine, even if it doesn't have the .NET Core 3.0 runtime installed.

To start the process right click on the project in Visual Studio and choose Publish. Press Start and then open the dropdown at the bottom and choose Create profile instead of Publish immediately.

This way, in the next step, you'll be able to customize the profile by clicking on Configure. Here you'll be able to set the Deployment mode to Self-contained, which means that the output folder will contain also the .NET Core 3.0 runtime.

Save and press Publish. Visual Studio will build the project and, if you have left the default folder, at the end of the process you will find a folder called publish under bin\Debug inside the project's folder.
Now copy it over a clean Windows machine (like a VM), which doesn't have the .NET Core 3.0 SDK or runtime installed, and launch it. You will notice how the application will start without problems. Cool, isn't it?

Wrapping up

In this blog post we have moved our first baby steps into the .NET Core 3.0 world for Windows desktop applications. The purpose of this post is mainly to have fun testing a new technology. As you have seen, there are still issues and the debugging experience is far from perfect. .NET Core 3.0 hasn't officially reached yet the preview phase, which means that it's totally not meant for production purposes.
However, if you are a Windows developer, it's a good idea to start familiarizing with .NET Core and, with the help of Portability Analyzer, start to understand how much code from your existing applications can be easily ported to this new platform.

If you want to play with the sample project I've built for this article, you can get it from GitHub.

Happy coding!

Comments (24)
  1. Great news. Looking forward in converting some of my Winforms apps from .net framework.

  2. pnp0a03_ says:

    Interesting. If you use the VS to create the deployment package, what package type will be used? MSIX or MSI?

    1. When you publish the application it doesn’t create any package, but just an executable that you can share. It’s up to you, then, to choose the deployment technology. For example, you can add a Windows Application Packaging Project to the solution to package the .NET Core 3.0 application as MSIX.

      1. AndreaDev says:

        What about the Linux and MacOs deploy? How could we distribute a self-contained application for other OS that are not Windows?

        1. Hello Andrea,
          WPF and Windows Forms on .NET Core 3.0 are still tight to Windows, because they have many dependencies (especially around the UI framework) which aren’t available on other platforms.
          If, instead, you’re leveraging .NET Core 3.0 for web applications or backends, Linux and MacOS are perfectly supported.


  3. yushulx says:

    Does the DesktopUI also work on macOS and Linux?

    1. Hello, the answer is no. It’s detailed in the introduction of the post:

      Disclaimer! This doesn’t mean that WPF and Windows Forms will become cross-platform and you’ll be able to run a Windows desktop application, as it is, also on Linux and MacOS. The UI piece of the two frameworks still has a dependency on the Windows rendering system and, as such, it can’t run on platforms which use instead a different visual rendering system.

      1. yushulx says:

        Thanks for your answer. I have another question. Is it possible to create cross-platform UI with .NET Core in the future?

        1. Right now there isn’t any specific plan. The preferred choice if you’re interested in building cross-platform UI in the Microsoft ecosystem is Xamarin Forms, which supports both UWP and WPF other than mobile platforms like Android and iOS.

          1. DanMeier says:

            Perhaps .NET Core 4.0 can be the “Unified UI” release! I’d love to see something like what Uno is working toward become the “official” Microsoft solution: XAML everywhere, including the web.

  4. I’ve tried porting our (not small) custom WPF control library to .NET Core 3 preview. Apart from the CLI not really working and VS Preview also only working when publishing and running the exe manually, I had most trouble with resource lookup. Somehow generic.xaml didn’t really work, nor did finding a ResourceDictionary with Application.LoadComponent. I wouldn’t be surprised if we did weird things here, but perhaps there’s a known (or unknown) issue here already.

    1. Hello,
      from my experiments, the XAML support is the part which breaks more often at the moment. One thing you can check is if you have set the right Build Action to the generic.xaml file.

      1. I used “ in the project file (I mostly edited it by hand because at the time I was still trying to get it to work with the dotnet CLI). The original project file also included MSBuild:Compile in there somewhere and other things, but I doubt that’s necessary (and the original project file has been through some version upgrades through the years, so I wouldn’t call it clean by any measure).

        But if XAML troubles are currently still expected/known I’ll probably wait for another preview later to test again. On the plus side, I got the whole thing to compile in about an hour of work (there were only four easily solvable API usage problems), it just wouldn’t run with a bunch of weird error messages (the most puzzling of which was that PresentationFramework v4.0.0.0 couldn’t be loaded), after the initial ones mostly related to resource loading.

        I just wanted to avoid finding issues in 9 months or so when we prepare a new release, only to notice that there are still showstoppers that prevent us from also targeting .NET Core 🙂

        1. The part between the “ is supposed to read {Page Include=”**/*.xaml”/}, just in XML …

          1. I think right now experimenting with .NET Core 3.0 can be helpful to understand, when it ships, how easy (or hard) could it be to move your existing WPF and Windows Forms application. However, I wouldn’t lost my sleep trying to make things working. 99% of the issues you’ll find (unless you’re clearly using an API which won’t be supported) happen because we’re using a daily build based product 🙂

  5. J.C.Kodel says:

    Useless. I would rather prefer a XAML that works in browser environments with Blazor (for doing the same Vue does).
    That way we could use Cordova, Capacitor or Electron.
    Better yet if we had a true cross-platform platform (such as Capacitor) that would allow us to run .net in every conceivable platform.

    1. Hello and thanks for your feedback!
      Well, useless is a strong word. If your goal is to go fully cross-platform, I can totally understand your point of view and .NET Core 3.0 doesn’t change your requirements. However, there are many scenarios out there (especially in the enterprise field) where a native application is required and the traditional .NET Framework approach slows downs the capacity of being agile. From this point of view, .NET Core can really be a game changer thanks to the side-by-side support and the big performance improvements.

      1. emiliano84 says:

        Totally agree 🙂

      2. Chamelespoon says:

        Quite. We’re in exactly that situation here: need a native Windows app (not UWP) and would like to be able to use all the convenience and performance improvements .NET Core provides.
        Personally I’ve been eagerly awaiting this release… well, the final one, anyway!

  6. SithRaven says:

    Just curious on how complete .NET Core 3 is right now, looks like it’s still missing a good chunk of WPF components when I run the portability analysis.

    1. Hello,
      .NET Core 3.0 is still in development, so things can always change. The Portability Analyzer can help to have an early understanding of what will be supported, but until things will be finalized there still might be changes.

  7. Opus Krokus says:

    So two things… First, why am I required to use my personal account to log in and post comments. This is work related, and I would prefer to get emails on my work account. Yet your site specifically refuses to let me use it and forces me to use my personal account. This blows my mind. Second, please tell me you are not planning to stealth assassinate Visual Studio the way you are the rest of the .Net infrastructure outside of Core (re 4.8 vs Core 3.0). I see things like in the article above where VS does not currently support the tech being talked about and I start getting nervous.

    1. Hello Opus,
      regarding the login issue I’m sorry for the problem you’re facing, but I don’t have control over this. This blog is hosted on the MSDN platform and we don’t have control over how it works and how it’s deployed.
      Regarding the second question, considering that we have just announced Visual Studio 2019 and that it’s the main product we have in the developer space, I find it very unlikely that we’re going to kill it anytime soon. Right now .NET Core 3.0 development doesn’t play well with Visual Studio simply because the technology is in very early preview, it isn’t even an official public preview. As such, Visual Studio doesn’t include yet the proper tooling support because things are still in development stage. It will be fully supported once .NET Core 3.0 will ship, as it is now for all the stable versions of .NET Core.

Comments are closed.

Skip to main content