Compartir a través de


Interoperación de WPF y Win32

En este tema se proporciona información general sobre cómo interoperar Windows Presentation Foundation (WPF) y código Win32. WPF proporciona un entorno enriquecido para crear aplicaciones. Sin embargo, cuando tienes una inversión sustancial en código Win32, podría ser más eficaz reutilizar parte de ese código.

Conceptos básicos de interoperación de WPF y Win32

Hay dos técnicas básicas para la interoperación entre WPF y código Win32.

  • Hospede el contenido de WPF en una ventana win32. Con esta técnica, puedes usar las funcionalidades de gráficos avanzadas de WPF dentro del marco de una aplicación y ventana win32 estándar.

  • Hospede una ventana win32 en el contenido de WPF. Con esta técnica, puedes usar un control Win32 personalizado existente en el contexto de otro contenido de WPF y pasar datos a través de los límites.

Cada una de estas técnicas se presenta conceptualmente en este tema. Para obtener una ilustración más orientada a código de hospedaje de WPF en Win32, vea Tutorial: Hospedaje de contenido de WPF en Win32. Para obtener una ilustración más orientada a código de hospedaje de Win32 en WPF, vea Tutorial: Hospedaje de un control Win32 en WPF.

Proyectos de interoperación de WPF

Las API de WPF son código administrado, pero la mayoría de los programas Win32 existentes se escriben en C++no administrado. No puede llamar a las API de WPF desde un programa no administrado verdadero. Sin embargo, mediante el uso de la /clr opción con el compilador de Microsoft Visual C++, puede crear un programa administrado mixto no administrado en el que puede mezclar sin problemas llamadas API administradas y no administradas.

Una complicación de nivel de proyecto es que no se pueden compilar archivos de Lenguaje de marcado extensible de aplicaciones (XAML) en un proyecto de C++. Hay varias técnicas de división de proyectos para compensar esto.

  • Cree un archivo DLL de C# que contenga todas las páginas XAML como un ensamblado compilado y, a continuación, haga que el archivo ejecutable de C++ incluya ese archivo DLL como referencia.

  • Cree un ejecutable de C# para el contenido de WPF y haga que haga referencia a un archivo DLL de C++ que contenga el contenido de Win32.

  • Usa Load para cargar cualquier XAML en tiempo de ejecución, en lugar de compilar tu XAML.

  • No use XAML en absoluto y escriba todo el WPF en el código, creando el árbol de elementos desde Application.

Use el enfoque que mejor le convenga.

Nota:

Si no ha usado antes C++/CLI, es posible que observe algunas palabras clave "nuevas", como gcnew y nullptr en los ejemplos de código de interoperación. Estas palabras clave reemplazan a la sintaxis de doble subrayado anterior (__gc) y proporcionan una sintaxis más natural para el código administrado en C++. Para más información sobre las características administradas de C++/CLI, consulte Extensiones de componente para plataformas en tiempo de ejecución.

Cómo WPF usa Hwnds

Para aprovechar al máximo la interoperabilidad de HWND en WPF, necesitas entender cómo WPF utiliza los HWND. Para cualquier HWND, no se puede mezclar la representación de WPF con la representación de DirectX o la representación de GDI/GDI+. Esto tiene una serie de implicaciones. Principalmente, para combinar estos modelos de representación, debe crear una solución de interoperación y usar segmentos designados según el modelo de representación que elija utilizar. Además, el comportamiento de representación crea una restricción de espacio aéreo para lo que la solución de interoperación puede lograr. El concepto de "espacio aéreo" se explica con más detalle en el tema Introducción a las regiones tecnológicas.

Todos los elementos de WPF en la pantalla están respaldados en última instancia por un HWND. Al crear un WPF Window, WPF crea un HWND de nivel superior y usa un HwndSource para colocar el Window y su contenido de WPF dentro del HWND. El resto del contenido de WPF en la aplicación comparte ese único HWND. Una excepción son los menús, los menús desplegables de cuadro combinado y otras ventanas emergentes. Estos elementos crean su propia ventana de nivel superior, por lo que un menú WPF puede ir más allá del borde de la ventana HWND que lo contiene. Cuando se usa HwndHost para colocar un HWND dentro de WPF, WPF informa a Win32 de cómo colocar el nuevo HWND secundario en relación con el HWND de WPF Window .

Un concepto relacionado con HWND es la transparencia dentro y entre cada HWND. Esto también se describe en el tema Introducción a las regiones tecnológicas.

Hospedar contenido de WPF en una ventana de Microsoft Win32

