Zero Installer, Zero MSIX, Zero Packaging with .NET Single File Apps

A feature that was added to .NET Core apps was the ability to publish as a single file. As we approach the release of .NET 5 I thought it worthwhile taking a look at the options for publishing Windows Forms and WPF applications with dotnet publish. For this post we’re going to work with a … Continue reading “Zero Installer, Zero MSIX, Zero Packaging with .NET Single File Apps”

A feature that was added to .NET Core apps was the ability to publish as a single file. As we approach the release of .NET 5 I thought it worthwhile taking a look at the options for publishing Windows Forms and WPF applications with dotnet publish.

For this post we’re going to work with a very basic Windows Forms and WPF application: WinFormsSingleFileSample and WpfSingleFileSample. To create these applications I used the Windows Forms (WinForms) Application and WPF Application project templates in Visual Studio 16.8 preview 2 (with .NET 5 RC1 installed). After creating the applications I ran them from within Visual Studio just to confirm there was no issue with the creation process – I always recommend doing this otherwise you end up chasing your tail if something is broken and you only realise after doing hours of work.

dotnet publish

We’ll start of by calling dotnet publish using the Release configuration (typically the Debug configuration will be used if the Configuration parameter isn’t specified):

dotnet publish -r win-x64 /p:Configuration=Release

Here’s the output for this command. Note that if this is the first time you’ve called dotnet build/publish for a specific architecture (using the -r win-x64 flag), it may take a few minutes to restore some of the dependencies.

C:\temp\WinFormsSingleFileSample>dotnet publish -r win-x64 /p:Configuration=Release
Microsoft (R) Build Engine version 16.8.0-preview-20451-02+51a1071f8 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.
Determining projects to restore…
Restored C:\temp\WinFormsSingleFileSample\WinFormsSingleFileSample\WinFormsSingleFileSample.csproj (in 86 ms).
You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
WinFormsSingleFileSample -> C:\temp\WinFormsSingleFileSample\WinFormsSingleFileSample\bin\Release\net5.0-windows\win-x64\WinFormsSingleFileSample.dll
WinFormsSingleFileSample -> C:\temp\WinFormsSingleFileSample\WinFormsSingleFileSample\bin\Release\net5.0-windows\win-x64\publish\

The final line includes the output folder for published application. If we look in this folder there are 293 items (280 files and a number of language folders). You can easily zip this folder and copy it to a different machine and run the application but it would be nice to have just a single output.

Self Contained

One option to reduce the number of files to be deployed is to provide the SelfContained parameter. The default value for the SelfContained parameter is true which means that you can copy the output folder to any machine without worrying about whether .NET has been installed.

dotnet publish -r win-x64 /p:Configuration=Release /p:SelfContained=false

Setting SelfContained to false results in a folder containing only 5 items.

Of course, you now need to manage the deployment of .NET dependencies (More information at https://docs.microsoft.com/en-us/dotnet/core/deploying/deploy-with-cli#self-contained-deployment)

PublishSingleFile

Let’s back up and remove the SelfContained parameter, since we’re really interested in distributing an app and not having to worry about any dependencies. This time we’re going to include the PublishSingleFile parameter

dotnet publish -r win-x64 /p:Configuration=Release /p:PublishSingleFile=true

The output on Windows isn’t exactly a single file. There are 11 files in the output folder – these are the native dependencies required in order to run the application.

For more information on why there are multiple files on Windows, check out the preview 8 blog post by Richard Lander from the dotnet team. This is also covered in the comments of this github issue.

IncludeAllContentForSelfExtract

There is still a mechanism where you can achieve a single file output, which is to include the IncludeAllContentForSelfExtract parameter.

dotnet publish -r win-x64 /p:Configuration=Release /p:PublishSingleFile=true /p:IncludeAllContentForSelfExtract=true

The output of this command is two files, one of which is the symbols file (i.e. the pdb file). To deploy this application you can just copy the .exe to the target machine.

One thing to note about the IncludeAllContentForSelfExtract parameter is that when you run the application for the first time, the native dependencies will be extracted which can result in a small delay in launching the app. The managed libraries will be loaded directly from the exe.

PublishTrimmed

Our single file application is currently ~146Mb, which seems a lot for an application that is literally an empty window. Let’s include the PublishedTrimmed file to see if we can optimise the output.

dotnet publish -r win-x64 /p:Configuration=Release /p:PublishSingleFile=true /p:IncludeAllContentForSelfExtract=true /p:PublishTrimmed=true

This command takes a little longer to run but now the output size is just ~83Mb which is a significant improvement. More information on application trimming

PublishReadyToRun

The last parameter we’re going to add is PublishReadyToRun. This essentially invokes AOT compilation, which in theory improves startup performance for the app. I say in theory because given the incredibly simple app we’re dealing with here, it’s unlikely we’ll see any actual performance improvement.

dotnet publish -r win-x64 /p:Configuration=Release /p:PublishSingleFile=true /p:IncludeAllContentForSelfExtract=true /p:PublishTrimmed=true /p:PublishReadyToRun=true  

Now our application has ballooned back up to ~138Mb.

Hopefully in this post you’ll have seen some of the options available to you when publishing Windows Forms (WinForms) and/or WPF applications.

Note: At the time of writing there is a bug with WPF applications where the PublishSingleFile parameter generates a binary that won’t launch in some cases. According to the dotnet team this is to be fixed with RC2 of .NET 5.

XAML Islands Getting Started Guide – Adding UWP Controls to Windows Forms or WPF Application

One of the reasons that Microsoft failed to get wide spread adoption of the Universal Windows Platform (UWP) is that there is already a massive investment into Windows Forms (WinForms) and Windows Presentation Foundation (WPF) applications. In this post we’re going to walk through how you can use XAML Islands to host UWP controls within an existing WinForms or WPF application.

One of the reasons that Microsoft failed to get wide spread adoption of the Universal Windows Platform (UWP) is that there is already a massive investment into Windows Forms (WinForms) and Windows Presentation Foundation (WPF) applications. What’s ironic is that this is true for both existing applications and new applications. Over the last couple of years Microsoft has changed strategy and has been looking at tools and techniques for bridging the gap between these frameworks in order to allow developers to take advantage of the rich controls and capabilities of UWP. In this post we’re going to walk through how you can use XAML Islands to host UWP controls within an existing WinForms or WPF application.

Before we get into working with XAML Islands, here are a couple of reference posts that are worth a read if you want to understand the background and some additional details about XMAL Islands:

Windows Forms

Let’s get into this – we’re going to start with Windows Forms and we’re going to be working with a Windows Forms application that’s sitting on .NET Core 3.1. As Miguel discusses in his post, there is support for .NET Framework but there are some limitations for third party controls. If your application is still based on .NET Framework, I would highly recommend looking at migrating to .NET Core.

In Visual Studio, we’ll create a new project using the Windows Forms (WinForms) Application project template. I’m currently using Visual Studio 2019 16.8 preview 2.1 where the project templates have been renamed – this template was formerly called Windows Forms App (.NET Core), which points to Microsoft’s intent to move developers to building Windows Forms app off .NET Core instead of .NET Framework (the .NET Framework based template is still called called Windows Forms App (.NET Framework)).

We’re going to select .NET Core 3.1 for the target framework

After creating the project we’ll rename Form1 to MainForm, and then proceed with adding four more forms that will host the four scenarios we’re going to look at.

Next I’ll create four buttons on the MainForm, which we’ll use to launch the four forms we just created.

The code behind for these buttons is relatively simple.

private void btnSimpleButton_Click(object sender, EventArgs e)
{
    new SimpleButtonForm().ShowDialog();
}
private void btnCustomControl_Click(object sender, EventArgs e)
{
    new CustomControlForm().ShowDialog();
}
private void btnThirdPartyControl_Click(object sender, EventArgs e)
{
    new ThirdPartyControlForm().ShowDialog();
}
private void btnThirdPartyControlWithStyle_Click(object sender, EventArgs e)
{
    new ThirdPartyControlWithStyleForm().ShowDialog();
}

Standard UWP Button

Now let’s start with the first scenario where we’re just going to display a standard UWP Button inside the SimpleButtonForm. To do this, the first thing we need to do is to reference the Microsoft.Toolkit.Forms.UI.XamlHost NuGet package.

Next, we’re going to add code in the SimpleButtonForm constructor to create the instance of both the Button and the WindowsXamlHost. The WindowsXamlHost is the wrapper that makes it really easy to add UWP based controls to the Windows Forms application.

public SimpleButtonForm()
{
    InitializeComponent();

    var myHostControl = new Microsoft.Toolkit.Forms.UI.XamlHost.WindowsXamlHost();
    myHostControl.Dock = System.Windows.Forms.DockStyle.Fill;
    myHostControl.Name = "hostUwpButton";

    var uwpButton = new Windows.UI.Xaml.Controls.Button();
    uwpButton.Content = "Say Something!";
    uwpButton.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;
    uwpButton.VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
    uwpButton.Click += UwpButton_Click;

    myHostControl.Child = uwpButton;
    this.Controls.Add(myHostControl);
}

private void UwpButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    MessageBox.Show("Hello World!");
}

