Using the UWP XAML hosting API in a desktop application

Starting in Windows 10, version 1903, non-UWP desktop applications (including WPF, Windows Forms, and C++ Win32 applications) can use the UWP XAML hosting API to host UWP controls in any UI element that is associated with a window handle (HWND). This API enables non-UWP desktop applications to use the latest Windows 10 UI features that are only available via UWP controls. For example, non-UWP desktop applications can use this API to host UWP controls that use the Fluent Design System and support Windows Ink.

The UWP XAML hosting API provides the foundation for a broader set of controls that we are providing to enable developers to bring Fluent UI to non-UWP desktop applications. This feature is called XAML Islands. For an overview of this feature, see UWP controls in desktop applications.

Note

If you have feedback about XAML Islands, create a new issue in the Microsoft.Toolkit.Win32 repo and leave your comments there. If you prefer to submit your feedback privately, you can send it to XamlIslandsFeedback@microsoft.com. Your insights and scenarios are critically important to us.

Should you use the UWP XAML hosting API?

The UWP XAML hosting API provides the low-level infrastructure for hosting UWP controls in desktop applications. Some types of desktop applications have the option of using alternative, more convenient APIs to accomplish this goal.

  • If you have a C++ Win32 desktop application and you want to host UWP controls in your application, you must use the UWP XAML hosting API. There are no alternatives for these types of applications.

  • For WPF and Windows Forms applications, we strongly recommend that you use the wrapped controls and host controls in the Windows Community Toolkit instead of using the UWP XAML hosting API directly. These controls use the UWP XAML hosting API internally and implement all of the behavior you would otherwise need to handle yourself if you used the UWP XAML hosting API directly, including keyboard navigation and layout changes. However, you can use the UWP XAML hosting API directly in these types of applications if you choose.

This article describes how to use the UWP XAML hosting API directly in your application instead of the controls provided by the Windows Community Toolkit.

Prerequisites

The UWP XAML hosting API has these prerequisites:

  • Windows 10, version 1903 (or later) and the corresponding build of the Windows SDK.
  • Configure your project to use Windows Runtime APIs and enable XAML Islands by following these instructions.

Architecture of XAML Islands

The UWP XAML hosting API includes these main Windows Runtime types and COM interfaces:

  • WindowsXamlManager. This class represents the UWP XAML framework. This class provides a single static InitializeForCurrentThread method that initializes the UWP XAML framework on the current thread in the desktop application.

  • DesktopWindowXamlSource. This class represents an instance of UWP XAML content that you are hosting in your desktop application. The most important member of this class is the Content property. You assign this property to a Windows.UI.Xaml.UIElement that you want to host. This class also has other members for routing keyboard focus navigation into and out of the XAML Islands.

  • IDesktopWindowXamlSourceNative and IDesktopWindowXamlSourceNative2 COM interfaces. IDesktopWindowXamlSourceNative provides the AttachToWindow method, which you use to attach a XAML Island in your application to a parent UI element. IDesktopWindowXamlSourceNative2 provides the PreTranslateMessage method, which enables the UWP XAML framework to process certain Windows messages correctly.

    Note

    These interfaces are declared in the windows.ui.xaml.hosting.desktopwindowxamlsource.h header file in the Windows SDK. By default, this file is in %programfiles(x86)%\Windows Kits\10\Include\<build number>\um. In a C++ Win32 project, you can reference this header file directly. In a WPF or Windows Forms project, you'll need to declare the interfaces in your application code using the ComImport attribute. Make sure your interface declarations exactly match the declarations in windows.ui.xaml.hosting.desktopwindowxamlsource.h.

The following diagram illustrates the hierarchy of objects in a XAML Island that is hosted in a desktop application.

DesktopWindowXamlSource architecture

  • At the base level is the UI element in your application where you want to host the XAML Island. This UI element must have a window handle (HWND). Examples of UI elements in which you can host a XAML Island include System.Windows.Interop.HwndHost for WPF applications, System.Windows.Forms.Control for Windows Forms applications, and a window for C++ Win32 applications.

  • At the next level is a DesktopWindowXamlSource object. This object provides the infrastructure for hosting the XAML Island. Your code is responsible for creating this object and attaching it to the parent UI element.

  • When you create a DesktopWindowXamlSource, this object automatically creates a native child window to host your UWP control. This native child window is mostly abstracted away from your code, but you can access its handle (HWND) if necessary.

  • Finally, at the top level is the UWP control you want to host in your desktop application. This can be any UWP object that derives from Windows.UI.Xaml.UIElement, including any UWP control provided by the Windows SDK as well as custom user controls.

