Tutorial: Hospedar objetos visuales en una aplicación Win32

Windows Presentation Foundation (WPF) proporciona un entorno enriquecido para crear aplicaciones. Sin embargo, cuando ya se tiene una inversión sustancial en código de Win32, puede ser más eficaz agregar la funcionalidad de WPF a la aplicación en lugar de volver a escribir el código. Para proporcionar la compatibilidad con los subsistemas de gráficos de Win32 y WPF que se utilizan de manera simultánea en una aplicación, WPF proporciona un mecanismo para hospedar objetos en una ventana de Win32.

Este tutorial describe cómo escribir una aplicación de ejemplo, Hit Test with Win32 Interoperation Sample, que hospeda objetos visuales de WPF en una ventana Win32.

Este tema contiene las secciones siguientes.

  • Requisitos
  • Crear la ventana host de Win32
  • Agregar objetos visuales a la ventana host de Win32
  • Implementar el filtro de mensajes de Win32
  • Procesar mensajes de Win32
  • Temas relacionados

Requisitos

En este tutorial se da por hecho que está familiarizado con la programación básica en WPF y Win32. Para obtener una introducción básica a la programación en WPF, vea Tutorial: Introducción a WPF. Para obtener una introducción a la programación en Win32, vea cualquiera de los numerosos libros sobre el tema, en particular Programming Windows escrito por Charles Petzold.

NotaNota

En este tutorial se incluyen varios ejemplos de código del ejemplo asociado.Sin embargo, para facilitar la legibilidad, no se incluye el código de ejemplo completo.Para obtener el ejemplo de código completo, vea Hit Test with Win32 Interoperation Sample.

Crear la ventana host de Win32

La clave para hospedar objetos de WPF en una ventana de Win32 es la clase HwndSource. Esta clase ajusta los objetos de WPF en una ventana de Win32, con lo que permite incorporarlos a la user interface (UI) como una ventana secundaria.

En el ejemplo siguiente se muestra el código utilizado para crear el objeto HwndSource como la ventana contenedora de Win32 para los objetos visuales. Para establecer el estilo, la posición y otros parámetros de la ventana de Win32 ventana, utilice el objeto HwndSourceParameters.

' Constant values from the "winuser.h" header file.
Friend Const WS_CHILD As Integer = &H40000000, WS_VISIBLE As Integer = &H10000000

Friend Shared Sub CreateHostHwnd(ByVal parentHwnd As IntPtr)
    ' Set up the parameters for the host hwnd.
    Dim parameters As New HwndSourceParameters("Visual Hit Test", _width, _height)
    parameters.WindowStyle = WS_VISIBLE Or WS_CHILD
    parameters.SetPosition(0, 24)
    parameters.ParentWindow = parentHwnd
    parameters.HwndSourceHook = New HwndSourceHook(AddressOf ApplicationMessageFilter)

    ' Create the host hwnd for the visuals.
    myHwndSource = New HwndSource(parameters)

    ' Set the hwnd background color to the form's background color.
    myHwndSource.CompositionTarget.BackgroundColor = System.Windows.Media.Brushes.OldLace.Color
End Sub
// Constant values from the "winuser.h" header file.
internal const int WS_CHILD = 0x40000000,
                   WS_VISIBLE = 0x10000000;

internal static void CreateHostHwnd(IntPtr parentHwnd)
{
    // Set up the parameters for the host hwnd.
    HwndSourceParameters parameters = new HwndSourceParameters("Visual Hit Test", _width, _height);
    parameters.WindowStyle = WS_VISIBLE | WS_CHILD;
    parameters.SetPosition(0, 24);
    parameters.ParentWindow = parentHwnd;
    parameters.HwndSourceHook = new HwndSourceHook(ApplicationMessageFilter);

    // Create the host hwnd for the visuals.
    myHwndSource = new HwndSource(parameters);

    // Set the hwnd background color to the form's background color.
    myHwndSource.CompositionTarget.BackgroundColor = System.Windows.Media.Brushes.OldLace.Color;
}
NotaNota