Important Note: If we run the application at this point we’ll see an error shown in the following image, that reads “WindowsXamlManager and DesktopWindowsXamlSource are supported for apps targeting Windows version 10.0.118226.0 and later”.

To fix this issue we need to include an app.manifest file with the following content:

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
	<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
		<application>
			<!-- Windows 10 -->
			<maxversiontested Id="10.0.18362.0"/>
			<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />

		</application>
	</compatibility>
</assembly>

The app.manifest file needs to be set as the Manifest file for the Windows Forms project via the Application tab of the Project Properties (Right-click on the project in Solution Explorer and select Properties).

Now, we can run the application, click on the button entitled “Simple UWP Button” and then click on the Say Something button.

What we’ve seen so far is simply using the built-in UWP controls. If you want to use your own custom controls, or third party controls, you’ll need to follow some additional steps.

Custom Control

For the custom control scenario, let’s start by creating a new project based on the Class Library project template. Note that you could also use a UWP class library for this and follow the same steps.

In order to add UWP controls to the class library, we’ll update the project file to use the uap10.0.16299 target framework (this step isn’t required if you’re use the UWP class library project template).

<Project Sdk="MSBuild.Sdk.Extras/2.1.2">
  <PropertyGroup>
    <TargetFrameworks>uap10.0.16299</TargetFrameworks>
  </PropertyGroup>
</Project>

Our custom control is going to be very basic with a single Button that’s going to generate a random number that’s displayed in a TextBlock.

<UserControl
    x:Class="UwpControlLibrary.MyCustomControl"
    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"
    mc:Ignorable="d">
    <StackPanel>
        <Button Content="Generate Random Number" Click="RandomNumber_Click" />
        <TextBlock Text="[placeholder]" x:Name="RandomNumberOutputTextBlock" />
    </StackPanel>
</UserControl>

With very simple code behind

private void RandomNumber_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    var rnd = new Random();
    RandomNumberOutputTextBlock.Text = rnd.Next(0, 10000).ToString();
}

Referencing our control isn’t a simple as just adding a project reference to our class library. Instead, we need to provide a context in which our control is going to be instantiated. When our control gets created in a normal UWP application, it does so within the context of the application, which allows for resolution of resources, styles etc. We need to provide a similar context for our control when it’s rendered within a Windows Forms application.

