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


Взаимодействие WPF и Win32

В данном разделе приводится общее описание метода обеспечения взаимодействия WPF и кода Win32. Клиент Windows Presentation Foundation (WPF) предоставляет среду с широкими возможностями для создания приложений. Однако если имеется сложный код Win32, возможно, более эффективным будет повторное использование части этого кода.

В этом разделе содержатся следующие подразделы.

  • Основы взаимодействия WPF и Win32
  • Проекты взаимодействия WPF
  • Как WPF использует Hwnds
  • Размещение содержимого WPF в окне Microsoft Win32
  • Размещение окна Microsoft Win32 в WPF
  • Переходы, назначенные клавиши и сочетания клавиш
  • Связанные разделы

Основы взаимодействия WPF и Win32

Существуют два основные метода взаимодействия между WPF и кодом Win32.

  • Размещение содержимого WPF в окне Win32. С помощью этого способа можно использовать дополнительные графические возможности WPF в структуре стандартных окон и приложений Win32.

  • Размещение окна Win32 в содержимом WPF. С помощью этого способа можно использовать существующий пользовательский элемент управления Win32 в контексте другого содержимого WPF и передавать данные через границы.

Каждый из этих способов детально представлен в этом разделе. Дополнительные примеры кода размещения WPF в Win32 см. в разделе Пошаговое руководство. Размещение содержимого WPF в Win32 ("Пошаговое руководство. Размещение страницы Windows Presentation Foundation в приложении Win32"). Дополнительные примеры кода размещения Win32 в WPF см. в разделе Пошаговое руководство. Размещение элемента управления Win32 в WPF ("Пошаговое руководство. Размещение простого элемента управления Win32 в приложении Windows Presentation Foundation").

Проекты взаимодействия WPF

APIs приложения WPF являются управляемым кодом, но большинство существующих программ Win32 записываются в неуправляемом приложении C++. Невозможно вызвать APIs WPF из подлинной неуправляемой программы. Однако с помощью параметра /clr с компилятором Microsoft Visual C++ можно создать смешанную управляемую-неуправляемую программу, где могут равномерно смешиваться управляемые и неуправляемые вызовы API.

Существует одна сложность на уровне проекта, которая заключается в том, что не нельзя скомпилировать файлы Extensible Application Markup Language (XAML) в проекте C++. Имеется несколько методов разделения проектов для решения данной проблемы.

  • Создайте библиотеку DLL C#, содержащую все страницы XAML в виде скомпилированной сборки, а затем включите в исполняемый файл C++ DLL в качестве ссылки.

  • Создайте исполняемый файл C# для содержимого WPF и используйте его ссылку на DLL C++ с содержимым Win32.

  • Используйте Load для загрузки любого объекта XAML во время выполнения вместо компиляции XAML.

  • Не используйте XAML вообще, и записывайте все приложение WPF в коде, создавая дерево элементов из Application.

Используйте любой, наиболее подходящий способ.

ПримечаниеПримечание

Если приложение C++/CLI раньше не использовалось, в примерах кода взаимодействия можно заменить некоторые "новые" слова, такие как gcnew и nullptr. Эти ключевые слова заменяют старый синтаксис с двойным подчеркиванием (__gc) и предоставляют более естественный синтаксис для управляемого кода в C++.Дополнительные сведения о возможностях управляемого кода C++/CLI см. в статьях Language Features for Targeting the CLR ("Языковые возможности в области настройки среды CLR") и Hello, C + +/ CLI ("Знакомство с C ++/ CLI").

Как WPF использует Hwnds

Чтобы расширить "взаимодействие HWND" с WPF, необходимо понимать, как WPF использует HWND. Для любого HWND нельзя смешивать отрисовку WPF с отрисовкой DirectX или отрисовкой GDI / GDI+. Это имеет ряд последствий. Главным образом, для смешивания этих моделей отрисовки необходимо создать решение взаимодействия и использовать разработанные сегменты взаимодействия для каждой модели рендеринга, которая выбрана для использования. Также получаемое при отрисовке изображение создает ограничение "airspace" для решения взаимодействия, которое можно применить. Концепция "airspace" объясняется более подробно в разделе Общие сведения об областях применения технологий.

Все элементы WPF на экране в конечном счете поддерживаются дескриптором окна HWND. При создании WPF объект Window, WPF создает HWND верхнего уровня и использует HwndSource для размещения окна Window и его содержимого WPF внутри HWND. Остальная часть содержимого WPF в приложении также использует этот единственный дескриптор HWND. Исключением являются меню, поля с раскрывающимся списком и другие всплывающие окна. Эти элементы создают свое собственное окно верхнего уровня, поэтому меню WPF может выйти за край HWND окна, которое его содержит. При использовании HwndHost для размещения HWND внутри WPF приложение WPF информирует Win32 о том, как разместить новый дочерний HWND относительно HWND Window WPF.

Связанный c HWND концепт является прозрачным внутри и между каждым HWND. Это также рассматривается в разделе Общие сведения об областях применения технологий.

