.NET Core 3: Use UWP Controls in WPF with XAML Islands

In this blog post, you will learn how to use XAML Islands to host the UWP MapControl in a .NET Core 3 Preview 4 WPF application.

XAML Islands is a technology that allows you to host modern UWP controls in your WPF, Windows Forms, and Win32 applications. You can use for example UWP’s InkCanvas or the MapControl, or you can use your custom UWP Controls. This allows you to modernize your apps with Windows 10 features.

XAML Islands is part of the Windows API. It was added in a preview version in Windows 1809. It is now available in version 1 in Windows 1903.

You can read more about XAML Islands in these two fantastic blog posts:

As mentioned, XAML Islands is part of the Windows 10 API. To make XAML Islands easily usable in your applications, Microsoft has created NuGet packages as part of the Windows Community Toolkit. We’ll use such a NuGet package in this blog post to host the UWP MapControl in a WPF application. So, let’s look at the WPF starter app

The WPF App to Start With

As a starter app, I use here a little WPF app that has a DataGrid that displays a few friends you might know.


Beside the properties FirstName, LastName, and City, a friend in that app has also the properties Latitude and Longitude. Here’s for example the snippet that returns Satya:

public class FriendsDataProvider
{
    public IEnumerable<Customer> LoadFriends()
    {
        yield return new Friend
        {
            FirstName = "Satya",
            LastName = "Nadella",
            City = "Redmond",
            Latitude = 47.673988,
            Longitude = -122.121513
        };

        ...
    }
}

If you want to try this sample on your own, you can download the starter app from this link here: https://github.com/thomasclaudiushuber/Wpf-Hosting-Uwp-MapControl/archive/starter.zip.

You find the final solution of this blog post in this repository:
https://github.com/thomasclaudiushuber/Wpf-Hosting-Uwp-MapControl

Set up Your Machine

To run the WPF .NET Core 3 app, you need to install .NET Core 3, and for this blog post the latest version, which is Preview 4. You can read more about Preview 4 in this blog post from Rich Lander, and you can download .NET Core 3 Preview 4 here.

I use Visual Studio 2019 Enterprise edition as an IDE. Not the preview version, I’m on 16.0.2. You can also use the Community or Professional edition to follow along, it doesn’t matter.

But before you can start the application, go in Visual Studio to Tools > Options. In the Options window go to “Projects and Solutions > .NET Core” and ensure that you’ve checked the option “Use previews of the .NET Core SDK” to be able to use .NET Core 3 Preview 4 in Visual Studio. Here’s how it should look:

Now you should be able to run the WPF starter app, so let’s continue and let’s add the UWP MapControl to that WPF app.

Note: I try to hit all the pitfalls I was hitting when playing around with .NET Core Preview 4 and XAML Islands. This should help you to solve the issues on your side. So, let’s go.

Add the UWP MapControl

For WPF, you find two important NuGet packages for XAML Islands:

  • Microsoft.Toolkit.Wpf.UI.XamlHost – Contains helpers to host any UWP control in your WPF app
  • Microsoft.Toolkit.Wpf.UI.Controls – Contains predefined wrapper controls, like InkCanvas and MapControl.

As we want to use the UWP MapControl, let’s install the NuGet package Microsoft.Toolkit.Wpf.UI.Controls in the WPF project. Ensure that you’ve checked the “Include prerelease” option like below to get the package version v6.0.0-preview4 that targets preview 4 of .NET Core.

That v6.0.0-preview4 version of the NuGet package was released on April 23rd, as you can read in Miguel‘s tweet below.

Now, as we’ve added the Microsoft.Toolkit.Wpf.UI.Controls NuGet package, let’s use the MapControl in the MainWindow.xaml file. I just write this in the XAML file

<MapControl Grid.Column="1"
            x:Name="mapControl"/>

When you place the cursor on the MapControl element, Visual Studio will show a light bulb. Press “CTRL+.” to add the missing namespace Microsoft.Toolkit.Wpf.UI.Controls:

