使用 UWP XAML 托管 API

重要

本主题使用或提及 CommunityToolkit/Microsoft.Toolkit.Win32 GitHub 存储库中的类型。 有关 UWP XAML 岛支持的重要信息,请参阅该存储库中的 XAML 岛通知

非 UWP 桌面应用(包括 C++ 桌面(Win32)、WPF 和 Windows 窗体 应用)可以使用 UWP XAML 托管 API在与窗口句柄(HWND)关联的任何 UI 元素中托管 UWP XAML 控件。 有关此功能的概述,请参阅托管桌面应用中的 UWP XAML 控件(UWP XAML Islands)。

UWP XAML 托管 API 是否适合桌面应用?

UWP XAML 托管 API 提供用于在桌面应用中托管 UWP XAML 控件的低级别基础结构。 某些类型的桌面应用可以选择使用替代、更方便的 API 来实现此目标。

  • 如果你有一个 C++ 桌面应用,并且想要在应用中托管 UWP XAML 控件,则必须使用 UWP XAML 托管 API。 这些类型的应用没有替代方法。

  • 对于WPF和Windows 窗体应用,强烈建议在 Windows Community Toolkit 中使用 XAML Island .NET 控件,而不是直接使用 UWP XAML 托管 API。 这些控件在内部使用 UWP XAML 托管 API,并实现所有行为,例如键盘导航和布局更改,而如果你直接使用该 API,你将需要自己处理这些行为。

由于我们建议仅使用 C++ 桌面应用使用 UWP XAML 托管 API,本文主要提供有关 C++ 桌面应用的说明和示例。 但是,可以选择在WPF和Windows 窗体应用中使用 UWP XAML 托管 API。 本文指向 Windows Community Toolkit 中WPF和Windows 窗体的 host 控件的相关源代码,以便查看这些控件如何使用 UWP XAML 托管 API。

了解如何使用 XAML 托管 API

若要遵循有关在 C++ 桌面应用中使用 XAML 托管 API 的代码示例的分步说明,请参阅以下文章:

Samples

在代码中使用 UWP XAML 托管 API 的方式取决于应用类型、应用设计以及其他因素。 为了帮助说明如何在完整应用的上下文中使用此 API,本文介绍以下示例中的代码。

C++ 桌面 (Win32)

以下示例演示如何在 C++ 桌面应用中使用 UWP XAML 托管 API:

  • 简单的 XAML 岛示例。 此示例演示了在未打包的 C++ 桌面应用中托管 UWP XAML 控件的基本实现。

  • 具有自定义控件示例的 XAML Island。 此示例演示了在打包的 C++ 桌面应用中托管自定义 UWP XAML 控件以及处理其他行为(如键盘输入和焦点导航)的完整实现。

WPF和Windows 窗体

Windows Community Toolkit 中的 WindowsXamlHost 控件用作在WPF和Windows 窗体应用中使用 UWP XAML 托管 API 的参考示例。 源代码在以下位置可用:

注释

强烈建议在Windows社区工具包中使用 XAML Island .NET 控件,而不是直接在WPF和Windows 窗体应用中使用 UWP XAML 托管 API。 本文中的WPF和Windows 窗体示例链接仅用于说明目的。

API 的体系结构

UWP XAML 托管 API 包括这些主要Windows 运行时类型和 COM 接口。

类型或接口 Description
WindowsXamlManager 此类定义表示 UWP XAML 框架。 此类提供单个静态 InitializeForCurrentThread 方法,该方法初始化桌面应用中当前线程上的 UWP XAML 框架。
DesktopWindowXamlSource 此类表示您在桌面应用中托管的 UWP XAML 内容的实例。 此类最重要的成员是 Content 属性。 将此属性分配给要托管的 Windows.UI.Xaml.UIElement 。 此类还具有用于将键盘焦点导航传入和传出 XAML 岛的其他成员。
IDesktopWindowXamlSourceNative 此 COM 接口提供 AttachToWindow 方法,用于将应用中的 XAML 岛附加到父 UI 元素。 每个 DesktopWindowXamlSource 对象实现此接口。
IDesktopWindowXamlSourceNative2 此 COM 接口提供 PreTranslateMessage 方法,使 UWP XAML 框架能够正确处理某些 Windows 消息。 每个 DesktopWindowXamlSource 对象实现此接口。