To do this, we need to create a new project based on the Blank App (Universal Windows) project template. I would avoid attempting to use either a UWP class library or multi-targeted class library for this as neither of them will generate the necessary output for the hosting of our custom control in a Windows Forms or WPF application.

In creating the new project, make sure you select 10.0.18362 (version 1903) as the minimum version.

We then need to add a reference to the Microsoft.Tookit.Win32.UI.XamlApplication NuGet package to the UWP application project.

The UWP application needs to reference the control library.

And the Windows Forms application needs to reference both the UWP application and the control library.

Now we can go ahead and add the code to the CustomControlForm to add an instance of the MyCustomControl.

public CustomControlForm()
{
    InitializeComponent();

    var myHostControl = new Microsoft.Toolkit.Forms.UI.XamlHost.WindowsXamlHost();
    myHostControl.Dock = DockStyle.Fill;
    myHostControl.Name = "uwpHost";

    var customControl = new MyCustomControl();
    customControl.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;
    customControl.VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
    myHostControl.Child = customControl;

    this.Controls.Add(myHostControl);
}

At this point if you try to run the Windows Forms application you’ll see build errors similar to the following

Microsoft.VCRTForwarders.140.targets(91,9): warning : Because your app is being built as AnyCPU no Microsoft.VCRTForwarders.140 DLLs were copied to your ouput folder. Microsoft.VCRTForwarders.140 only supports x86, x64, or arm64 applications due to a C++ Runtime dependency.

or

error : The OutputPath property is not set for project 'UwpXamlIslandHostApp.csproj'. Please check to make sure that you have specified a valid combination of Configuration and Platform for this project. Configuration='Debug' Platform='AnyCPU'.

The errors are pointing to a disparity between the platforms that the projects are being built for. To work around this, you need to change the Platform for each project to be consistent. Right-click on the solution in Solution Explorer and select Configuration Manager. For each platform, make sure the same Platform is selected. This may mean that you have to create a new configuration for those projects that only have Any CPU, such as in this example.

From the New Project Platform dialog, select the platform and make sure the “Create new solution platforms” option is unchecked.

With this done, we should be able to run the application and click on the Custom Control button to launch the CustomControlForm that hosts the MyCustomControl. In this case the MyCustomControl encapsulates the functionality for handling the Button click and updating the Text on the TextBlock.

Third Party Control

In this scenario we’re going to reference the Telerik UWP control library (Telerik.UI.for.UniversalWindowsPlatform on NuGet) and make use of the RadCalendar. The first step is to simply add the reference to the NuGet package. I’m going to go ahead and add it to both the Windows Forms project, as well as both the UWP application and class library projects.

With the reference added, we can simply create an instance of the RadCalendar inside the constructor of the ThirdPartyControlForm.

public ThirdPartyControlForm()
{
    InitializeComponent();

    var myHostControl = new Microsoft.Toolkit.Forms.UI.XamlHost.WindowsXamlHost();
    myHostControl.Dock = DockStyle.Fill;
    myHostControl.Name = "uwpHost";

    var customControl = new Telerik.UI.Xaml.Controls.Input.RadCalendar();
    customControl.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;
    customControl.VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
    myHostControl.Child = customControl;
            
    this.Controls.Add(myHostControl);
}

And without any further changes, we can go ahead and run the Windows Forms application and click on the Third Party Control button. This will show the ThirdPartyControlForm with the RadCalendar visible.

Third Party Control With Style

The last scenario also makes use of the RadCalendar. This time we’re going to combine it with other Windows Forms controls to illustrate how you can use data binding and apply styles.

To begin with we’re going to use the Windows Forms designer to put together a basic layout. Unfortunately even though the WindowsXamlHost control appears in the Toolbox, an exception is thrown by Visual Studio when attempting to add it directly to the Form. Instead, I’ve added a Panel which will act as a placeholder for the WindowsXamlHost, and subsequently the RadCalendar.