This adds the namespace with the “controls” prefix and uses that prefix on the MapControl element like below, and now you should be able to build your solution successfully.

<controls:MapControl Grid.Column="1"
                     x:Name="mapControl"/>

Now let’s run the application.

When I run the application, I get an InvalidCastException. DesktopWindowXamlSource cannot be casted to IDesktopWindowXamlSourceNative2. It looks like below:

The exception above happens, because a .NET Core 3 WPF app with the added NuGet package for XAML Islands needs to run on Windows 1903. The exception above is actually an error based on a mismatch of the NuGet package and the found Windows API, because I’m still running Windows 1809 that contains just the preview version of the XAML Islands API. Let’s check my Windows version. Let’s go to the start menu and let’s type “winver” and let’s hit Enter. This brings up Windows version 1809.

So, let’s update my machine to the latest version of Windows, which is 1903. To update, you can join the Windows Insiders program or you can grab version 1903 from MSDN. I just grabbed it from MSDN and updated with the .ISO file. Now let’s run winver again, and now I see 1903 instead of 1809. Here’s the proof:

Great, now v1 of XAML Islands is available, as I’ve just installed Windows 1903 on my machine. So, let’s run the application again and let’s see what happens.

On Windows 1903 I get another exception, it occurs in the MainWindow’s constructor when the InitializeComponent method is called – and so, when the MapControl is created. It says that I need to target Windows version 10.0.18226.0 or later, as you can see below:

To target that Windows version with your WPF app, you can package your WPF app as MSIX, or you can add an application manifest. Before we look at these options, let’s install the latest Windows SDK that was made for Windows 1903.

Install the Windows SDK for build 18362

The Windows SDK for Windows 1903 (May Update, build 18362) is available since a few days, as you can read below in Clint‘s tweet.

Windows version 1903 is the build version 10.0.18362. To get that corresponding SDK version, run the Visual Studio Installer and modify your existing Visual Studio 2019 installation. As you can see in the screenshot below on the right side, I’ve selected in the Visual Studio Installer the Windows 10 SDK 10.0.18362.0, and it’s installed already on my machine. You can read more about this installation in Clint’s blog post.

Now, the next question is:

How can you target a specific Windows version with your WPF application?

Great question. To target a specific Windows version with your WPF application, you have two options:

  • Add a Windows Application Packaging Project to your solution (Which packages your WPF app in an MSIX package)
  • Create an application manifest file in your WPF project

In this blog post, I’ll use the Windows Application Packaging Project to package it as MSIX. If you don’t want to package your WPF app with MSIX, you can use the application manifest. Matteo has written a great explanation here how to use such an application manifest file. And as he wrote on twitter, there are these two options: MSIX or application manifest.

In this blog post, I’ll use the Windows Application Packaging Project.

Let’s add a Windows Application Packaging Project

Let’s right-click the solution and let’s add a new project. Search in the Dialog for “Windows Application Packaging” and select the template for C#:

Let’s click next, and let’s call the project FriendsApp.Wpf.App:

Now let’s click on Create, and then the Dialog opens up that you know already if you’re familiar with building UWP apps. As you see below, you can select here version 18632 as a minimum version. Let’s do this and let’s click ok:

Now the solution has two projects: The WPF project, and the packaging project:

In the screenshot above, you can see that the packaging project has an “Applications” node in the Solution Explorer. When you right-click it, you get a context menu with an item called “Add Reference…”. Click it, and select in the opening dialog the WPF project as a reference like shown below:

After you clicked OK, you can see in the Solution Explorer that the packaging project is referencing the WPF project. It appears under “Applications”:

Now let’s set the packaging project as a start up project in Solution Explorer by right-clicking it and selecting “Set as StartUp Project” from the context menu. So far, so good. Now let’s just try to build the solution, before we run the application.

On my machine, the build failed. I can see 0 errors in the Error List window, and the Output windows tells me that 1 project succeeded, and 1 project failed. It’s the WPF project that succeeded, which I can find out when I build it alone. And it’s the packaging project that failed.

