Para ver el artículo en inglés, active la casilla Inglés. También puede ver el texto en inglés en una ventana emergente si pasa el puntero del mouse por el texto.
Traducción
Inglés

Interoperabilidad de WPF y Win32

 

Publicado: junio de 2016

En este tema se proporciona información general sobre cómo interoperar código WPF y Win32.  Windows Presentation Foundation (WPF) proporciona un entorno enriquecido para la creación de aplicaciones.  Sin embargo, cuando tenga que una inversión sustancial en código Win32, puede que sea más efectivo parte de ese código.  

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

  • Hospedar el contenido WPF en una ventana Win32.  Con esta técnica, puede utilizar las capacidades gráficas avanzadas de WPF dentro del marco de una ventana estándar y una aplicación Win32.  

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

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

Las API WPF son código administrado, pero la mayoría de los programas Win32 existentes están escritos en C++ no administrado.  No es posible llamar a WPFAPI desde un verdadero programa no administrado.  Sin embargo, utilizando la opción /clr con el compilador Microsoft Visual C++, es posible crear un programa mixto administrado-no administrado, donde puede mezclar de manera transparente llamadas API administradas y no administradas.  

Una complicación del nivel del proyecto es que no es posible compilar archivos Lenguaje XAML (Extensible Application Markup Language) en un proyecto C++.  Hay varias técnicas de división de proyectos para compensarlo.  

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

  • Cree un ejecutable C# para el contenido WPF y haga que use como referencia una Archivo DLL C++ que incluya el contenido Win32.

  • Utilice Load para cargar el XAML, en tiempo de ejecución, en lugar de compilar el XAML.

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

Utilice el enfoque que funcione mejor para usted.

System_CAPS_noteNota

Si no ha utilizado C++/CLI antes, quizás observe algunas palabras clave “nuevas” como gcnew y nullptr en los ejemplos de código de interoperación. Estas palabras clave reemplazan la sintaxis más antigua de doble- subrayado (__gc) y proporcionan una sintaxis más natural para el código administrado en C++.  Para obtener más información sobre las características administradas de C++/CLI, vea Extensiones de componentes para plataformas de tiempo de ejecución y Hello, C++/CLI.  

Para aprovechar al máximo WPF "HWND interop", debe entender cómo WPF utiliza HWND.  Para cualquier HWND, no es posible mezclar la representación de WPF la representación de DirectX o la de GDI / GDI+.  Esto tiene varias implicaciones.  En primer lugar, para mezclar estos modelos de representación debe crear una solución de interoperación y usar segmentos designados de interoperación para cada modelo de la representación que decida 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 mayor detalle en el tema Información general sobre áreas de la tecnología.  

Todos los elementos de WPF de la pantalla están respaldados, en último término, por un HWND.  Al crear un WPFWindow, WPF se crea un HWND de nivel superior, que utiliza un objeto HwndSource para colocar el objeto Window y su contenido WPF dentro del HWND.  El resto del contenido WPF de la aplicación comparte ese HWND singular.  Una excepción son los menús, los elementos desplegables de los cuadros combinados y otros elementos emergentes.  Estos elementos crean su propia ventana de nivel superior, que es la causa de que un menú de WPF pueda ir más allá del borde del HWND de la ventana que lo contiene.  Al utilizar HwndHost para colocar un HWND dentro de WPF, WPF informa a Win32 de cómo colocar el nuevo HWND secundario respecto al HWND Window de WPF.  

Un concepto relacionado con HWND es la transparencia dentro de y entre HWND.  Esto también se explica en el tema Información general sobre áreas de la tecnología.  