I’ve also added a Windows Forms DateTimePicker and a Label. The idea is that the user should be able to use either the RadCalendar or the DateTimePicker to select a date, which will be displayed in the Label below.

We’ll add a very simple class that will be used for data binding.

public class DataModel : INotifyPropertyChanged
{
    private string dateAsString;
    private DateTime myDate;

    public event PropertyChangedEventHandler PropertyChanged;

    public string DateAsString
    {
        get => dateAsString; set
        {
            dateAsString = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DateAsString)));
        }
    }

    public DateTime MyDate
    {

        get => myDate;
        set
        {
            myDate = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyDate)));
            DateAsString = myDate.ToString("O");
        }
    }
}

In terms of data binding to the RadCalendar, we have a couple of options. We could manually create the data binding expression. This seems quite archaic, so alternatively we can specify the binding in XAML. However, this only works if the instance of the RadCalendar is being created in XAML, so that we can specify the binding expression. Easily done – by creating a CustomCalendar UserControl in our Control Library, with the following XAML.

<UserControl x:Class="UwpControlLibrary.CustomCalendar"
             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:input="using:Telerik.UI.Xaml.Controls.Input"
             xmlns:telerikCalendar="using:Telerik.UI.Xaml.Controls.Input.Calendar"
             mc:Ignorable="d">
    <UserControl.Resources>
        <telerikCalendar:CalendarDateToSingleDateRangeConverter x:Key="converter" />
    </UserControl.Resources>
    <input:RadCalendar SelectedDateRange="{Binding MyDate, Converter={StaticResource converter}, Mode=TwoWay}"
                       SelectionMode="Single" />
</UserControl>

Note that in this case, being able to do the binding in XAML is particularly useful since we need to create and use an instance of the CalendarDateToSingleDateRangeConverter. This converter allows for binding a single DateTime property (MyDate) to the SelectedDateRange property.

Back to the Windows Forms project, the code for creating the instance of the CustomCalendar control and wiring up the data binding with the other controls on the page, looks like this.

public ThirdPartyControlWithStyleForm()
{
    InitializeComponent();

    var myHostControl = new Microsoft.Toolkit.Forms.UI.XamlHost.WindowsXamlHost();
    myHostControl.Dock = DockStyle.Fill;
    myHostControl.Name = "uwpHost";

    var customControl = new CustomCalendar();
    customControl.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;
    customControl.VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
    myHostControl.Child = customControl;

    pnlXamlIsland.Controls.Add(myHostControl);

    var data = new DataModel();
    customControl.DataContext = data;
    dtpPickDate.DataBindings.Add(new Binding(nameof(DateTimePicker.Value), data, nameof(DataModel.MyDate), true, DataSourceUpdateMode.OnPropertyChanged));
    lblDate.DataBindings.Add(new Binding(nameof(Label.Text), data, nameof(DataModel.DateAsString), true, DataSourceUpdateMode.OnPropertyChanged));
}

Running the Windows Forms application and clicking on the Third Party Control With Style button shows the ThirdPartyControlWithStyleForm. Either the RadCalendar (nested in the CustomCalendar control) or the DateTimePicker can be used to select a date, which is shown in the Label below.

You’ll notice that the selected date in the RadCalendar has a different style applied with a green background and red border. This has been applied using an implicit style defined in the App.xaml in the UWP application project.

<xamlhost:XamlApplication xmlns:xamlhost="using:Microsoft.Toolkit.Win32.UI.XamlHost"
                          x:Class="UwpXamlIslandHostApp.App"
                          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                          xmlns:input="using:Telerik.UI.Xaml.Controls.Input">
    <xamlhost:XamlApplication.Resources>
        <Style TargetType="input:RadCalendar">
            <Setter Property="SelectedCellStyle">
                <Setter.Value>
                    <input:CalendarCellStyle>
                        <input:CalendarCellStyle.DecorationStyle>
                            <Style TargetType="Border">
                                <Setter Property="Background"
                                        Value="PaleGreen" />
                                <Setter Property="BorderBrush"
                                        Value="MediumVioletRed" />
                            </Style>
                        </input:CalendarCellStyle.DecorationStyle>
                    </input:CalendarCellStyle>
                </Setter.Value>
            </Setter>
        </Style>
    </xamlhost:XamlApplication.Resources>
