教程:在 Win32 应用程序中承载 Visual 对象

Windows Presentation Foundation (WPF) 提供用于创建应用程序的丰富环境。 但是,如果您在 Win32 代码上有大量的投资,则将 WPF 功能添加到应用程序而不是重写代码的做法或许会更有效。 为了为应用程序中并发使用的 Win32 和 WPF 图形子系统提供支持,WPF 提供了一种承载 Win32 窗口中的对象的机制。

本教程介绍如何编写示例应用程序 Hit Test with Win32 Interoperation Sample(Win32 互操作命中测试示例),该应用程序在 Win32 窗口中承载 WPF 可使对象。

本主题包括下列各节。

  • 要求
  • 创建宿主 Win32 窗口
  • 将 Visual 对象添加到宿主 Win32 窗口
  • 实现 Win32 消息筛选器
  • 处理 Win32 消息
  • 相关主题

要求

本教程假定您基本熟悉 WPF 和 Win32 编程。 有关 WPF 编程的基本介绍,请参见 演练:开始使用 WPF。 有关对 Win32 编程的介绍,请参见众多关于此主题书籍中的任何一本,特别推荐由 Charles Petzold 编写的 Programming Windows(《Windows 编程》)。

注意注意

本教程包含关联示例中的一些代码示例。但是,为了易读起见,本教程并不包含完整的代码示例。有关完整代码示例,请参见 Hit Test with Win32 Interoperation Sample(Win32 互操作命中测试示例)。

创建宿主 Win32 窗口

在 Win32 窗口中承载 WPF 对象的关键是 HwndSource 类。 此类在 Win32 窗口中包装 WPF 对象,允许将它们作为子级窗口合并到user interface (UI) 中。

下面的示例演示将 HwndSource 对象作为 Visual 对象的 Win32 容器窗口进行创建的代码。 若要为 Win32 窗口设置窗口的样式、位置和其他参数,请使用 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;
}
注意注意

不能将 ExtendedWindowStyle 属性的值设置为 WS_EX_TRANSPARENT。这意味着宿主 Win32 窗口不能是透明的。因此,宿主 Win32 窗口的背景色将设置为与其父窗口相同的背景色。

将 Visual 对象添加到宿主 Win32 窗口

为 Visual 对象创建宿主 Win32 容器窗口后,可向其添加 Visual 对象。 如果需要确保如动画等的 Visual 对象的任何转换,扩展时请勿超出宿主 Win32 窗口的边框。

下面的示例演示创建 HwndSource 对象并向其中添加 Visual 对象的代码。

注意注意

HwndSource 对象的 RootVisual 属性设置为添加到宿主 Win32 窗口的第一个 Visual 对象。根 Visual 对象定义 Visual 对象树最顶层的节点。任何添加到宿主 Win32 窗口的后续 Visual 对象都作为子对象进行添加。

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);
    }
}

实现 Win32 消息筛选器

Visual 对象的宿主 Win32 窗口需要窗口消息筛选器过程来处理从应用程序队列发送到该窗口的消息。 窗口过程接收来自 Win32 系统的消息。 这些消息可以为输入消息或窗口管理消息。 可以选择在窗口过程中处理消息或将消息传递到系统进行默认处理。

定义为 Visual 对象的父级的 HwndSource 对象必须引用提供的窗口消息筛选器过程。 创建 HwndSource 对象时,请将 HwndSourceHook 属性设置为引用窗口过程。

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

下面的示例演示处理释放鼠标左键和右键消息的代码。 鼠标命中位置的坐标值包含在 lParam 参数的值中。

' 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;
}

处理 Win32 消息

下面示例中的代码演示如何对包含在宿主 Win32 窗口中的 Visual 对象的层次结构执行命中测试。 可以确定一个点是否处于 Visual 对象的几何图形之内,其方法是使用 HitTest 方法指定进行命中测试的根 Visual 对象和坐标值。 在本例中,根 Visual 对象是 HwndSource 对象的 RootVisual 属性的值。

        ' 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();
    }
}

有关对 Visual 对象进行命中测试的更多信息,请参见可视化层中的命中测试

请参见

参考

HwndSource

概念

可视化层中的命中测试

其他资源

Hit Test with Win32 Interoperation Sample