La clave para hospedar un WPF en una ventana win32 es la HwndSource clase . Esta clase ajusta el contenido de WPF en una ventana win32, de modo que el contenido de WPF se pueda incorporar a la interfaz de usuario como una ventana secundaria. El siguiente enfoque combina Win32 y WPF en una sola aplicación.

  1. Implemente el contenido de WPF (el elemento raíz de contenido) como una clase administrada. Normalmente, la clase hereda de una de las clases que pueden contener varios elementos secundarios o que se usan como elemento raíz, como DockPanel o Page. En los pasos posteriores, esta clase se conoce como la clase de contenido wpF y las instancias de la clase se conocen como objetos de contenido de WPF.

  2. Implemente una aplicación de Windows con C++/CLI. Si comienza con una aplicación existente de C++ no administrada, generalmente puede habilitarla para que llame al código administrado cambiando la configuración del proyecto para incluir la marca del compilador /clr (el alcance completo de lo que podría ser necesario para admitir la compilación /clr no se describe en este tema).

  3. Establezca el modelo de subprocesos en Single Threaded Apartment (STA). WPF utiliza este modelo de subprocesos.

  4. Manipule la notificación WM_CREATE en su procedimiento de ventana.

  5. En el controlador (o una función a la que llama el controlador), haga lo siguiente:

    1. Cree un nuevo HwndSource objeto con la ventana primaria HWND como parámetro parent .

    2. Cree una instancia de la clase de contenido de WPF.

    3. Asigne una referencia del objeto de contenido WPF a la propiedad del objeto HwndSourceRootVisual.

    4. La propiedad HwndSource del objeto Handle contiene el identificador de ventana (HWND). Para obtener un HWND que puede usar en la parte no administrada de la aplicación, convierta Handle.ToPointer() en un HWND.

  6. Implemente una clase administrada que contenga un campo estático que contenga una referencia al objeto de contenido de WPF. Esta clase permite obtener una referencia al objeto de contenido WPF desde el código Win32; pero, más importante aún, impide que HwndSource sea recogido inadvertidamente por el recolector de basura.

  7. Reciba notificaciones del objeto de contenido de WPF adjuntando un controlador a uno o varios de los eventos de objeto de contenido de WPF.

  8. Para comunicarse con el objeto de contenido de WPF, utilice la referencia almacenada en el campo estático para establecer propiedades, llamar métodos, etc.

Nota:

Puedes realizar alguna o todas las definiciones de clase de contenido de WPF para el paso Uno en XAML mediante la clase parcial predeterminada de la clase de contenido, si generas un ensamblado independiente y, a continuación, haces referencia a él. Aunque normalmente incluyes un Application objeto como parte de la compilación del XAML en un ensamblado, no terminas usando eso Application como parte de la interoperación, solo usas una o varias de las clases raíz para los archivos XAML a los que hace referencia la aplicación y haces referencia a sus clases parciales. El resto del procedimiento es esencialmente similar al descrito anteriormente.

Cada uno de estos pasos se ilustra a través del código del tema Tutorial: Hospedaje de contenido de WPF en Win32.

Hospedar una ventana de Microsoft Win32 en WPF

La clave para hospedar una ventana win32 dentro de otro contenido de WPF es la HwndHost clase . Esta clase ajusta la ventana en un elemento WPF que se puede agregar a un árbol de elementos WPF. HwndHost también admite API que le permiten realizar tareas como procesar mensajes para la ventana hospedada. El procedimiento básico es el siguiente:

  1. Cree un árbol de elementos para una aplicación WPF (puede ser a través de código o marcado). Busque un punto adecuado y permitido en el árbol de elementos donde se puede agregar la HwndHost implementación como elemento secundario. En el resto de estos pasos, este elemento se conoce como elemento de reserva.

  2. Deriva de HwndHost para crear un objeto que almacene tu contenido de Win32.

  3. En esa clase host, invalide el HwndHost método BuildWindowCore. Devuelve el HWND de la ventana hospedada. Es posible que desee ajustar los controles reales como una ventana secundaria de la ventana devuelta; Ajustar los controles en una ventana host proporciona una manera sencilla de que el contenido de WPF reciba notificaciones de los controles. Esta técnica ayuda a corregir algunos problemas de Win32 relacionados con el control de mensajes en el límite del control hospedado.

  4. Invalide los HwndHost métodos DestroyWindowCore y WndProc. La intención aquí es procesar la limpieza y quitar las referencias al contenido hospedado, especialmente si creó referencias a objetos no administrados.

  5. En el archivo de código subyacente, cree una instancia de la clase de hospedaje de controles y consíjala como elemento secundario del elemento de reserva. Normalmente, usaría un controlador de eventos como Loadedo usar el constructor de clase parcial. Pero también puede agregar el contenido de interoperación a través de un comportamiento en tiempo de ejecución.

  6. Procesar mensajes de ventana seleccionados, como las notificaciones de control. Existen dos enfoques. Ambos proporcionan acceso idéntico al flujo de mensajes, por lo que su elección es en gran medida una cuestión de comodidad de programación.

    • Implemente el procesamiento de mensajes para todos los mensajes (no solo mensajes de apagado) en su sustitución del método HwndHostWndProc.

    • Haga que el elemento WPF de hospedaje procese los mensajes controlando el MessageHook evento. Este evento se genera para cada mensaje que se envía al procedimiento de ventana principal de la ventana hospedada.

    • No se pueden procesar mensajes de ventanas que están fuera de proceso mediante WndProc.

  7. Comunicarse con la ventana hospedada mediante la invocación de plataforma para llamar a la función no administrada SendMessage.