下图说明了托管在桌面应用中的 XAML 岛中的对象的层次结构。

  • 在基本级别是应用中要托管 XAML 岛的 UI 元素。 此 UI 元素必须具有窗口句柄(HWND)。 托管 XAML 岛的 UI 元素示例包括适用于 C++ 桌面应用的 window, 适用于 WPF 应用的 System.Windows.Interop.HwndHost,Windows 窗体 应用的 System.Windows.Forms.Control

  • 在下一级别是 DesktopWindowXamlSource 对象。 此对象提供用于托管 XAML 岛的基础结构。 代码负责创建此对象并将其附加到父 UI 元素。

  • 创建 DesktopWindowXamlSource 时,此对象会自动创建一个本机子窗口来托管 UWP XAML 控件。 此本机子窗口的细节主要从代码中被抽象化掉,但如有必要,您可以访问它的句柄(HWND)。

  • 最后,您要在桌面应用中托管的 UWP XAML 控件位于顶级。 这可以是派生自 Windows 的任何 UWP 对象。Ui。Xaml.UIElement,包括Windows SDK 提供的任何 UWP XAML 控件以及自定义用户控件。

DesktopWindowXamlSource 体系结构

注释

在桌面应用中托管 UWP XAML Islands 时,可以在同一线程上同时运行多个 XAML 内容树。 若要访问 XAML 岛中 XAML 内容树的根元素,并获取有关其托管上下文的相关信息,请使用 XamlRoot 类。 CoreWindowApplicationView窗口 API 不会为 UWP XAML 岛提供正确的信息。 有关详情,请参阅本部分

最佳做法

使用 UWP XAML 托管 API 时,请遵循托管 UWP XAML 控件的每个线程的以下最佳做法:

故障排除

在 UWP 应用中使用 UWP XAML 托管 API 时出错

問题 决议
你的应用收到一条 COMException ,并显示以下消息:“无法激活 DesktopWindowXamlSource。 此类型不能在 UWP 应用中使用。“或”无法激活 WindowsXamlManager”。 此类型不能在 UWP 应用中使用。 此错误表示你尝试在 UWP 应用中使用 UWP XAML 托管 API(具体而言,你正尝试在 UWP 应用中实例化 DesktopWindowXamlSourceWindowsXamlManager 类型)。 UWP XAML 托管 API 仅用于非 UWP 桌面应用,例如WPF、Windows 窗体和 C++ 桌面应用程序。

尝试使用 WindowsXamlManager 或 DesktopWindowXamlSource 类型时出错

問题 决议
你的应用程序收到一个异常,消息是:“WindowsXamlManager 和 DesktopWindowXamlSource 支持面向 Windows 版本 10.0.18226.0 及更高版本的应用程序。 请检查应用程序清单或包清单,并确保 MaxTestedVersion 属性已更新。 此错误表明应用程序尝试在 UWP XAML 托管 API 中使用 WindowsXamlManagerDesktopWindowXamlSource 类型,但 OS 无法确定应用是生成以面向版本 1903 或更高版本Windows 10版本。 UWP XAML 托管 API 在早期版本的 Windows 10 中首次作为预览版引入,但仅在 Windows 10 版本 1903 中受支持。

若要解决此问题,请为应用创建 MSIX 包并从该包运行,或在 project 中安装 Microsoft.Toolkit.Win32.UI.SDK NuGet 包。

附加到不同线程上的窗口时出错

問题 决议
你的应用收到一条 COMException ,并显示以下消息:“AttachToWindow 方法失败,因为指定的 HWND 是在不同的线程上创建的。 此错误指示应用程序调用 IDesktopWindowXamlSourceNative::AttachToWindow 方法,并向其传递了另一个线程上创建的窗口的 HWND。 必须将创建在与调用此方法的代码相同线程上的窗口的 HWND 传递给此方法。

附加到不同顶级窗口时出错

問题 决议
你的应用收到一条 COMException,并显示以下消息:“AttachToWindow 方法失败,因为指定的 HWND 隶属于与之前在同一线程中传递给 AttachToWindow 的 HWND 不同的顶级窗口。” 此错误表示应用程序调用IDesktopWindowXamlSourceNative::AttachToWindow方法,并将一个窗口的 HWND 传递给它,该窗口是从与先前在同一线程上调用此方法时指定的不同顶层窗口派生的。

应用程序在特定线程上调用 AttachToWindow 后,同一线程上所有其他 DesktopWindowXamlSource 对象只能附加到同一顶级窗口的后代,该窗口是在对 AttachToWindow 的第一次调用中传递的。 当特定线程关闭所有 DesktopWindowXamlSource 对象时,下一个 DesktopWindowXamlSource 将再次自由附加到任何窗口。

若要解决此问题,请关闭绑定到此线程上其他顶级窗口的所有 DesktopWindowXamlSource 对象,或为此 DesktopWindowXamlSource 创建新线程。