Взаимодействие WPF и Win32
В этом разделе приводится общее описание метода обеспечения взаимодействия WPF и кода Win32. Windows Presentation Foundation (WPF) предоставляет среду с широкими возможностями для создания приложений. Однако если имеется сложный код Win32, возможно, более эффективным будет повторное использование части этого кода.
Основы взаимодействия WPF и Win32
Существуют два основных метода взаимодействия между WPF и кодом Win32.
Размещение содержимого WPF в окне Win32. С помощью этого способа можно использовать дополнительные графические возможности WPF в структуре стандартных окон и приложений Win32.
Размещение окна Win32 в содержимом WPF. С помощью этого способа можно использовать существующий пользовательский элемент управления Win32 в контексте другого содержимого WPF и передавать данные через границы.
Каждый из этих способов детально представлен в этом разделе. Для иллюстрации, Дополнительные примеры кода, на котором размещается WPF в Win32, см. в разделе Пошаговое руководство: Размещение содержимого WPF в Win32. Для иллюстрации, Дополнительные примеры кода, на котором размещается Win32 в WPF, см. в разделе Пошаговое руководство: Размещение элемента управления Win32 в WPF.
Проекты взаимодействия WPF
WPF API are managed code, but most existing [!include[TLA2#tla_win32](../../../../includes/tla2sявляются)], можно создать смешанную управляемую-неуправляемую программу, где могут равномерно смешиваться управляемые и неуправляемые вызовы API.
Существует одна сложность на уровне проекта, которая заключается в том, что нельзя скомпилировать файлы XAML в проект C++. Имеется несколько методов разделения проектов для решения данной проблемы.
Создать библиотеку DLL C#, содержащий все вашей XAML страницы в форме скомпилированной сборки, а затем вашей C++ включите в исполняемый файл DLL как ссылку.
Создайте на C# исполняемый файл для WPF содержимого и его ссылаться C++ DLL , содержащий Win32 содержимого.
Используйте Load загружать все XAML во время выполнения вместо компиляции вашей XAML.
Не используйте XAML вообще и записать все ваши WPF в коде, создавая дерево элементов из Application.
Используйте любой наиболее подходящий способ.
Note
Если вы еще не пользовались C++/CLI, то можете заметить некоторые "новые" ключевые слова, например gcnew
и nullptr
, в примерах кода взаимодействия. Эти ключевые слова заменяют более старый синтаксис с двойным подчеркиванием (__gc
) и обеспечивают более естественный синтаксис для управляемого кода в C++. Дополнительные сведения о возможностях управляемого кода C++/CLI см. в статьях Расширения компонентов для платформ среды выполнения и Знакомство с C ++/ CLI.
Как в WPF используются дескрипторы HWND
Для эффективного использования взаимодействия 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 относительно WPF Window HWND.
С HWND связано понятие прозрачности внутри и между каждым дескриптором HWND. Оно также рассматривается в разделе Общие сведения об областях применения технологий.
Размещение содержимого WPF в окне Microsoft Win32
Ключом к размещению WPF на Win32 окно HwndSource класса. Этот класс заключает содержимое WPF в окно Win32 таким образом, что содержимое WPF может быть включено в UI в качестве дочернего окна. Следующий подход объединяет Win32 и WPF в одном приложении.
Реализуйте содержимое WPF (содержимое корневого элемента) в виде управляемого класса. Как правило, класс наследуется от одного из классов, которые может содержать несколько дочерних элементов или использоваться в качестве корневого элемента, например DockPanel или Page. В последующих шагах этот класс называется классом содержимого WPF, а его экземпляры называются объектами содержимого WPF.
Реализуйте приложение Win32 с помощью C++/CLI. При запуске существующего неуправляемого приложения C++, как правило, можно разрешить ему вызывать управляемый код путем изменения параметров проекта с целью включения флага компилятора
/clr
(полное описание объектов, необходимых для поддержки компиляции/clr
, в этом разделе не приводится).Установите в качестве потоковой модели однопотоковое подразделение (STA). WPF Эта потоковая модель используется.
Обработайте уведомления WM_CREATE в процедуре окна.
В обработчике (или функции, которую вызывает обработчик) выполните указанные ниже действия.
Создайте новый HwndSource объект с HWND родительского окна как его
parent
параметра.Создайте экземпляр вашего класса содержимого WPF.
Назначьте ссылку WPF объект содержимого HwndSource объект RootVisual свойство.
HwndSource Объект Handle свойство содержит дескриптор окна (HWND). Чтобы получить HWND, который можно использовать в неуправляемой части приложения, приведите
Handle.ToPointer()
к HWND.
Реализуйте управляемый класс, содержащий статическое поле, которое хранит ссылку на объект содержимого WPF. Этот класс позволяет получить ссылку на WPF объекта содержимого из вашей Win32 код, но более важно, защищает ваш HwndSource от случайного удаления сборщиком мусора.
Получите уведомления от объекта содержимого WPF, присоединив обработчик к одному или нескольким событиям объекта содержимого WPF.
Для взаимодействия с объектом содержимого WPF используйте ссылку, которую сохранили в статическом поле, чтобы задать свойства, методы вызова и т. д.
Note
Определить класс содержимого WPF для шага 1 можно частично или полностью в XAML с помощью разделяемого класса по умолчанию класса содержимого. Для этого нужно создать отдельную сборку и сослаться на нее. Несмотря на то, что обычно содержат Application объекта в процессе компиляции XAML в сборку, вы не доходят до использования его Application как часть взаимодействие, просто используйте один или несколько корневых классов для XAML файлов называется Чтобы для приложения и ссылаться на их разделяемые классы. Оставшаяся часть процедуры практически аналогична описанной выше.
Каждое из этих действий будет показано в коде в разделе Пошаговое руководство: Размещение содержимого WPF в Win32.
Размещение окна Microsoft Win32 в WPF
Ключом к размещению Win32 окно в другие WPF содержимое HwndHost класса. Он заключает окно в элемент WPF, который можно добавить в дерево элементов WPF. HwndHost также поддерживает API , которые позволяют выполнять такие задачи, как обработка сообщений для размещенного окна. Ниже описываются основные действия.
Создайте дерево элементов для приложения WPF (посредством кода или разметки). Найдите соответствующую и допустимую точку в дереве элементов, где HwndHost реализация может быть добавлен как дочерний элемент. В оставшихся действиях процедуры этот элемент называется элементом резервирования.
Являются производными от HwndHost для создания объекта, который содержит ваши Win32 содержимого.
В размещаемом классе переопределите HwndHost метод BuildWindowCore. Возвратите HWND размещенного окна. Вероятно, вам может потребоваться заключить существующие элементы управления в дочернее окно возвращаемого окна. Заключение элементов управления в основное окно представляет собой простой способ получения уведомлений от элементов управления для содержимого WPF. Этот способ позволяет избежать некоторых проблем Win32, касающихся обработки сообщений на границе размещаемых элементов управления.
Переопределить HwndHost методы DestroyWindowCore и WndProc. Цель этого — выполнить очистку и удалить ссылки на размещаемое содержимое, особенно если созданы ссылки на неуправляемые объекты.
В файле кода программной части создайте экземпляр класса, размещающего элемент управления, и сделайте его дочерним классом элемента резервирования. Обычно используется обработчик событий например Loaded, или использовать конструктор разделяемого класса. Но можно также добавить содержимое взаимодействия с помощью поведения среды выполнения.
Обработайте сообщения выбранного окна, такие как уведомления элементов управления. Существуют два подхода. Оба предоставляют идентичный доступ к потоку сообщений, поэтому выбор во многом определяется удобством программирования.
Реализуйте обработку сообщений для всех сообщений (не только сообщений о завершении работы) в переопределении HwndHost метод WndProc.
Разместите WPF обработку сообщения путем обработки элемента MessageHook событий. Это событие вызывается для каждого сообщения, которое отправляется в главную процедуру размещенного окна.
Не удается обработать сообщения от windows, которые не соответствуют процесс, используя WndProc.
Для взаимодействия с размещенным окном используйте вызов неуправляемого кода с целью вызова неуправляемой функции
SendMessage
.
Следующие действия создают приложение, которое работает с вводом мыши. Вы можете добавить поддержку табуляции для размещенного окна, реализовав IKeyboardInputSink интерфейс.
Каждое из этих действий будет показано в коде в разделе Пошаговое руководство: Размещение элемента управления Win32 в WPF.
Дескрипторы HWND внутри 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.
Важные различия в режиме ввода
В общем случае, когда устройства ввода находятся в пределах HwndHost размещенных Win32 регион, события ввода поступают непосредственно к Win32.
Когда указатель мыши находится над HwndHost, приложение не получает WPF события мыши, а для параметра WPF свойство IsMouseOver будет
false
.Хотя HwndHost имеет фокус клавиатуры, приложение не получит WPF события и значение WPF свойство IsKeyboardFocusWithin будет
false
.Когда фокус находится внутри HwndHost и изменения в другой элемент управления внутри HwndHost, приложение не получит WPF события GotFocus или LostFocus.
Связанные свойства пера и события являются аналогами и не предоставляют сведений, когда перо находится над HwndHost.
Переходы, назначенные клавиши и сочетания клавиш
IKeyboardInputSink И IKeyboardInputSite интерфейсы позволяют создавать взаимодействие с помощью клавиатуры для смешанных WPF и Win32 приложений:
Переход между компонентами Win32 и WPF
Назначенные клавиши и сочетания клавиш, которые работают, когда фокус находится внутри компонента Win32 или WPF.
HwndHost И HwndSource оба класса позволяют реализовывать IKeyboardInputSink, но они могут обрабатывать входящие сообщения, необходимые для более сложных сценариев. Переопределите соответствующие методы, чтобы получить нужное поведение клавиатуры.
Интерфейсы обеспечивают поддержку только для событий перехода между областями WPF и Win32. Внутри области Win32 поведение перехода полностью контролируется логикой перехода, реализованной в Win32, если таковая имеется.