Siguiendo estos pasos, se crea una aplicación que funciona con la entrada del mouse. Puede agregar soporte de tabulación para la ventana alojada mediante la implementación de la IKeyboardInputSink interfaz.

Cada uno de estos pasos se ilustra a través del código del tema Tutorial: Hospedaje de un control Win32 en WPF.

Hwnds en WPF

Puede considerarse HwndHost como un control especial. (Técnicamente, HwndHost es una FrameworkElement clase derivada, no una Control clase derivada, pero se puede considerar un control para fines de interoperación). HwndHost abstrae la naturaleza win32 subyacente del contenido hospedado de modo que el resto de WPF considera que el contenido hospedado es otro objeto similar a control, que debe representar y procesar la entrada. HwndHost por lo general, se comporta como cualquier otro WPF FrameworkElement, aunque hay algunas diferencias importantes en torno a la salida (dibujo y gráficos) y la entrada (mouse y teclado) en función de las limitaciones de lo que pueden admitir los HWND subyacentes.

Diferencias importantes en el comportamiento de salida

  • FrameworkElement, que es la HwndHost clase base, tiene bastantes propiedades que implican cambios en la interfaz de usuario. Estas incluyen propiedades como FrameworkElement.FlowDirection, que cambia el diseño de los elementos dentro de ese elemento como elemento primario. Sin embargo, la mayoría de estas propiedades no se asignan a posibles equivalentes win32, incluso si estos equivalentes podrían existir. Demasiadas de estas propiedades y sus significados son demasiado específicas de la tecnología de representación para que las asignaciones sean prácticas. Por lo tanto, establecer propiedades como FlowDirection on HwndHost no tiene ningún efecto.

  • HwndHost no se puede girar, escalar, sesgar ni verse afectado por una transformación.

  • HwndHost no admite la Opacity propiedad (combinación alfa). Si el contenido dentro de HwndHost realiza System.Drawing operaciones que incluyen información alfa, que no es una infracción, sino que solo HwndHost admite opacidad = 1.0 (100%).

  • HwndHost aparecerá encima de otros elementos de WPF en la misma ventana de nivel superior. Sin embargo, un menú generado por ToolTip o ContextMenu es una ventana independiente de nivel superior, por lo que se comportará correctamente con HwndHost.

  • HwndHost no respeta la región de recorte de su elemento primario UIElement. Esto puede ser un problema si intenta colocar una HwndHost clase dentro de una región de desplazamiento o Canvas.

Diferencias importantes en el comportamiento de entrada

  • En general, mientras que los dispositivos de entrada están dentro del ámbito de la HwndHost región Win32 hospedada, los eventos de entrada van directamente a Win32.

  • Mientras el ratón está sobre HwndHost, su aplicación no recibe eventos de ratón de WPF y el valor de la propiedad WPF IsMouseOver será false.

  • Mientras HwndHost tiene el foco del teclado, su aplicación no recibirá eventos de teclado de WPF y el valor de la propiedad WPF IsKeyboardFocusWithin será false.

  • Cuando el foco está dentro de HwndHost y cambia a otro control dentro de HwndHost, la aplicación no recibirá los eventos GotFocus de WPF ni LostFocus.

  • Las propiedades y los eventos relacionados del lápiz óptico son análogos y no notifican información mientras el lápiz óptico está sobre HwndHost.

Tabbing, Mnemónicos y Aceleradores

Las IKeyboardInputSink interfaces y IKeyboardInputSite permiten crear una experiencia de teclado sin problemas para aplicaciones de WPF y Win32 mixtas:

  • Tabulación entre los componentes Win32 y WPF

  • Mnemonics y aceleradores que funcionan tanto cuando el foco está dentro de un componente Win32 como cuando se encuentra dentro de un componente WPF.

Las clases HwndHost y HwndSource proporcionan implementaciones de IKeyboardInputSink, pero es posible que no controlen todos los mensajes de entrada que desee para escenarios más avanzados. Invalide los métodos adecuados para obtener el comportamiento del teclado que desee.

Las interfaces solo proporcionan compatibilidad con lo que sucede en la transición entre las regiones WPF y Win32. Dentro de la región Win32, el comportamiento de tabulación se controla completamente mediante la lógica implementada de Win32 para el tabulador, si existe.

Consulte también