The way you use the UWP XAML hosting API in your code depends on your application type, the design of your application, and other factors. To help illustrate how to use this API in the context of a complete application, this article refers to code from the following samples.

C++ Win32

C++ Win32 Sample. This sample demonstrates a complete implementation of hosting a UWP user control in an unpackaged C++ Win32 application (that is, an application that is not built into an MSIX package).

WPF and Windows Forms

The WindowsXamlHost control in the Windows Community Toolkit acts as a reference sample for using the UWP hosting API in WPF and Windows Forms applications. The source code is available at the following locations:

Host UWP XAML controls

Here are the main steps for using the UWP XAML hosting API to host a UWP control in your application.

  1. Initialize the UWP XAML framework for the current thread before your application creates any of the Windows.UI.Xaml.UIElement objects that it will host in the DesktopWindowXamlSource.

    • If your application creates the DesktopWindowXamlSource object before it creates any of the Windows.UI.Xaml.UIElement objects, this framework will be initialized for you when you instantiate the DesktopWindowXamlSource object. In this scenario, you don't need to add any code of your own to initialize the framework.

    • However, if your application creates the Windows.UI.Xaml.UIElement objects before it creates the DesktopWindowXamlSource object that will host them, your application must call the static WindowsXamlManager.InitializeForCurrentThread method to explicitly initialize the UWP XAML framework before the Windows.UI.Xaml.UIElement objects are instantiated. Your application should typically call this method when the parent UI element that hosts the DesktopWindowXamlSource is instantiated.

    Windows::UI::Xaml::Hosting::WindowsXamlManager windowsXamlManager =
        Windows::UI::Xaml::Hosting::WindowsXamlManager::InitializeForCurrentThread();
    
    global::Windows.UI.Xaml.Hosting.WindowsXamlManager windowsXamlManager =
        global::Windows.UI.Xaml.Hosting.WindowsXamlManager.InitializeForCurrentThread();
    

    Note

    This method returns a WindowsXamlManager object that contains a reference to the UWP XAML framework. You can create as many WindowsXamlManager objects as you want on a given thread. However, because each object holds a reference to the UWP XAML framework, you should dispose the objects to ensure that XAML resources are eventually released.

  2. Create a DesktopWindowXamlSource object and attach it to a parent UI element in your application that is associated with a window handle.

    To do this, you'll need to follow these steps:

    1. Create a DesktopWindowXamlSource object and cast it to the IDesktopWindowXamlSourceNative or IDesktopWindowXamlSourceNative2 COM interface.

    2. Call the AttachToWindow method of the IDesktopWindowXamlSourceNative or IDesktopWindowXamlSourceNative2 interface, and pass in the window handle of the parent UI element in your application.

    3. Set the initial size of the internal child window contained in the DesktopWindowXamlSource. By default, this internal child window is set to a width and height of 0. If you don't set the size of the window, any UWP controls you add to the DesktopWindowXamlSource will not be visible. To access the internal child window in the DesktopWindowXamlSource, use the WindowHandle property of the IDesktopWindowXamlSourceNative or IDesktopWindowXamlSourceNative2 interface. The following examples use the SetWindowPos function to set the size of the window.

    Here are some code examples that demonstrate this process.

    // This example assumes you already have an HWND variable named 'parentHwnd' that
    // contains the handle of the parent window.
    Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopWindowXamlSource;
    auto interop = desktopWindowXamlSource.as<IDesktopWindowXamlSourceNative>();
    check_hresult(interop->AttachToWindow(parentHwnd));
    
    HWND childInteropHwnd = nullptr;
    interop->get_WindowHandle(&childInteropHwnd);
    
    SetWindowPos(childInteropHwnd, 0, 0, 0, 300, 300, SWP_SHOWWINDOW);
    
    // This WPF example assumes you already have an HwndHost named 'parentHwndHost'
    // that will act as the parent UI element for your XAML Island. It also assumes
    // you have used the DllImport attribute to import SetWindowPos from user32.dll
    // as a static method into a class named NativeMethods.
    Windows.UI.Xaml.Hosting.DesktopWindowXamlSource desktopWindowXamlSource =
        new Windows.UI.Xaml.Hosting.DesktopWindowXamlSource();
    
    IntPtr iUnknownPtr = System.Runtime.InteropServices.Marshal.GetIUnknownForObject(
        desktopWindowXamlSource);
    IDesktopWindowXamlSourceNative desktopWindowXamlSourceNative =
        System.Runtime.InteropServices.Marshal.Marshal.GetTypedObjectForIUnknown(
            iUnknownPtr, typeof(IDesktopWindowXamlSourceNative))
            as IDesktopWindowXamlSourceNative;
    
    desktopWindowXamlSourceNative.AttachToWindow(parentHwndHost.Handle);
    
    var childInteropHwnd = desktopWindowXamlSourceNative.WindowHandle;
    NativeMethods.SetWindowPos(childInteropHwnd, HWND_TOP, 0, 0, 300, 300, SWP_SHOWWINDOW);
    
  3. Set the Windows.UI.Xaml.UIElement you want to host to the Content property of your DesktopWindowXamlSource object. The following example sets a Windows.UI.Xaml.Controls.Grid named myGrid to the Content property.

    desktopWindowXamlSource.Content(myGrid);
    
    desktopWindowXamlSource.Content = myGrid;
    