El valor de la propiedad ExtendedWindowStyle no se puede establecer en WS_EX_TRANSPARENT.Esto significa que la ventana host de Win32 no puede ser transparente.Por esta razón, el color de fondo de la ventana host de Win32 está establecido en el mismo color de fondo que su ventana primaria.

Agregar objetos visuales a la ventana host de Win32

Una vez creada la ventana host de Win32 contenedora para los objetos visuales, puede agregarle los objetos visuales. Es conveniente asegurarse de que ninguna transformación de los objetos visuales, como las animaciones, se extienda más allá de los límites del rectángulo delimitador de la ventana host de Win32.

En el ejemplo siguiente se muestra el código utilizado para crear el objeto HwndSource y agregarle objetos visuales.

NotaNota

La propiedad RootVisual del objeto HwndSource se establece en el primer objeto visual agregado a la ventana host de Win32.El objeto visual raíz define el nodo de nivel superior del árbol de objetos visuales.Todos los objetos visuales subsiguientes se agregan a la ventana host de Win32 como objetos secundarios.

Public Shared Sub CreateShape(ByVal parentHwnd As IntPtr)
    ' Create an instance of the shape.
    Dim myShape As New MyShape()

    ' Determine whether the host container window has been created.
    If myHwndSource Is Nothing Then
        ' Create the host container window for the visual objects.
        CreateHostHwnd(parentHwnd)

        ' Associate the shape with the host container window.
        myHwndSource.RootVisual = myShape
    Else
        ' Assign the shape as a child of the root visual.
        CType(myHwndSource.RootVisual, ContainerVisual).Children.Add(myShape)
    End If
End Sub
public static void CreateShape(IntPtr parentHwnd)
{
    // Create an instance of the shape.
    MyShape myShape = new MyShape();

    // Determine whether the host container window has been created.
    if (myHwndSource == null)
    {
        // Create the host container window for the visual objects.
        CreateHostHwnd(parentHwnd);

        // Associate the shape with the host container window.
        myHwndSource.RootVisual = myShape;
    }
    else
    {
        // Assign the shape as a child of the root visual.
        ((ContainerVisual)myHwndSource.RootVisual).Children.Add(myShape);
    }
}

Implementar el filtro de mensajes de Win32

La ventana host de Win32 para los objetos visuales requiere un procedimiento de filtro de mensaje de ventana que administre los mensajes que se envían a la ventana desde la cola de la aplicación. El procedimiento de ventana recibe los mensajes del sistema de Win32. Éstos pueden ser mensajes de entrada o de administración de ventanas. Si lo desea, puede administrar un mensaje en el procedimiento de ventana o pasárselo al sistema para que se efectúe el procesamiento predeterminado.

El objeto HwndSource que definió como elemento primario para los objetos visuales debe hacer referencia al procedimiento de filtro de mensajes de ventana que proporcione. Al crear el objeto HwndSource, establezca la HwndSourceHook de tal forma que haga referencia al procedimiento de ventana.

parameters.HwndSourceHook = New HwndSourceHook(AddressOf ApplicationMessageFilter)
parameters.HwndSourceHook = new HwndSourceHook(ApplicationMessageFilter);

En el ejemplo siguiente se muestra el código para administrar los mensajes que se emiten al soltar el botón primario y secundario del mouse. El valor del parámetro lParam contiene el valor de las coordenadas de la posición donde se hace clic con el mouse.

' Constant values from the "winuser.h" header file.
Friend Const WM_LBUTTONUP As Integer = &H202, WM_RBUTTONUP As Integer = &H205