La clave para hospedar un WPF en una ventana Win32 es la clase HwndSource.  Esta clase envuelve el contenido WPF en una ventana Win32, para que el contenido WPF se pueda incorporar en la interfaz de usuario (UI) como una ventana secundaria.  El enfoque siguiente combina Win32 y WPF en una aplicación única.  

  1. Implemente el contenido WPF (el elemento raíz del contenido) como una clase administrada.  Normalmente, la clase hereda de una de las clases que pueden contener varios elementos secundarios y/o utilizarse como un elemento raíz, tales como DockPanel o Page.  En pasos subsiguientes, esta clase se conoce como la clase de contenido WPF y a las instancias de la clase se hace referencia como objetos de contenido WPF.  

  2. Implemente una aplicación Win32 con C++/CLI.  Si se está empezando con una aplicación de C++ no administrada existente, normalmente se puede habilitar para que llame al código administrado cambiando la configuración del proyecto, de modo que incluya la marca de compilador /clr (en este tema no se describe el ámbito completo de lo que puede ser necesario para admitir la compilación /clr).  

  3. Establezca el modelo de subprocesos en apartamento de un único subproceso (STA).  WPF utiliza este modelo de subprocesos.  

  4. Administre la notificación WM_CREATE en el procedimiento de la ventana.

  5. Dentro del controlador (o de una función a la que llame el controlador), haga lo siguiente:

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

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

    3. Asigne una referencia al contenido WPF a la propiedad RootVisual del objeto HwndSource.

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

  6. Implemente una clase administrada que contiene un campo estático que contiene una referencia al objeto de contenido WPF.  Esta clase permite recibir una referencia al objeto de contenido WPF desde el código Win32 pero, lo que es más importante, evita que HwndSource se someta inadvertidamente a la recolección de elementos no utilizados.  

  7. Reciba las notificaciones del objeto de contenido WPF adjuntando un controlador a uno o más de los eventos del objeto de contenido WPF.

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

System_CAPS_noteNota

Puede hacer una parte o toda la definición de tipo de contenido de WPF para el paso Uno en XAML mediante la clase parcial predeterminada de la clase de contenido, si genera un ensamblado independiente y haga referencia. Aunque se incluye normalmente un objeto de Application como parte de la compilación de XAML en un ensamblado, no finaliza para anteriormente mediante ese Application como parte de la interoperabilidad, solo usa uno o más de las clases de la raíz de los archivos de XAML referidos por la aplicación y hace referencia a sus clases parciales.  El resto del procedimiento es esencialmente idéntico al perfilado anteriormente.  

Cada uno de estos pasos se muestra mediante código en el tema Tutorial: Hospedar contenido de WPF en Win32.

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

  1. Crear un árbol de elementos para una aplicación WPF (puede hacerse mediante código o mediante marcado).  Buscar un punto adecuado y permitido en el árbol de elementos donde la implementación HwndHost se pueda agregar como un elemento secundario.  En el resto de estos pasos, este elemento se conoce como el elemento de reserva.  

  2. Derivar de HwndHost para crear un objeto que contenga el contenido Win32.

  3. En esa clase de host, invalidar el método BuildWindowCore de HwndHost.  Devolver el HWND de la ventana hospedada.  Quizá desee envolver los controles reales como una ventana secundaria de la ventana devuelta; envolver los controles en una ventana de host es una medio sencillo para que el contenido WPF reciba notificaciones de los controles.  Esta técnica ayuda a corregir algunos problemas Win32 relativos al control de mensajes en el límite del control hospedado.  

  4. Invalidar los métodos DestroyWindowCore y WndProc de HwndHost.  La intención aquí es procesar la limpieza y quitar las referencias al contenido hospedado, en particular si se creó alguna referencia a objetos no administrados.  

  5. En el archivo de código subyacente, cree una instancia de la clase que hospeda el control y conviértala en un elemento secundario del elemento de reserva.  Normalmente utilizaría un controlador de eventos como Loaded o el constructor de clase parcial.  Pero también podría agregar el contenido de interoperación mediante un comportamiento en tiempo de ejecución.  

  6. Procesar los mensajes de ventana seleccionados, tales como las notificaciones de control.  Hay dos enfoques.  Ambos proporcionan idéntico acceso a la secuencia de mensajes, por lo que la elección es, en gran medida, una cuestión de comodidad de programación.  

    • Implementar el procesamiento de mensajes para todos los mensajes (no solamente para los mensajes de cierre del sistema) en la invalidación del método WndProc de HwndHost.

    • Haga que el WPF hospedador procese los mensajes administrando el evento MessageHook.  Este evento se provoca para cada mensaje que se envía al procedimiento de ventana principal de la ventana hospedada.  

    • No se puede procesar mensajes de ventanas que estén fuera del proceso utilizando WndProc.

  7. Realice la comunicación con la ventana hospedada utilizando la invocación de plataforma para llamar a la función SendMessage no administrada.

