Windows Presentation Foundation (WPF)提供了用于创建应用程序的丰富环境。 但是,当你对 Win32 代码进行大量投资时,向应用程序添加 WPF 功能而不是重写代码可能更有效。 为了为应用程序中并发使用的 Win32 和 WPF 图形子系统提供支持,WPF 提供了用于在 Win32 窗口中托管对象的机制。
本教程介绍如何编写示例应用程序使用 Win32 互操作进行命中测试示例,它将在 Win32 窗口中承载 WPF 视觉对象。
要求
本教程假定基本熟悉 WPF 和 Win32 编程。 有关 WPF 编程的基本简介,请参阅 演练:我的第一个 WPF 桌面应用程序。 有关 Win32 编程的简介,请参阅关于该主题的众多书籍,尤其是查尔斯·佩茨罗德所著的《编程 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 对象并将视觉对象添加到其中的代码。
注释
RootVisual 对象的 HwndSource 属性设置为添加到宿主 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 方法指定进行命中测试的根视觉对象和坐标值,从而确定一个点是否位于视觉对象的几何内。 在这种情况下,根视觉对象是 RootVisual 对象的 HwndSource 属性的值。
// 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
有关针对视觉对象的命中测试的详细信息,请参阅视觉层中的