Поделиться через


Использование API размещения WinRT XAML в классическом приложении C++ (Win32)

Внимание

В этом разделе используются или упоминаются типы из репозитория CommunityToolkit/Microsoft.Toolkit.Win32 GitHub. Дополнительные сведения о поддержке XAML Islands см . в уведомлении о xaml Islands в этом репозитории.

Начиная с Windows 10 версии 1903, не относящиеся к платформе UWP классические приложения (включая приложения C++ (Win32), WPF и Windows Forms) могут использовать API размещения WinRT XAML для размещения элементов управления WinRT XAML в любом элементе пользовательского интерфейса, связанном с дескриптором окна (HWND). Этот API позволяет классическим приложениям, не относящимся к UWP, использовать новейшие возможности пользовательского интерфейса Windows, которые доступны только через элементы управления WinRT XAML. Например, классические приложения, не относящиеся к UWP, могут использовать этот API для размещения элементов управления WinRT XAML, которые используют систему Fluent Design и поддерживают Windows Ink.

API размещения WinRT XAML создает основу с более широким набором элементов управления, который мы предоставляем разработчикам для реализации пользовательского интерфейса Fluent в классических приложениях, не относящихся к UWP. Эта возможность называется XAML Islands. Общие сведения об этой возможности см. в статье Размещение элементов управления WinRT XAML в классических приложениях (XAML Islands).

Примечание.

Если вы хотите поделиться мнением об XAML Island, создайте запрос в репозитории Microsoft.Toolkit.Win32 и оставьте свои комментарии.

Является ли API размещения WinRT XAML правильным выбором для классического приложения?

Этот API предоставляет низкоуровневую инфраструктуру для размещения элементов управления WinRT XAML в классических приложениях. Для классических приложений некоторых типов можно использовать альтернативные более удобные API-интерфейсы для выполнения этой задачи.

  • Если у вас есть классическое приложение C++ и вы хотите разместить элементы управления WinRT XAML в приложении, необходимо использовать API размещения WinRT XAML. Для таких типов приложений нет альтернативы.

  • Для приложений WPF и Windows Forms мы настоятельно рекомендуем применять элементы управления XAML Islands .NET из набора средств сообщества Windows, а не использовать API размещения WinRT XAML. Эти элементы управления используют API размещения WinRT XAML на внутреннем уровне и реализуют все типы поведения, которые при использовании этого API напрямую пришлось бы реализовать самостоятельно, в том числе навигацию с помощью клавиатуры и изменение раскладки.

Так как мы рекомендуем, чтобы только классические приложения C++ использовали API размещения WinRT XAML, в этой статье приводятся инструкции и примеры преимущественно для классических приложений C++. Но API размещения WinRT XAML можно использовать в приложениях WPF и Windows Forms при необходимости. В этой статье рассматривается соответствующий исходный код для элементов управления ведущего приложения для WPF и Windows Forms в наборе средств сообщества Windows, чтобы вы могли увидеть, как API размещения WinRT XAML используется этими элементами управления.

Как использовать API размещения XAML

Чтобы выполнить пошаговые инструкции с примерами кода по использованию API размещения XAML в классических приложениях C++, см. следующие статьи:

Примеры

Способ использования API размещения WinRT XAML в коде зависит от типа, структуры приложения и других факторов. Чтобы продемонстрировать, как использовать этот API в контексте целого приложения, в этой статье рассматриваются следующие примеры кода.

Классические приложения C++ (Win32)

В следующих примерах показано, как использовать API размещения WinRT XAML в классическом приложении C++:

  • Простой пример XAML Islands. В этом примере показана базовая реализация размещения элемента управления WinRT XAML в классическом приложении C++ для распаковки.

  • Пример XAML Islands с настраиваемым элементом управления. Здесь показана полная реализация размещения пользовательского элемента управления WinRT XAML в упакованном классическом приложении C++, а также обработка других типов поведения, таких как ввод с клавиатуры и перемещение фокуса.

WPF и Windows Forms

Элемент управления WindowsXamlHost в наборе средств сообщества Windows служит эталонным примером использования API размещения WinRT XAML в приложениях WPF и Windows Forms. Исходный код доступен по ссылкам ниже.

