TLDR: You can now build Xamarin.Forms WPF applications on .NET Core, you can find a sample here.
Xamarin.Forms supports a wide variety of platforms, so called platform backends. Some of these are iOS, Android, macOS, UWP, GTK or WPF. While the WPF implementation was running on the old .NET Framework WPF version, in april support for .NET Core was merged. This post will show you how to add the WPF backend to your existing Xamarin.Forms project and what opportunities this brings. I will use Visual Studio as reference but you can do all of the steps and run the application using the dotnet CLI and any editor you want!
Disclaimer: If you want to target Windows with your application, you should generally use the UWP backend since its the stable and an officially supported backend. If you are sure you want to use WPF, go ahead!
Prerequisites
- Visual Studio 2019 (Windows) 16.5 with the Xamarin, .NET Desktop and .NET Core workloads installed
- The latest .NET Core SDK
- A Xamarin.Forms project you want to add the WPF backend to, I will use my FormsDesktop project from my .NET desktop framework sample collection.
My sample project before the WPF backend is added looks like this:
Adding the WPF .NET Core project
Start of by adding a new WPF App (.NET Core) to your solution.
If you want to use the CLI you can use the command dotnet new wpf -n PROJECTNAME.WPF
We now have to modify the project:
- Add a reference to the shared project in your solution, in my sample that would be the "FormsDesktop" project. The CLI command from inside the WPF project folder would be
dotnet add reference ../FormsDesktop
- Add the latest
Xamarin.Forms.Platform.WPF
NuGet package to the WPF project and make sure it has the same version as the Xamarin.Forms references in your other projects. You can use the NuGet manager in Visual Studio or the commanddotnet add package Xamarin.Forms.Platform.WPF
- Open the .csproj file of the WPF project and add the following lines:
<ItemGroup>
<EmbeddedResource Remove="**\*.xaml" />
</ItemGroup>
This is a workaround for a bug when working with xaml files which would lead to unresolvable build errors.
After these steps, your .csproj file should look like this:
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Remove="**\*.xaml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Forms.Platform.WPF" Version="4.6.0.772" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FormsDesktop\FormsDesktop.csproj" />
</ItemGroup>
</Project>
Initializing the Xamarin.Forms application
After adding the project all we have to do is to initialize the Xamarin.Forms application when starting the WPF application.
The MainWindow
is by default the window that is initially loaded when the application is started. Start with the .xaml
file and add a reference to the Xamarin.Forms.WPFnamespacexmlns:wpf=clr-namespace:Xamarin.Forms.Platform.WPF;assembly=Xamarin.Forms.Platform.WPF
. Then change the class from Window
to wpf:FormsApplicationPage
<wpf:FormsApplicationPage x:Class="Xamarin.Forms.WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Xamarin.Forms.WPF"
xmlns:wpf="clr-namespace:Xamarin.Forms.Platform.WPF;assembly=Xamarin.Forms.Platform.WPF"
mc:Ignorable="d"
Title="WPF .NET Core" Height="450" Width="800">
</wpf:FormsApplicationPage>
Continue with the MainWindow.xaml.cs
file where the Xamarin.Forms framework is initialized and the application is loaded as content of the WPF application. To do this, add the Xamarin.Forms initialization Forms.Init()
and the application load LoadApplication(new SharedProject.App())
.
namespace Xamarin.Forms.WPF
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
Forms.Init();
LoadApplication(new FormsDesktop.App());
}
}
}
Run it!
You can now run the WPF application directly from Visual Studio or the CLI with the dotnet run
command.
Publishing the application
Once the application runs we just have to publish it to ship it to some user. Like any .NET Core application, you can just publish it from Visual Studio (right click the project - publish) or use the CLI with dotnet publish -c Release
.
Now the great benefits of .NET Core come into play. Old WPF projects that build on the .NET framework always required the correct .NET runtime on the users device. With .NET Core, you can publish the application self contained which will ship the runtime with the application! Its just a drop down in Visual Studio (Deployment Mode) or a switch in the publishing command -r win10-x86
. There are more runtime identifers available for specific windows versions, you can find them here.
Next up, instead of shipping a huge folder with hundreds of files, we can create a single executable by selecting Produce single file in Visual Studio or the -p:PublishSingleFile=true
switch.
Last but not least, .NET Core is now capable of tree shaking to reduce the size of the published application with the Trim unused assemblies setting or the -p:PublishTrimmed=true
switch. This will reduce an empty application from 140 to around 90mb. Just be aware that this feature is still in preview and like linking Android and iOS applications, you have to make sure that no assembly is removed that is only used by reflection.
That's a wrap
This article showed how to add a WPF backend to your Xamarin.Forms project that is targeting .NET Core and some of the benefits this brings, like working and running from the dotnet CLI, tree shaking, self contained or single executable deployments. If you have any further questions dont hesitate to reach out to me on Twitter.
Top comments (1)
Thanks for the Xamarin.Forms and WPF article. Its great.
I've found it works well apart from the PanGestureRecognizer isn't passed through even though it looks like it should be based on this discussion. Tap works fine. (forums.xamarin.com/discussion/1398...)
Do you know if this a known issue? Do you know of any work arounds?
The aim here is to have Xamarin.Forms running on iOS, Android and WPF and to have a pinch/pan/tap working to interact with images and skcanvas drawings. I understand that for WPF we presumably will need zoom buttons to replace the pinch functionality, but was hoping Pan would work. Any info or suggestions would be greatly appreciated!