For complete examples that demonstrate these tasks in the context of a working sample application, see the following code files:

Handle keyboard input and focus navigation

There are several things your app needs to do to properly handle keyboard input and focus navigation when it hosts XAML Islands.

Keyboard input

To properly handle keyboard input for each XAML Island, your application must pass all Windows messages to the UWP XAML framework so that certain messages can be processed correctly. To do this, in some place in your application that can access the message loop, cast the DesktopWindowXamlSource object for each XAML Island to an IDesktopWindowXamlSourceNative2 COM interface. Then, call the PreTranslateMessage method of this interface and pass in the current message.

Keyboard focus navigation

When the user navigates through the UI elements in your application using the keyboard (for example, by pressing Tab or direction/arrow key), you'll need to programmatically move focus into and out of the DesktopWindowXamlSource object. When the user's keyboard navigation reaches the DesktopWindowXamlSource, move focus into the first Windows.UI.Xaml.UIElement object in the navigation order for your UI, continue to move focus to the following Windows.UI.Xaml.UIElement objects as the user cycles through the elements, and then move focus back out of the DesktopWindowXamlSource and into the parent UI element.

The UWP XAML hosting API provides several types and members to help you accomplish these tasks.

  • When the keyboard navigation enters your DesktopWindowXamlSource, the GotFocus event is raised. Handle this event and programmatically move focus to the first hosted Windows.UI.Xaml.UIElement by using the NavigateFocus method.

  • When the user is on the last focusable element in your DesktopWindowXamlSource and presses the Tab key or an arrow key, the TakeFocusRequested event is raised. Handle this event and programmatically move focus to the next focusable element in the host application. For example, in a WPF application where the DesktopWindowXamlSource is hosted in a System.Windows.Interop.HwndHost, you can use the MoveFocus method to transfer focus to the next focusable element in the host application.

For examples that demonstrate how to do this in the context of a working sample application, see the following code files:

Handle layout changes

When the user changes the size of the parent UI element, you'll need to handle any necessary layout changes to make sure your UWP controls display as you expect. Here are some important scenarios to consider.

  • In a C++ Win32 application, when your application handles the WM_SIZE message it can reposition the hosted XAML Island by using the SetWindowPos function. For an example, see the SampleApp.cpp code file in the C++ Win32 sample.

  • When the parent UI element needs to get the size of the rectangular area needed to fit the Windows.UI.Xaml.UIElement that you are hosting on the DesktopWindowXamlSource, call the Measure method of the Windows.UI.Xaml.UIElement. For example:

  • When the size of the parent UI element changes, call the Arrange method of the root Windows.UI.Xaml.UIElement that you are hosting on the DesktopWindowXamlSource. For example:

    • In a WPF application you might do this from the ArrangeOverride method of the HwndHost object that hosts the DesktopWindowXamlSource. For an example, see the WindowsXamlHost.Layout.cs file in the Windows Community Toolkit.

    • In a Windows Forms application you might do this from the handler for the SizeChanged event of the Control that hosts the DesktopWindowXamlSource. For an example, see the WindowsXamlHost.Layout.cs file in the Windows Community Toolkit.