</xamlhost:XamlApplication>

That’s it for the Windows Forms application – four different scenarios for hosting UWP controls in a Windows Forms application using Xaml Islands.

Windows Presentation Foundation (WPF)

Now we’ll move on to showing the same four scenarios in a WPF application. As we’ve already done a lot of the setup work for the various controls, this section will focus on the differences with the hosting in WPF. To get started we’ll use the WPF Application project template.

Like we did for the Windows Forms application, we’ll create four additional Windows and connect them to four buttons on the main Window of the application.

We’ll need to reference the Microsoft.Toolkit.Wpf.UI.XamlHost NuGet package.

You’ll also need to add an app.manifest file and set it as the manifest file for the WPF application.

Standard UWP Button

The XAML and code behind for the SimpleButtonWindow are as follows.

<Window x:Class="WPFIslandsDemo.SimpleButtonWindow"
        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:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
        mc:Ignorable="d"
        Title="SimpleButtonWindow" Height="450" Width="800">
    <Grid>
        <xamlhost:WindowsXamlHost x:Name="XamlHost"/>
    </Grid>
</Window>

public SimpleButtonWindow()
{
    InitializeComponent();

    var button = new Windows.UI.Xaml.Controls.Button();
    button.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;
    button.VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
    button.Content = "Say Something";
    button.Click += Button_Click;
    XamlHost.Child = button;
}

private void Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    MessageBox.Show("Hello World!");
}

Running this and clicking the Simple UWP Button, we see a new Window appear that’s similar to the Windows Forms example.

Custom Control

Adding the Custom Control is actually even simpler, as we can just specify the MyCustomControl using the InitialTypeName property. Don’t forget to add references to the UWP application and class library projects.

<Window x:Class="WPFIslandsDemo.CustomControlWindow"
        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:WPFIslandsDemo"
        xmlns:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
        mc:Ignorable="d"
        Title="CustomControlWindow" Height="450" Width="800">
    <Grid>
        <xamlhost:WindowsXamlHost InitialTypeName="UwpControlLibrary.MyCustomControl" />
    </Grid>
</Window>

Again, this looks very similar to the Windows Forms output.

Third Party Control

The ThirdPartyControlWindow is very similar to the CustomControlWindow in that we can just specify the InitialTypeName attribute. In this case using the class Telerik.UI.Xaml.Controls.Input.RadCalendar.

The ThirdPartyControlWithStyleWindow is slightly more complex as we need to establish the data binding. Here’s the XAML and code behind.

<Window x:Class="WPFIslandsDemo.ThirdPartyControlWithStyleWindow"
        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:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
        mc:Ignorable="d"
        Title="ThirdPartyControlWithStyleWindow"
        Height="450"
        Width="800">
    <StackPanel>
        <TextBlock Text="Pick as date:" />
        <xamlhost:WindowsXamlHost InitialTypeName="UwpControlLibrary.CustomCalendar" />
        <DatePicker SelectedDate="{Binding MyDate, Mode=TwoWay}" />

        <TextBlock Text="{Binding DateAsString}" />
    </StackPanel>
</Window>

public ThirdPartyControlWithStyleWindow()
{
    InitializeComponent();

    DataContext = new DataModel();
}

Notice how simple this is – the DataContext is applied to both the WPF and UWP controls, making it possible to easily integrate controls from both frameworks into the same layout with minimal fuss.

And that’s how easy it is to integrate UWP controls with both Windows Forms and WPF applications. The source code for this walkthrough is available on GitHub