What you need to do is to add at least one runtime identifier to the WPF project. So, let’s open the FriendsApp.Wpf.csproj file. You can select the FriendsApp.Wpf project in the Solution Explorer and the .csproj file opens up. Then, in that file, you can add a RuntimeIdentifiers element and separate multiple runtime identifiers with semicolons, or you add the RuntimeIdentifier element (without s at the end) for a single runtime identifier. In the csproj-file below, I add win-x86 and win-x64 as runtime identifiers:

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <UseWPF>true</UseWPF>
    <RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifierss>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Toolkit.Wpf.UI.Controls" Version="6.0.0-preview4" />
  </ItemGroup>

</Project>

Now let’s select x64 for the packaging project:

And now let’s rebuild the solution. On my machine, the rebuild succeeded after this step.

Now let’s run the application – ensure that you’re starting the packaging project and not directly the WPF project – and we’re finally here:

The UWP MapControl shows up in the WPF app

After adding the RuntimeIdentifiers element, and after setting x64 as a target for the packaging project, the packaging project can be started, and the UWP MapControl shows up in the running WPF application:

Now let’s use the MapControl.

Use the UWP MapControl in WPF

As you might remember from above, we gave the MapControl in XAML the name mapControl. That means we can access it from the Codebehind file with this name.

Now let’s add an event handler to the DataGrid’s SelectionChanged event to navigate to the latitude and longitude of the selected friend. I’ve implemented it like below. Important to mention is that I’ve added a using directive for the UWP namespace Windows.Devices.Geolocation to use classes like BasicGeoposition and Geopoint, this just worked:

private async void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    try
    {
        if (dataGrid.SelectedItem is Friend friend)
        {
            var basisGeoposition = new BasicGeoposition
            {
                Latitude = friend.Latitude,
                Longitude = friend.Longitude
            };

            var geopoint = new Geopoint(basisGeoposition);

            await mapControl.TrySetViewAsync(geopoint);

            await mapControl.TryZoomToAsync(12);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Now, let’s run the app again and let’s select Satya in the DataGrid. Then the map navigates to Redmond, as you can see below:

Let’s select myself, and the map navigates to the small city where I live, which is called Müllheim.

Great. So, now we have a WPF application running with .NET Core 3.0 preview 4 that uses a UWP MapControl via XAML Islands. Given the fact that .NET Core and the Windows Toolkit NuGet packages are still in preview, and that VS2019 doesn’t have full .NET Core 3 support yet, this was a quite smooth process.

You find the final sample in this GitHub Repo:

https://github.com/thomasclaudiushuber/Wpf-Hosting-Uwp-MapControl 

Happy coding,
Thomas

Share this post

Comments (13)

  • Dew Drop – April 24, 2019 (#2944) | Morning Dew Reply

    […] .NET Core 3 Preview 4: Use UWP Controls in WPF with XAML Islands (Thomas Claudius Huber) […]

    April 24, 2019 at 12:33 pm
  • Andreas Reply

    Hey there, I just found this post (and your blog in general – it looks really cool btw!) and just wanted to say thanks for sharing this! I’m exited to try it by myself today in the evening. =D
    Also I noticed that you wrote the following in your first block of code in the tutorial
    IEnumerable LoadFriends(). In the Github repo it’s the other way around.
    On Github it’s IEnumerable LoadCustomers(). Got a bit confused at first =)

    April 25, 2019 at 8:09 am
    • Thomas Claudius Huber Reply

      Hi Andreas,

      great catch. I rewrote the history on GitHub to adjust it there to LoadFriends too. Sorry for the confusion and thanks for reaching out.

      Happy coding,
      Thomas

      April 25, 2019 at 10:09 am
  • Calling Windows 10 APIs From Your WPF Application – Thomas Claudius Huber Reply

    […] the previous blog post you learned that you can host UWP Controls in WPF, WinForms, and Win32 with a technology called […]

    April 26, 2019 at 8:03 pm
  • Burton Roberts Reply

    Will WPF using XAML Islands be the new preferred choice for future enterprise XAML desktop solutions? Or will it be UWP?

    May 4, 2019 at 7:34 pm
    • Thomas Claudius Huber Reply

      That’s indeed a great question. I think XAML Islands is a great way to bring modern features into existing WPF or WinForms applications. But if you’re building a new application and you know it will run on Windows 10, then I might go with UWP. But as WPF runs on modern .NET Core 3, WPF with XAML Islands is also a great option.

      I see it mostly like this:
      When WPF came out, Microsoft built the ElementHost Control for WinForms to host WPF controls in WinForms applications. Did we continue to build WinForms applications and just host WPF controls in it or did we start building new applications with WPF? Both was true, but if you wanted to build a more modern application, you used WPF. If you have good WinForms knowledge, you might continue with WinForms and you can host WPF controls in your app if they don’t exist in WinForms.

      Today I think it’s exactly the same with UWP and XAML Islands. You can host UWP controls in WPF and WinForms. But does that mean you continue to build WPF and WinForms applications and you just host UWP Controls in them, or do you start building new applications with UWP? I think same answer as above: Both is true. If you want to build a more modern application, you use UWP. And WPF and WinForms are also great options, as you can host UWP controls in them with XAML Islands.

      May 5, 2019 at 10:13 am
  • Callon Campbell Reply

    What is your take on Microsoft killing UWP? Seems like everything that was great about UWP is being pulled out and into WinUI and then you can leverage from WinForms, WPF and Win32. What’s confusing is should we keep using the UWP project template or will it go away?

    https://www.thurrott.com/podcasts/206463/first-ring-daily-live-lets-juwp-to-the-build-conclusion#respond

    https://www.thurrott.com/dev/206351/microsoft-confirms-uwp-is-not-the-future-of-windows-apps

    May 13, 2019 at 9:03 pm
    • Callon Campbell Reply

      I feel like until .NET Core 3 is released and we see more improvements to WinUI and more guidance from Microsoft on moving forward, then UWP is still the solution for now.

      May 13, 2019 at 9:05 pm
      • Thomas Claudius Huber Reply

        Hi Callon, definitely, I think the same, UWP is still the solution for now, and also for the future. I think in the future you’ll use either WinUI with the UWP app model or with the Win32 app model to build new desktop apps.

        May 13, 2019 at 9:18 pm
    • Thomas Claudius Huber Reply

      Microsoft is not killing UWP, with WinUI UWP is stronger than ever before. WinUI 3 will contain UWP XAML and UWP Controls. It decouples these from specific Windows 10 versions, so that we can use these and new features on any Windows version. I would continue to use UWP with WinUI 2.1 today, and then you have all the freedom to switch to Win32 and WinUI3 in the future, as WinUI3 contains the UWP XAML that you write. And if the UWP sandbox is great for you, you continue to use the UWP app model, else you use Win32 app model.

      UWP XAML and WinUI 3 is definitely the future of Windows apps. I don’t know why Paul Thurrott thinks it’s dead? Maybe he means the UWP app model? UWP XAML and WinUI 3 (Which contains UWP Controls) is the future, that was made very clear at MSBuild. Even React Native can be used to build UWP apps. But maybe when WinUI 3 and UWP XAML are usable independently from a Windows 10 version and with Win32, then the UWP app model might not be used so much anymore. But as I wrote already, as WinUI3 is UWP XAML, I think the best you can do today is building your new apps with UWP.

      May 13, 2019 at 9:26 pm
  • Modernizing Existing Windows Desktop Applications | Claudio Bernasconi's TechBlog Reply

    […] Thomas Claudius Huber has written an in-depth blog post about how to use a UWP control in an existing WPF application. […]

    May 15, 2019 at 8:02 am
  • Callon Reply

    Excellent post. Have you tried using the Share contract from a WPF app? If so do you have any insights to get this working?

    Thanks

    August 5, 2019 at 5:47 pm

Leave a Reply to Andreas Cancel reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.