Пошаговое руководство. Размещение содержимого WPF в Win32
Windows Presentation Foundation (WPF) предоставляет среду с широкими возможностями для создания приложений. Однако если вы существенно потратились на код Win32, то добавление функциональности WPF в ваше приложение может быть более эффективно, чем переписывание исходного кода. WPF предоставляет простой механизм для размещения WPF содержимого в Win32 окна.
Этом руководстве описывается создание примера приложения, размещения содержимого WPF в окне Win32, в котором узлы WPF содержимого в Win32 окна. Вы можете расширить этот пример для размещения любого окна Win32. Поскольку этот пример включает смешанный управляемый и неуправляемый код, приложение создается на C++/CLI.
Требования
В этом учебнике предполагается, что вы знакомы с основами программирования в WPF и Win32. Основные сведения о WPF программирования, см. в разделе Приступая к работе. Общие сведения о Win32 программирования, следует ссылаться на любой из многочисленных книг по этой теме, в частности программирования Windows Чарльза Петцольда.
Так как пример, сопровождающий этот учебник, реализован в C++/CLI, это руководство предполагает знакомство с использованием C++ программу Win32API а также понимание программирования управляемого кода. Знакомство с C++/CLI желательно, но не обязательно.
Note
Этот учебник включает ряд примеров кода из связанного образца приложения. Однако для удобства чтения он не содержит полный пример кода. Полный образец кода, см. в разделе размещение содержимого WPF в окне Win32.
Основная процедура
В этом разделе описаны основные процедуру, используемую для узла WPF содержимого в Win32 окна. В остальных разделах подробно описывается каждый шаг.
Ключом к размещению WPF содержимого Win32 окно HwndSource класса. Этот класс заключает WPF содержимого в Win32 окно, в котором она должна быть включена в ваш UI как дочернего окна. Следующий подход объединяет Win32 и WPF в одном приложении.
Реализуйте вашей WPF содержимое в виде управляемого класса.
Реализуйте приложение Win32 с помощью C++/CLI. Если вы начинаете с существующего приложения и неуправляемого кода C++, то обычно можете позволить приложению вызывать управляемый код, изменив параметры проекта, чтобы включить флаг компилятора
/clr
.Установите в качестве потоковой модели однопотоковое подразделение (STA).
Обрабатывать WM_CREATEуведомления в процедуре окна и выполните следующее:
Создайте новый объект HwndSource с родительским окном в качестве его параметра
parent
.Создайте экземпляр вашего класса содержимого WPF.
Назначьте ссылку WPF объект содержимого RootVisual свойство HwndSource.
Получите HWND для содержимого. Свойство Handle объекта HwndSource содержит дескриптор окна (HWND). Чтобы получить HWND, который можно использовать в неуправляемой части приложения, приведите
Handle.ToPointer()
к HWND.
Реализуйте управляемый класс, содержащий статическое поле для хранения ссылки на ваше содержимое WPF. Этот класс позволяет получить ссылку на содержимое WPF из вашего кода Win32.
Назначьте содержимое WPF этому статическому полю.
Получайте уведомления от WPF содержимого путем присоединения обработчика к одному или нескольким из WPF события.
Взаимодействуйте с содержимым WPF с помощью ссылки, которую вы сохранили в статическом поле, для задания свойств и т. д.
Note
Можно также использовать XAML для реализации вашей WPF содержимого. Однако его необходимо компилировать отдельно как DLL и ссылаться на эту DLL из своего приложения Win32. Оставшаяся часть процедуры аналогична описанной выше.
Реализация ведущего приложения
В этом разделе описывается размещение WPF содержимого в базовом Win32 приложения. Само содержимое реализуется в C++/CLI как управляемый класс. Большей частью это просто программирование WPF. Ключевые аспекты реализации содержимого рассматриваются в реализация содержимого WPF.
Базовое приложение
Отправной точкой для ведущего приложения было создание шаблона Visual Studio 2005.
Откройте Visual Studio 2005 и выберите новый проект из файл меню.
Выберите Win32 из списка Visual C++ типы проектов. Если язык по умолчанию не C++, вы найдете эти типы проектов в разделе другие языки.
Выберите проект Win32 шаблон, задайте имя для проекта и нажмите кнопку ОК для запуска мастер приложений Win32.
Примите параметры мастера по умолчанию и нажмите кнопку Готово для запуска проекта.
Этот шаблон создает базовое приложение Win32, включая:
точку входа для приложения;
окно со связанной процедурой окна (WndProc);
Меню с файл и помочь заголовки. Файл меню имеет выхода элемента, который закрывает приложение. Помочь меню имеет о элемента, который запускает простое диалоговое окно.
Прежде чем приступить к созданию кода для узла WPF содержимого, необходимо внести два изменения в этот базовый шаблон.
Сначала следует скомпилировать проект как управляемый код. По умолчанию проект компилируется как неуправляемый код. Однако поскольку WPF реализуется в управляемом коде, проект должен быть скомпилирован соответствующим образом.
Щелкните правой кнопкой мыши имя проекта в обозревателе решений и выберите свойства в контекстном меню, чтобы запустить страницы свойств диалоговое окно.
Выберите свойства конфигурации в представлении дерева в левой области.
Выберите среда CLR поддержку от проекта по умолчанию списка в правой области.
Выберите Поддержка Common Language Runtime (/ clr) из раскрывающегося списка.
Note
Этот флаг компилятора позволяет использовать управляемый код в приложении, но ваш неуправляемый код будет продолжать компилироваться как раньше.
WPF Используется однопотоковое подразделение (STA), потоковая модель. Чтобы правильно работать с WPF содержимого кода, вам необходимо установить потоковой модели приложения в STA путем применения атрибута к точке входа.
Размещение содержимого WPF
WPF Содержимое является простым приложением ввода адреса. Оно состоит из нескольких элементов управления TextBox для получения имени пользователя, адреса и т. д. Существует также два Button элементов управления, ОК и отменить. Когда пользователь щелкает ОК, кнопки Click обработчик событий собирает данные из TextBox элементы управления, назначает его соответствующим свойствам и вызывает пользовательское событие, OnButtonClicked
. Когда пользователь щелкает отменить, обработчик просто вызывает OnButtonClicked
. Объект аргумента события для OnButtonClicked
содержит логическое поле, которое указывает, какая кнопка была нажата.
Код для размещения WPF содержимое реализуется в обработчике WM_CREATE уведомления в главном окне.
GetHwnd
Метод принимает сведения о размере и позиции, а также родительский дескриптор окна и возвращает дескриптор окна размещенного WPF содержимого.
Note
Нельзя использовать директиву #using
для пространства имен System::Windows::Interop
. Иначе будет создан конфликт имен между структурой MSG в этом пространстве имен и структурой MSG, объявленной в winuser.h. Вместо этого необходимо использовать полные имена для доступа к содержимому этого пространства имен.
Нельзя разместить WPF содержимое непосредственно в окне приложения. Вместо этого сначала создайте объект HwndSource, который должен включать в себя содержимое WPF. Этот объект по сути является окном, предназначенным для размещения WPF содержимого. Вы размещаете HwndSource объекта в родительском окне, создав его в качестве дочернего элемента Win32 окно, которое является частью приложения. Параметры конструктора HwndSource содержат в основном те же сведения, которые будут передаваться в CreateWindow при создании дочернего окна Win32.
Далее необходимо создать экземпляр WPF объекта содержимого. В этом случае содержимое WPF реализуется как отдельный класс WPFPage
, использующий C++/CLI. Можно также реализовать содержимое WPF с помощью XAML. Тем не менее, для этого необходимо настроить отдельный проект и построить WPF содержимое в виде DLL. Вы можете добавить ссылку на эту DLL в свой проект и использовать эту ссылку для создания экземпляра содержимого WPF.
Отобразить WPF содержимого в вашем дочернем окне, назначьте ссылку WPF содержимого RootVisual свойство HwndSource.
Следующая строка кода присоединяет обработчик событий WPFButtonClicked
к событию WPF содержимого OnButtonClicked
. Этот обработчик вызывается, когда пользователь щелкает ОК или отменить кнопки. См. в разделе взаимодействие с содержимым дальнейшее обсуждение этого обработчика событий.
В последней строке кода показан возврат дескриптора окна (HWND), который связан с объектом HwndSource. Вы можете использовать этот дескриптор из своего кода Win32 для отправки сообщений в размещенное окно, хотя в этом примере так не делается. Объект HwndSource инициирует событие каждый раз при получении сообщения. Для обработки сообщений вызовите метод AddHook, чтобы присоединить обработчик сообщений, а затем обрабатывать сообщения в этом обработчике.
Хранение ссылки на содержимое WPF
Для многих приложений потребуется взаимодействовать с содержимым WPF позднее. Например, может возникнуть необходимость изменить свойства содержимого WPF или разместить в объекте HwndSource другое содержимое WPF. Для этого потребуется ссылка на объект HwndSource или содержимое WPF. Объект HwndSource и связанное с ним содержимое WPF остаются в памяти до уничтожения дескриптора окна. Однако переменная, которая назначается объекту HwndSource, выйдет из области действия сразу после возврата из процедуры окна. Обычный способ решения этой проблемы с приложениями Win32 заключается в использовании статической или глобальной переменной. К сожалению, нельзя назначать управляемый объект таким типам переменных. Вы можете назначить дескриптор окна, связанный с объектом HwndSource, глобальной или статической переменной, но которая не предоставляет доступ к самому объекту.
Самым простым решением этой проблемы является реализация управляемого класса, который содержит набор статических полей для хранения ссылок на любые управляемые объекты, к которым требуется доступ. В данном примере используется класс WPFPageHost
для хранения ссылки на содержимое WPF, а также начальные значения его свойств, которые могут быть изменены пользователем позднее. Это определяется в заголовке.
В последней части GetHwnd
функция присваивает значения этим полям для последующего использования, пока myPage
остается в области.
Взаимодействие с содержимым WPF
Существует два типа взаимодействия с содержимым WPF. Приложение получает сведения из WPF содержимого, когда пользователь щелкает ОК или отменить кнопки. Приложение также имеет UI, в котором пользователь может изменять изменять различные свойства содержимого WPF, такие как цвет фона или размер шрифта по умолчанию.
Как упоминалось выше, когда пользователь нажимает одну из кнопок, содержимое WPF вызывает событие OnButtonClicked
. Приложение присоединяет обработчик к этому событию, чтобы получать эти уведомления. Если ОК была нажата кнопка, обработчик возвращает сведения о пользователе из WPF содержимого и отображает его в наборе статических элементов управления.
Обработчик получает объект аргумента WPF пользовательского события из содержимого MyPageEventArgs
. Объекта IsOK
свойству true
Если ОК была нажата, и false
Если отменить была нажата кнопка.
Если ОК была нажата кнопка, обработчик получает ссылку на WPF содержимого из класса контейнера. Затем он собирает сведения о пользователе, которые хранятся в соответствующих свойствах содержимого WPF, и использует статические элементы управления для отображения этих сведений в родительском окне. Поскольку данные содержимого WPF хранятся в виде управляемых строк, они должны быть маршалированы для использования элементом управления Win32. Если отменить была нажата кнопка, обработчик удаляет данные из статических элементов управления.
Приложение UI предоставляет набор переключателей, с помощью которых пользователь может изменять цвет фона для содержимого WPF и некоторые свойства, связанные со шрифтами. Следующий пример представляет выдержку из процедуры окна приложения (WndProc), содержащую обработку его сообщения, которая устанавливает разные свойства в различных сообщениях, включая цвет фона. Остальные примеры похожи и не приводятся. Подробные сведения и контекст см. в полном примере.
Чтобы задать цвет фона, получите ссылку на содержимое WPF (hostedPage
) из WPFPageHost
и установите в свойстве цвета фона соответствующий цвет. В примере используется три варианта цвета: исходный цвет, светло-зеленый и светло-оранжевый. Исходный цвет фона хранится в виде статического поля в классе WPFPageHost
. Чтобы задать другие два цвета, создайте новый объект SolidColorBrush и передайте в конструктор значение статического цвета из объекта Colors.
Реализация страницы WPF
Вы можете размещать и использовать WPF содержимого без каких-либо знаний о фактической реализации. Если WPF содержимое было упаковано в отдельную DLL, его можно встроить в любом CLR языка. Ниже приведено краткое пошаговое описание реализации C++/CLI, которая используется в данном примере. Этот подраздел состоит из следующих подразделов.
Макет
UI Элементов в WPF содержимое состоят из пяти TextBox элементы управления, связанных с Label элементов управления: Имя, адрес, городе, штате и Zip. Существует также два Button элементов управления, ОК и Отмена
Содержимое WPF реализовано в классе WPFPage
. Макет обрабатывается с помощью элемента макета Grid. Этот класс наследует от Grid, который фактически делает его корневым элементом содержимого WPF.
WPF Содержимого конструктор принимает требуемые ширину и высоту и размеры Grid соответствующим образом. Затем он определяет базовый макет, создавая набор ColumnDefinition и RowDefinition объектов и добавления их в Grid базовый объект ColumnDefinitions и RowDefinitions коллекций, соответственно. Это задает сетку из пяти строк и семи столбцов с размерами, определяемыми содержимым ячеек.
Затем конструктор добавляет элементы UI в объект Grid. Первый элемент — это текст заголовка, который является элементом управления Label, выравниваемым по центру в первой строке сетки.
Следующая строка содержит элемент управления Label «Имя» и связанный с ним элемент управления TextBox. Так как тот же код используется для каждой пары «метка/текстовое поле», он помещается в пару закрытых методов и используется для всех пяти пар «метка/текстовое поле». Эти методы создают соответствующий элемент управления и вызывают класс Grid статического метода SetColumn и методы SetRow для размещения элементов управления в соответствующую ячейку. После создания элемента управления в примере вызывается метод Add в свойстве Children объекта Grid для добавления элемента управления в сетку. Для добавления оставшихся пар «метка/текстовое поле» используется аналогичный код. Подробности см. в примере кода.
Реализация этих двух методов выглядит следующим образом:
Наконец, в этом примере добавляется ОК и отменить кнопки и присоединяет обработчик событий к их Click события.
Возврат данных в главное окно
При нажатии одной из кнопок вызывается ее событие Click. Главное окно может просто присоединять обработчики к этим событиям и получать данные напрямую из элементов управления TextBox. В примере используется несколько менее прямой подход. Он обрабатывает Click в WPF контента, а затем вызывает пользовательское событие OnButtonClicked
для уведомления WPF содержимого. Это позволяет WPF содержимого выполнить некоторую проверку параметров перед уведомлением узла. Обработчик получает текст из элементов управления TextBox и назначает его общим свойствам, из которых узел может получать сведения.
Объявление события в WPFPage.h:
Обработчик событий Click в WPFPage.cpp:
Установка свойств WPF
Win32 Узла позволяет пользователю изменять разные WPF свойства содержимого. Со стороны Win32 это просто изменение свойств. Реализация в классе содержимого WPF несколько более сложная, поскольку нет одного глобального свойства, которое управляет шрифтами для всех элементов управления. Вместо этого для каждого элемента управления изменяется соответствующее свойство в методах доступа set этого свойства. В следующем примере показано код DefaultFontFamily
свойство. Задание свойства вызывает закрытый метод, который в свою очередь устанавливает свойства FontFamily для различных элементов управления.
Из WPFPage.h:
Из WPFPage.cpp: