教程:在 Win32 应用程序中承载视觉对象
Windows Presentation Foundation (WPF) 提供了用于创建应用程序的丰富环境。 但是,当你对 Win32 代码有大量投入时,向应用程序添加 WPF 功能(而不是重写代码)可能更高效。 为了对应用程序中并发使用的 Win32 和 WPF 图形子系统提供支持,WPF 提供了一种在 Win32 窗口中承载对象的机制。
本教程介绍如何编写示例应用程序使用 Win32 互操作进行命中测试示例,它将在 Win32 窗口中承载 WPF 视觉对象。
要求
本教程假定你已基本熟悉 WPF 和 Win32 编程。 有关 WPF 编程的基本说明,请参阅演练:我的第一个 WPF 桌面应用程序。 有关 Win32 编程的说明,可参考有关该主题的众多书籍,尤其是 Charles Petzold 所著的《Windows 编程》。
注意
本教程包括来自相关示例的一些代码示例。 但是,出于可读性考虑,不包括完整的示例代码。 有关完整示例代码,请参阅使用 Win32 互操作进行命中测试示例。
创建宿主 Win32 窗口
在 Win32 窗口中承载 WPF 对象的关键是 HwndSource 类。 此类将 WPF 对象包装在 Win32 窗口中,从而使它们可以作为子窗口并入用户界面 (UI) 中。
以下示例演示用于创建 HwndSource 对象作为视觉对象的 Win32 容器窗口的代码。 若要为 Win32 窗口设置窗口样式、位置和其他参数,请使用 HwndSourceParameters 对象。
// 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;
}
' 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
注意
ExtendedWindowStyle 属性的值不能设置为 WS_EX_TRANSPARENT。 这意味着,宿主 Win32 窗口不能为透明。 出于此原因,该宿主 Win32 窗口的背景色设置为与其父窗口相同的背景色。
将视觉对象添加到宿主 Win32 窗口
为视觉对象创建宿主 Win32 容器窗口后,可向其添加视觉对象。 需要确保诸如动画等视觉对象的任何转换扩展时不超出宿主 Win32 窗口的边框。
下面的示例演示用于创建 HwndSource 对象并向其中添加视觉对象的代码。
注意
HwndSource 对象的 RootVisual 属性设置为添加到宿主 Win32 窗口的第一个视觉对象。 根视觉对象定义视觉对象树最顶层的节点。 任何添加到宿主 Win32 窗口的后续视觉对象都将作为子对象进行添加。
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);
}
}
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
实现 Win32 消息筛选器
视觉对象的宿主 Win32 窗口需要窗口消息筛选器过程来处理从应用程序队列发送到该窗口的消息。 窗口过程接收来自 Win32 系统的消息。 这些消息可以是输入消息,也可以是窗口管理消息。 可以选择在窗口过程中处理一条消息,或将消息传递到系统以供默认处理。
定义为视觉对象的父级的 HwndSource 对象必须引用所提供的窗口消息筛选器过程。 创建 HwndSource 对象时,将 HwndSourceHook 属性设置为引用该窗口过程。
parameters.HwndSourceHook = new HwndSourceHook(ApplicationMessageFilter);
parameters.HwndSourceHook = New HwndSourceHook(AddressOf ApplicationMessageFilter)
下面的示例演示用于处理释放鼠标左键和右键消息的代码。 鼠标点击位置的坐标值包含在 lParam
参数的值中。
// 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;
}
' 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
处理 Win32 消息
下面示例中的代码演示如何对包含在宿主 Win32 窗口中的视觉对象的层次结构执行命中测试。 可以通过使用 HitTest 方法指定进行命中测试的根视觉对象和坐标值,从而确定一个点是否位于视觉对象的几何内。 在本例中,根视觉对象是 HwndSource 对象的 RootVisual 属性的值。
// 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();
}
}
' 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
有关对视觉对象进行命中测试的详细信息,请参阅可视化层中的命中测试。