Handle DPI changes

The UWP XAML framework handles DPI changes for hosted UWP controls automatically (for example, when the user drags the window between monitors with different screen DPI). For the best experience, we recommend that your Windows Forms, WPF, or C++ Win32 application is configured to be per-monitor DPI aware.

To configure your application to be per-monitor DPI aware, add a side-by-side assembly manifest to your project and set <dpiAwareness> element to PerMonitorV2. For more information about this value, see the description for DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <application xmlns="urn:schemas-microsoft-com:asm.v3">
        <windowsSettings>
            <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
        </windowsSettings>
    </application>
</assembly>

Host custom UWP XAML controls

Important

To host a custom UWP XAML control, you must have the source code for the control so you can compile against it in your application.

If you want to host a custom UWP XAML control (either a control you define yourself or a control provided by a 3rd party), you must perform the following additional tasks in addition to the process described in the previous section.

  1. Define a custom type that derives from Windows.UI.Xaml.Application and also implements IXamlMetadataProvider. This type acts as a root metadata provider for loading metadata for custom UWP XAML types in assemblies in the current directory of your application.

  2. Call the GetXamlType method of your root metadata provider when the type name of the UWP XAML control is assigned (this could be assigned in code at run time, or you might choose to enable this to be assigned in the Visual Studio Properties window).

    For examples, see the following code files:

    • C++ Win32: See the XamlApplication.cpp code file in the C++ Win32 sample.

    • WPF and Windows Forms: See the XamlApplication.cs and UWPTypeFactory.cs code files in the Windows Community Toolkit. These files are part of the shared implementation of the WindowsXamlHost classes for WPF and Windows Forms, which help illustrate how to use the UWP XAML hosting API in those types of apps.

  3. Integrate the source code for the custom UWP XAML control into your host application solution, build the custom control, and use it in your application. For instructions for a WPF or Windows Forms application, see these instructions. For an example for a C++ Win32 application, see the Microsoft.UI.Xaml.Markup and MyApp projects in the C++ Win32 sample.

Troubleshooting

Error using UWP XAML hosting API in a UWP app

Issue Resolution
Your app receives a COMException with the following message: "Cannot activate DesktopWindowXamlSource. This type cannot be used in a UWP app." or "Cannot activate WindowsXamlManager. This type cannot be used in a UWP app." This error indicates you are trying to use the UWP XAML hosting API (specifically, you are trying to instantiate the DesktopWindowXamlSource or WindowsXamlManager types) in a UWP app. The UWP XAML hosting API is only intended to be used in non-UWP desktop apps, such as WPF, Windows Forms, and C++ Win32 applications.

Error attaching to a window on a different thread

Issue Resolution
Your app receives a COMException with the following message: "AttachToWindow method failed because the specified HWND was created on a different thread." This error indicates that your application called the IDesktopWindowXamlSourceNative::AttachToWindow method and passed it the HWND of a window that was created on a different thread. You must pass this method the HWND of a window that was created on the same thread as the code from which you are calling the method.

Error attaching to a window on a different top-level window

Issue Resolution
Your app receives a COMException with the following message: "AttachToWindow method failed because the specified HWND descends from a different top-level window than the HWND that was previously passed to AttachToWindow on the same thread." This error indicates that your application called the IDesktopWindowXamlSourceNative::AttachToWindow method and passed it the HWND of a window that descends from a different top-level window than a window you specified in a previous call to this method on the same thread.

After your application calls IDesktopWindowXamlSourceNative::AttachToWindow on a particular thread, all other DesktopWindowXamlSource objects on the same thread can only attach to windows that are descendants of the same top-level window that was passed in the first call to IDesktopWindowXamlSourceNative::AttachToWindow. When all the DesktopWindowXamlSource objects are closed for a particular thread, the next DesktopWindowXamlSource is then free to attach to any window again.

To resolve this issue, either close all DesktopWindowXamlSource objects that are bound to other top-level windows on this thread, or create a new thread for this DesktopWindowXamlSource.