Friend Shared Function ApplicationMessageFilter(ByVal hwnd As IntPtr, ByVal message As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
    ' Handle messages passed to the visual.
    Select Case message
        ' Handle the left and right mouse button up messages.
        Case WM_LBUTTONUP, WM_RBUTTONUP
            Dim pt As New System.Windows.Point()
            pt.X = CUInt(lParam) And CUInt(&HFFFF) ' LOWORD = x
            pt.Y = CUInt(lParam) >> 16 ' HIWORD = y
            MyShape.OnHitTest(pt, message)
    End Select

    Return IntPtr.Zero
End Function
// Constant values from the "winuser.h" header file.
internal const int WM_LBUTTONUP = 0x0202,
                   WM_RBUTTONUP = 0x0205;

internal static IntPtr ApplicationMessageFilter(
    IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    // Handle messages passed to the visual.
    switch (message)
    {
        // Handle the left and right mouse button up messages.
        case WM_LBUTTONUP:
        case WM_RBUTTONUP:
            System.Windows.Point pt = new System.Windows.Point();
            pt.X = (uint)lParam & (uint)0x0000ffff;  // LOWORD = x
            pt.Y = (uint)lParam >> 16;               // HIWORD = y
            MyShape.OnHitTest(pt, message);
            break;
    }

    return IntPtr.Zero;
}

Procesar mensajes de Win32

En el código del ejemplo siguiente se muestra cómo se efectúa una prueba de posicionamiento con respecto a la jerarquía de objetos visuales contenida en la ventana host de Win32. Puede identificar si un punto está dentro de la geometría de un objeto visual utilizando el método HitTest para especificar el objeto visual raíz y el valor de las coordenadas con las que se comparará la prueba de posicionamiento. En este caso, el objeto visual raíz es el valor de la propiedad RootVisual del objeto HwndSource.

        ' Constant values from the "winuser.h" header file.
        Public Const WM_LBUTTONUP As Integer = &H0202, WM_RBUTTONUP As Integer = &H0205

        ' Respond to WM_LBUTTONUP or WM_RBUTTONUP messages by determining which visual object was clicked.
        Public Shared Sub OnHitTest(ByVal pt As System.Windows.Point, ByVal msg As Integer)
            ' Clear the contents of the list used for hit test results.
            hitResultsList.Clear()

            ' Determine whether to change the color of the circle or to delete the shape.
            If msg = WM_LBUTTONUP Then
                MyWindow.changeColor = True
            End If
            If msg = WM_RBUTTONUP Then
                MyWindow.changeColor = False
            End If

            ' Set up a callback to receive the hit test results enumeration.
            VisualTreeHelper.HitTest(MyWindow.myHwndSource.RootVisual, Nothing, New HitTestResultCallback(AddressOf CircleHitTestResult), New PointHitTestParameters(pt))

            ' Perform actions on the hit test results list.
            If hitResultsList.Count > 0 Then
                ProcessHitTestResultsList()
            End If
        End Sub
// Constant values from the "winuser.h" header file.
public const int WM_LBUTTONUP = 0x0202,
                 WM_RBUTTONUP = 0x0205;

// Respond to WM_LBUTTONUP or WM_RBUTTONUP messages by determining which visual object was clicked.
public static void OnHitTest(System.Windows.Point pt, int msg)
{
    // Clear the contents of the list used for hit test results.
    hitResultsList.Clear();

    // Determine whether to change the color of the circle or to delete the shape.
    if (msg == WM_LBUTTONUP)
    {
        MyWindow.changeColor = true;
    }
    if (msg == WM_RBUTTONUP)
    {
        MyWindow.changeColor = false;
    }

    // Set up a callback to receive the hit test results enumeration.
    VisualTreeHelper.HitTest(MyWindow.myHwndSource.RootVisual,
                             null,
                             new HitTestResultCallback(CircleHitTestResult),
                             new PointHitTestParameters(pt));

    // Perform actions on the hit test results list.
    if (hitResultsList.Count > 0)
    {
        ProcessHitTestResultsList();
    }
}

Para obtener más información sobre las pruebas de posicionamiento con respecto a objetos visuales, vea Realizar pruebas de posicionamiento en la capa visual.

Vea también

Referencia

HwndSource

Conceptos

Realizar pruebas de posicionamiento en la capa visual

Otros recursos

Hit Test with Win32 Interoperation Sample