Примечание.

Мы настоятельно рекомендуем применять элементы управления XAML Islands .NET из набора средств сообщества Windows, а не использовать API размещения WinRT XAML напрямую в приложениях WPF и Windows Forms. В этой статье ссылки на примеры WPF и Windows Forms приведены только для наглядности.

Архитектура API

API размещения WinRT XAML предусматривает следующие основные типы среды выполнения Windows и COM-интерфейсы.

Тип или интерфейс Description
WindowsXamlManager Этот класс представляет платформу XAML для элементов UWP. Он предоставляет один статический метод InitializeForCurrentThread, который инициализирует платформу XAML для UWP в текущем потоке классического приложения.
DesktopWindowXamlSource Этот класс представляет экземпляр содержимого UWP XAML, которое размещается в классическом приложении. Самым важным членом этого класса является свойство Content. Вы назначаете это свойство элементу Windows.UI.Xaml.UIElement, который требуется разместить. Этот класс также содержит другие члены для маршрутизации фокуса с клавиатуры на объекты XAML Islands и с них.
IDesktopWindowXamlSourceNative Этот COM-интерфейс предоставляет метод AttachToWindow, который используется для присоединения элемента XAML Islands к родительскому элементу пользовательского интерфейса в приложении. Каждый объект DesktopWindowXamlSource реализует этот интерфейс.
IDesktopWindowXamlSourceNative2 Этот COM-интерфейс предоставляет метод PreTranslateMessage, который позволяет платформе XAML для UWP правильно обрабатывать определенные сообщения Windows. Каждый объект DesktopWindowXamlSource реализует этот интерфейс.

На следующей схеме показана иерархия объектов в элементе XAML Islands в классическом приложении.

  • На базовом уровне находится элемент пользовательского интерфейса в приложении, в котором требуется разместить элемент XAML Islands. У этого элемента пользовательского интерфейса должен быть HWND. Примеры элементов пользовательского интерфейса, в которых можно разместить XAML Islands, включают Window для классических приложений C++, System.Windows.Interop.HwndHost для приложений WPF и System.Windows.Forms.Control для приложений Windows Forms.

  • На следующем уровне находится объект DesktopWindowXamlSource. Этот объект предоставляет инфраструктуру для размещения элемента XAML Islands. Ваш код отвечает за создание этого объекта и его присоединение к родительскому элементу пользовательского интерфейса.

  • При создании DesktopWindowXamlSource этот объект автоматически создает собственное дочернее окно для размещения элемента управления WinRT XAML. Это собственное дочернее окно в основном абстрагировано от кода, но при необходимости можно получить доступ к его HWND.

  • Наконец, на верхнем уровне находится элемент управления WinRT XAML, который требуется разместить в классическом приложении. Это может быть любой объект UWP, производный от Windows.UI.Xaml.UIElement, в том числе любой элемент управления WinRT XAML, предоставляемый в пакете Windows SDK, а также пользовательский элемент управления.

Архитектура DesktopWindowXamlSource

Примечание.

При размещении объектов XAML Island в классическом приложении можно одновременно использовать несколько деревьев содержимого XAML, выполняющихся в одном и том же потоке. Для доступа к корневому элементу дерева содержимого XAML в XAML Island и получения связанных сведений о контексте, в котором он размещен, используйте класс XamlRoot. API-интерфейсы CoreWindow, ApplicationView и Window не предоставляют правильную информацию об объектах XAML Islands. Дополнительные сведения см. в этом разделе.

Рекомендации

При использовании API размещения XAML WinRT следуйте приведенным ниже рекомендациям для каждого потока, в котором размещены элементы управления XAML WinRT:

  • Создайте выделенный экземпляр WindowsXamlManager для потока.
  • Для каждого элемента управления XAML WinRT, который требуется разместить, создайте экземпляр DesktopWindowXamlSource.
  • Уничтожьте каждый ставший ненужным экземпляр DesktopWindowXamlSource.
  • Перед выходом из потока уничтожьте выделенный экземпляр WindowsXamlManager для потока. Обратите внимание, что уничтожение этого экземпляра WindowsXamlManager является асинхронным и требует очистки очереди сообщений Windows перед выходом из потока. Примеры того, как это сделать, см. на странице с примерами кода XAML Islands.
  • После уничтожения экземпляра WindowsXamlManager для указанного потока создание нового экземпляра WindowsXamlManager в том же потоке не поддерживается и приведет к непредсказуемому поведению.

Устранение неполадок

Ошибка использования API размещения WinRT XAML в приложении UWP

Проблема Решение
Приложение получает COMException со следующим сообщением: Cannot activate DesktopWindowXamlSource. Этот тип не может использоваться в приложении UWP" или "Не удалось активировать WindowsXamlManager. This type cannot be used in a UWP app." (Не удалось активировать DesktopWindowXamlSource. Этот тип нельзя использовать в приложении UWP.) Эта ошибка означает, что вы пытаетесь использовать API размещения WinRT XAML (в частности, вы пытаетесь создать экземпляры типов DesktopWindowXamlSource или WindowsXamlManager) в приложении UWP. API размещения WinRT XAML предназначен для использования только в классических приложениях, не относящихся к UWP, включая приложения WPF, Windows Forms и C++.

Ошибка при попытке использовать типы WindowsXamlManager или DesktopWindowXamlSource

Проблема Решение
Приложение получает исключение со следующим сообщением: "WindowsXamlManager и DesktopWindowXamlSource поддерживаются для приложений, предназначенных для Windows версии 10.0.18226.0 и более поздних". Please check either the application manifest or package manifest and ensure the MaxTestedVersion property is updated." (WindowsXamlManager и DesktopWindowXamlSource поддерживаются для приложений, предназначенных для Windows версии 10.0.18226.0 и более поздних. Проверьте манифест приложения или манифест пакета и убедитесь, что свойство MaxTestedVersion обновлено.) Эта ошибка означает, что приложение пыталось использовать типы WindowsXamlManager или DesktopWindowXamlSource в API размещения WinRT XAML, но ОС не может определить, создано ли приложение для Windows 10 версии 1903 или более поздних. API размещения WinRT XAML впервые появился в виде ознакомительной версии в более ранней версии Windows 10, но поддерживается только начиная с Windows 10 версии 1903.

Чтобы устранить эту проблему, создайте пакет MSIX для приложения и запустите его из пакета или установите пакет NuGet Microsoft.Toolkit.Win32.UI.SDK в проекте.

Ошибка присоединения к окну в другом потоке

Проблема Решение
Приложение получает COMException со следующим сообщением: AttachToWindow method failed because the specified HWND was created on a different thread. (Сбой метода AttachToWindow, так как указанный HWND создан в другом потоке.) Эта ошибка означает, что приложение вызвало метод IDesktopWindowXamlSourceNative::AttachToWindow и передало ему HWND окна, созданный в другом потоке. Этот метод необходимо передать в HWND окна, созданный в том же потоке, что и код, из которого вызывается метод.

Ошибка присоединения к окну в другом окне верхнего уровня

Проблема Решение
Приложение получает исключение COMException со следующим сообщением: "Сбой метода AttachToWindow, так как указанный HWND, производный от другого окна верхнего уровня, отличается от HWND, который ранее передан в AttachToWindow в том же потоке". Эта ошибка означает, что приложение вызвало метод IDesktopWindowXamlSourceNative::AttachToWindow и передало ему HWND окна, производный от другого окна верхнего уровня, отличного от указанного окна в предыдущем вызове этого метода в этом же потоке.

После того как приложение вызовет AttachToWindow в определенном потоке, все остальные объекты DesktopWindowXamlSource в том же потоке могут быть присоединены только к окнам, являющимся потомками того же окна верхнего уровня, которое передано в первый вызов AttachToWindow. Когда все объекты DesktopWindowXamlSource закрыты для конкретного потока, следующий DesktopWindowXamlSource снова можно присоединить к любому окну.

Чтобы устранить эту проблему, закройте все объекты DesktopWindowXamlSource, привязанные к другим окнам верхнего уровня в этом потоке, или создайте другой поток для этого DesktopWindowXamlSource.