Every now and then you’re presented with a scenario that should in theory work as advertised, but you’re not convinced until you actually see it with your own eyes. I recently found myself in this situation when migrating some code from the full .NET Framework running on a traditional Windows Server cluster to .NET Core running as a microservice on Kubernetes.
You can download the code for this post from my hello-netcore-wpf-nano GitHub repo. The Dockerfile for the Windows Desktop Nano Server container image on Docker Hub can be found in my dotnet-runtime-windowsdesktop GitHub repo.
The code in question is non-visual but has a dependency on WPF (Windows Presentation Foundation), in particular the Geometry classes in the
System.Windows.Media namespace. Because .NET Core version 3 and later includes WPF, and Nano Server (a compact version of the Windows operating system designed to run in containers) supports .NET Core (but not the full .NET Framework), it should be possible to run a non-visual WPF application on Nano Server in a Docker container — and to deploy it to Kubernetes using Amazon EKS, which has support for Windows containers.
.NET Core WPF Console App
The first step is to create a WPF console app using .NET Core, which is a rather unnatural act, since WPF is normally used to create a visual UI. You need to start out with a WPF .NET Core Library, using either Visual Studio or the .NET Core CLI.
dotnet new wpflib -n MyWpfConsoleApp
Running this command on Windows — don’t even think of doing it on a Mac — will produce a class library project based on the Windows Desktop SDK. To convert it to a .NET Core console app all you need to do is edit the csproj file and set the project output type to Exe.
Then add a Progam.cs file with a static
Main method in which you use some Geometry classes.
dotnet run will produce the following output.
Point X: 1, Y: 2, Z: 3
Dockerized WPF Console App
To use Docker you must first install Docker Desktop for Windows.
To Dockerize your WPF console app, start by adding a Dockerfile to the project. If you are using Visual Studio, ordinarily you would right-click the project in the Solution Explorer and select Add Docker Support. But this won’t work with a WPF app.
A more feasible approach is to open the project folder in Visual Studio Code, where you have installed the Docker extension. Press Ctrl+P to open the command pallet, type Docker and select Add Docker files to Workspace.
Select .NET Core Console for Windows and you’ll get a Dockerfile that looks like this.
Build and run the container.
docker build -t wpf-console-app . docker run --rm --name wpf-console-app wpf-console-app
At this point you’ll get an error complaining that the Microsoft.WindowsDesktop.App framework could not be found.
It was not possible to find any compatible framework version The framework 'Microsoft.WindowsDesktop.App', version '3.1.0' was not found. You can resolve the problem by installing the specified framework and/or SDK.
This is because the Windows Desktop framework is not installed on the Nano Server base image. For that you’ll need to create a custom base image that installs the Windows Desktop framework on Nano Server (or simply use my Windows Desktop Nano Server container image). Here is the WinDesktop.Dockerfile I created. It downloads and runs the installer for the Windows Desktop runtime, then copies the dotnet folder from the installer image to the Nano Server runtime image.
Note: If you create your own custom container image using this Dockerfile, you will need to push it to a container registry of your choice, such as Docker, Azure, Amazon or Google.
To get your local Dockerfile to work, replace the FROM statement at the top with one that specifies the custom Docker image you created (or you can reference my base image).
FROM tonysneed/dotnet-runtime-windowsdesktop:3.1-nanoserver-1909 AS base
After re-running the
docker build and
docker run commands shown above, you should see output from your WPF console app.
Congratulations. You now have a Nano Server container running a .NET Core app that references WPF.