Размещение содержимого WPF в окне Microsoft Win32

Ключом к размещению WPF в окне Win32 является класс HwndSource. Этот класс переносит WPF содержимое в окно Win32 таким образом, что содержимое WPF может быть включено в user interface (UI) в качестве дочернего окна. Следующий подход объединяет Win32 и WPF в одном приложении.

  1. Реализуйте содержимое WPF (содержимое корневого элемента) в виде управляемого класса. Как правило, класс наследуется из одного из классов, который может содержать несколько дочерних элементов и/или использоваться в качестве корневого элемента, например DockPanel или Page. В последующих шагах этот класс называется классом содержимого WPF, и экземпляры класса называются объектами содержимого WPF.

  2. Реализуйте приложение Win32 с приложением C++/CLI. При запуске существующего неуправляемого приложения C++, как правило, можно активировать вызов управляемого кода путем изменения параметров проекта с целью включения флага компилятора /clr (полное описание объектов, необходимых для поддержки компиляции /clr в данном разделе не приводится).

  3. Установите потоковую модель в однопотоковое подразделение (Single Threaded Apartment — STA). Данная потоковая модель используется WPF.

  4. Обработайте уведомления WM_CREATE в процедуре окна.

  5. В обработчике (или функции, которую вызывает обработчик) выполните следующие действия.

    1. Создайте новый объект HwndSource с родительским HWND окна в качестве его параметра parent.

    2. Создайте экземпляр класса содержимого WPF.

    3. Назначьте ссылку на объект содержимого WPF объекту HwndSource свойства RootVisual.

    4. Объект HwndSource свойства Handle содержит дескриптор окна (HWND). Для получения HWND, который можно использовать в неуправляемой части приложения, преобразуйте Handle.ToPointer() в HWND.

  6. Реализуйте управляемый класс, содержащий статическое поле, которое хранит ссылку на объект содержимого WPF. Этот класс не только позволяет получить ссылку на объект содержимого WPF из кода Win32, но и, что еще более важно, защищает HwndSource от случайного удаления сборщиком мусора.

  7. Получите уведомления из объекта содержимого WPF путем присоединения обработчика к одному или нескольким событиям объекта содержимого WPF.

  8. Свяжитесь с объектом содержимого WPF с помощью ссылки, которую вы сохранили в статическом поле для установки свойств, вызова методов и т. д.

ПримечаниеПримечание

Если создается отдельная сборка, на которую в дальнейшем ссылаются из кода, для выполнения первого шага можно задать некоторые или все определения класса содержимого WPF в XAML, используя разделяемый класс по умолчанию класса содержимого. Хотя обычно в сборку включается объект Application как часть компиляции кода XAML, не завершайте использование объекта Application в качестве части взаимодействия, просто используйте один или несколько корневых классов для файлов XAML, на которые ссылается приложение, и ссылайтесь на их разделяемые классы.Оставшаяся часть процедуры по существу аналогична описанной выше.

Каждое из этих действий будет показано в коде в разделе Пошаговое руководство. Размещение содержимого WPF в Win32.

Размещение окна Microsoft Win32 в WPF

Ключом к размещению окна Win32 внутри другого содержимого WPF является класс HwndHost. Класс облекает окно в оболочку элемента WPF, который можно добавить в дерево элементов WPF. Кроме того, HwndHost поддерживает объекты APIs, которые позволяют выполнять такие задачи, как обработка сообщений для размещенного окна. Основная процедура:

  1. Создайте дерево элемента для приложения WPF (посредством кода или разметки). Найдите соответствующую и допустимую точку в дереве элемента, где реализация HwndHost может быть добавлена в качестве дочернего элемента. В оставшихся шагах этих действий этот элемент называется элементом резервирования.

  2. Создайте класс производный от HwndHost для создания объекта, который хранит содержимое Win32.

  3. В размещаемом классе переопределите метод BuildWindowCore объекта HwndHost. Возвратите HWND размещенного окна. Вероятно, вам может потребоваться заключение существующих элементов управления в оболочку дочернего окна возвращаемого окна; заключение элементов управления в оболочку окна размещения представляет собой простой способ получения уведомлений от элементов управления для содержимого WPF. Этот способ позволяет избежать некоторых проблем Win32, касающихся обработки сообщений на границе размещаемых элементов управления.

  4. Переопределите методы DestroyWindowCore и WndProc объекта HwndHost. Цель этого — обработать очистку и удаление ссылок в размещаемом содержимом, особенно если созданы ссылки на неуправляемые объекты.

  5. В файле кода программной части создайте экземпляр элемента управления, размещающего класс, и сделайте его дочерним элемента резервирования. Обычно используется обработчик событий, такой как Loaded, или конструктор разделяемого класса. Но можно также добавить содержимое взаимодействия с помощью поведения среды выполнения.

  6. Обработка сообщений выбранного окна, таких как уведомления элементов управления. Существуют два подхода. Оба предоставляют идентичный доступ к потоку сообщений, поэтому выбор во многом определяется удобством программирования.

    • Реализуйте обработку сообщений для всех сообщений (не только сообщений о завершении работы) в переопределении метода WndProc объекта HwndHost.

    • Разместите обработку сообщения элемента WPF посредством обработки события MessageHook. Это событие вызывается для каждого сообщения, которое отправляется в главную процедуру окна размещенного окна.

    • Нельзя обрабатывать сообщения из окон, которые не участвуют в процессе, с помощью метода WndProc.

  7. Свяжитесь с размещенным окном с помощью запуска платформы для вызова неуправляемых функций SendMessage.