Al seguir estos pasos, se crea una aplicación que funciona con la entrada de mouse.  Puede agregar compatibilidad con tabulación a la ventana hospedada implementando la interfaz IKeyboardInputSink.  

Cada uno de estos pasos se muestra mediante código en el tema Tutorial: Hospedar un control de Win32 en WPF.

Puede pensar en HwndHost como en un control especial.  (Técnicamente, HwndHost es una clase derivada de FrameworkElement, no una clase derivada de Control, pero puede considerarse un control a efectos de la interoperación). HwndHost abstrae la naturaleza de Win32 subyacente del contenido hospedado de modo que el resto de WPF considera que el contenido hospedado es otro objeto de tipo control, que debe presentar y procesar entradas.   HwndHost se comporta generalmente como cualquier otro objeto WPF FrameworkElement, aunque hay algunas diferencias importantes relacionadas con la salida (dibujo y gráficos) y la entrada (mouse y teclado) basadas en las limitaciones de compatibilidad de los HWND subyacentes.  

  • FrameworkElement, que es la clase base de HwndHost, tiene varias propiedades que implican cambios en la interfaz de usuario.  Entre ellas hay propiedades como FrameworkElement.FlowDirection, que cambia el diseño de los elementos que hay dentro de ese elemento como un elemento primario.  Sin embargo, la mayoría de estas propiedades no están asignadas a posibles equivalentes Win32, aun cuando puedan existir tales equivalentes.  Demasiadas de estas propiedades y sus significados son demasiado específicas de la tecnología de representación para que las asignaciones resulten prácticas.  Por consiguiente, establecer propiedades como FlowDirection en HwndHost no tiene ningún efecto.  

  • HwndHost no se puede rotar, escalar, sesgar ni verse afectada de ningún otro modo por una transformación.

  • HwndHost no admite la propiedad Opacity (mezcla alfa).  Si el contenido de HwndHost realiza operaciones System.Drawing que incluyan información alfa, no se trata de una infracción, pero el objeto HwndHost en conjunto sólo admite Opacidad = 1,0 (100%).  

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

  • HwndHost no respeta la zona de recorte de su objeto UIElement primario.  Esto puede ser un problema si intenta colocar una clase HwndHost dentro de un área de desplazamiento o de un control Canvas.  

  • En general, mientras los dispositivos de entrada estén en el ámbito de la región Win32 hospedad en HwndHost, los eventos de entrada van directamente a Win32.

  • Aunque el mouse esté encima del objeto HwndHost, la aplicación no recibirá eventos del mouse WPF y el valor de la propiedad WPFIsMouseOver será false.

  • Aunque HwndHost tiene el foco de teclado, la aplicación no recibirá eventos de teclado WPF y el valor de la propiedad WPFIsKeyboardFocusWithin será false.

  • Cuando el foco está dentro de HwndHost y cambia a otro control dentro del objeto HwndHost, la aplicación no recibirá los eventos WPFGotFocus o LostFocus.

  • Las propiedades y eventos de lápiz relacionados son análogos y no proporcionan información mientras el lápiz está encima del objeto HwndHost.

Las interfaces IKeyboardInputSink y IKeyboardInputSite permiten usar el teclado sin diferencias en una mezcla de aplicaciones WPF y Win32:

  • Tabulación entre componentes Win32 y WPF

  • Las teclas de acceso y los aceleradores funcionan tanto cuando el foco está dentro de un componente Win32 como cuando está dentro de un componente WPF.

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

Las interfaces solamente proporcionan compatibilidad para lo que ocurre en la transición entre las regiones WPF y Win32.  Dentro de la región Win32, el comportamiento de tabulación está completamente controlado por la lógica Win32 implementada para la tabulación, si existe.  

Mostrar: