Compartir a través de


Información general sobre la interoperabilidad de WPF y Win32

Actualización: noviembre 2007

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.

Este tema contiene las secciones siguientes.

  • Fundamentos de interoperación entre WPF y Win32
  • Proyectos de interoperación WPF
  • Cómo WPF utiliza Hwnd
  • Hospedar contenido WPF en una ventana Microsoft Win32
  • Hospedar una ventana Microsoft Win32 en WPF
  • Tabulación, teclas de acceso y aceleradores
  • Temas relacionados

Fundamentos de interoperación entre WPF y Win32

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 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 un control simple de Win32 en una aplicación de Windows Presentation Foundation. Para ver una ilustración más orientada al código del hospedaje de Win32 en WPF, vea Tutorial: Hospedar contenido de Windows Presentation Foundation en una aplicación Win32.

Proyectos de interoperación 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 transparentemente llamadas API administradas y no administradas.

Una complicación del nivel del proyecto es que no es posible compilar archivos Lenguaje de marcado de aplicaciones extensible (XAML) 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 DLL como una referencia.

  • Cree un ejecutable C# ejecutable para el contenido WPF y haga que use como referencia una 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.

Nota

Si no ha utilizado C++/CLI antes, quizá observe algunas palabras clave "nuevas", tales como gcnew y nullptr en los ejemplos de código de interoperación. Estas palabras clave reemplazan sintaxis anterior 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 Language Features for Targeting the CLR y Hello, C++/CLI.

Cómo WPF utiliza Hwnd

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 Interoperabilidad de WPF: Información general sobre "Espacio aéreo" y regiones de ventana.

Todos los elementos 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ú 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 WPFWindow.

Un concepto relacionado con HWND es la transparencia dentro de y entre HWND. Esto también se explica en el tema Interoperabilidad de WPF: Información general sobre "Espacio aéreo" y regiones de ventana.

Hospedar contenido WPF en una ventana Microsoft Win32

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 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 el marcador 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 un apartamento de un único subproceso (Single Threaded Apartment, 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 asociando un controlador a uno o más de los eventos del objeto de contenido WPF.

  8. Realice las comunicaciones con el objeto de contenido WPF utilizando la referencia que almacenó en el campo estático para establecer propiedades, llamar a métodos, etc.

Nota

Puede hacer la definición de la clase de contenido WPF para el paso uno de XAML, en todo o en parte, usando la clase parcial predeterminada de la clase de contenido, si produce un ensamblado separado y, a continuación, hace referencia a él. Aunque normalmente incluirá un objeto Application como parte de la compilación del XAML en un ensamblado, finalmente no utilizará ese objeto Application como parte de la interoperación; solamente utilizará una o más clases raíz para los archivos XAML a los que haga referencia la aplicación y hará 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 Windows Presentation Foundation en una aplicación Win32.

Hospedar una ventana Microsoft Win32 en WPF

La clave para hospedar una ventana Win32 ventana 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 simple de Win32 en una aplicación de Windows Presentation Foundation.

Hwnds dentro de 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 interoperación.) HwndHost abstrae la naturales Win32 subyacente del contenido hospedad de modo que el resto de WPF considera que el control hospedad es otro objeto de tipo control, que debe representarse y procesar entradas. HwndHost se comporta generalmente como cualquier otro objeto WPFFrameworkElement, 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.

Diferencias notables en el comportamiento de salida

  • 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.

Diferencias notables en el comportamiento de entrada

  • 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.

Tabulación, teclas de acceso y aceleradores

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.

Vea también

Conceptos

Tutorial: Hospedar un control simple de Win32 en una aplicación de Windows Presentation Foundation

Tutorial: Hospedar contenido de Windows Presentation Foundation en una aplicación Win32

Referencia

HwndHost

HwndSource

System.Windows.Interop