Следующие действия создают приложение, которое работает с вводом мыши. Можно добавить поддержку перехода с помощью клавиши табуляции для размещенного, реализацией интерфейса IKeyboardInputSink.

Каждое из этих действий будет показано в коде в разделе Пошаговое руководство. Размещение элемента управления Win32 в WPF.

Hwnds внутри WPF

Можно представить HwndHost как специальный элемент управления. (С технической точки зрения, HwndHost представляет собой класс, производный от класса FrameworkElement, а не от класса Control, но его можно считать элементом управления применительно к соответствующему взаимодействию.) HwndHost выделяет базовый характер Win32 размещенного содержимого таким образом, что остальная часть WPF считает размещенное содержимое другим объектом, подобным элементу управления, который должен отображать и обрабатывать входные данные. Как правило, HwndHost ведет себя подобно любому другому элементу WPF FrameworkElement, хотя в зависимости от ограничений в поддержке базовых дескрипторов HWND в данном случае могут существовать некоторые существенные различия применительно к выводу данных (рисование и графика) и вводу данных (мышь и клавиатура).

Важные различия в режиме вывода

  • Объект FrameworkElement, который является базовым классом HwndHost, имеет несколько свойств, которые подразумевают изменение пользовательского интерфейса. Они включают свойства, такие как FrameworkElement.FlowDirection, которые меняют расположение элементов внутри этого элемента как родительского. Однако большая часть этих свойств не сопоставляется с возможными эквивалентами Win32, даже если такие эквиваленты могут существовать. Слишком многие из этих свойств и их значений слишком специфичны для технологии отрисовки, чтобы быть практически применимыми. Поэтому свойства параметров, например FlowDirection для объекта HwndHost, в данном случае не оказывают никакого влияния.

  • Объект HwndHost не могут быть повернут, масштабированы, наклонен или, в противном случае, он будет подвергнут преобразованию.

  • HwndHost не поддерживает свойство Opacity (альфа-смешение). Если содержимое внутри HwndHost выполняет операции System.Drawing, включающие сведения об альфа-составляющей, это не является нарушением, но HwndHost, так как единое целое, поддерживает только Opacity = 1.0 (100 %).

  • HwndHost будет отображаться в верхней части других элементов WPF в окне верхнего уровня. Однако созданное меню ToolTip или ContextMenu является отдельным окном верхнего уровня и поэтому будет работать правильно с объектом HwndHost.

  • HwndHost не поддерживает кадрирование области его родительского объекта UIElement. Эта является потенциальной проблемой при попытке поместить класс HwndHost внутри области прокрутки или Canvas.

Важные различия в режиме ввода

  • В общем случае, когда устройства ввода находятся внутри размещенной области Win32 объекта HwndHost, события ввода поступают непосредственно в приложение Win32.

  • В то время как указатель мыши находится над объектом HwndHost, приложение не получает событий мыши WPF, и значением свойства IsMouseOver приложения WPF будет false.

  • В то время как HwndHost имеет фокус клавиатуры, приложение не получает событий клавиатуры WPF, и значением свойства IsKeyboardFocusWithin приложения WPF будет false.

  • Когда фокус находится внутри HwndHost и переходит в другой элемент управления внутри HwndHost, приложение не получает событий GotFocus или LostFocus приложения WPF.

  • Связанные свойства пера и события являются аналогами и не предоставляют отчет, когда перо находится над объектом HwndHost.

Переходы, назначенные клавиши и сочетания клавиш

Интерфейсы IKeyboardInputSink и IKeyboardInputSite позволяют создавать непрерывное взаимодействие с клавиатурой для смешанных приложений WPF и Win32:

  • Переход между компонентами Win32 и WPF.

  • Назначенные клавиши и сочетания клавиш, которые работают, когда фокус находится внутри компонента Win32 или WPF.

Классы HwndHost и HwndSource обеспечивают реализацию IKeyboardInputSink, но они могут не обрабатывать все входные сообщения, необходимые для дополнительных скриптов. Переопределите соответствующие методы, чтобы получить нужное поведение клавиатуры.

Интерфейсы только обеспечивают поддержку событий переходов между областями WPF и Win32. Внутри области Win32 поведение переключения полностью контролируется реализованной логикой Win32 для переходов, если таковые имеются.

См. также

Ссылки

HwndHost

HwndSource

System.Windows.Interop

Основные понятия

Пошаговое руководство. Размещение элемента управления Win32 в WPF

Пошаговое руководство. Размещение содержимого WPF в Win32