建议的解决方案
我建议使用此签名的简单“AirRepair”类:
public class AirRepair : Decorator
{
public HwndHost Win32Host ... // DP bound to HwndHost
public Geometry RepairArea ... // Default is entire decorated control,
// or use OpacityMask
}
这样使用:
<Grid>
<WebBrowser x:Name="browser" ... />
<AirRepair Win32Host="{Binding ElementName=browser}"
Margin="10" HorizontalAlignment="Left" ...>
<TextBox ... />
</AirRepair>
</Grid>
AirRepair 可与WebBrowser
、WindowsFormsHost
或任何其他HwndHost
. 装饰控件覆盖的区域显示在 Win32 内容中,它接受焦点和鼠标事件。对于非矩形装饰控件,可以通过RepairArea
and/orOpacityMask
属性指定要显示的区域。
这个怎么运作
AirRepair 通过以下方式解决空域问题:
HwndHost
在给定的使用下创建一个子 hWndHwndSource
- 将其 hRgn 设置为适当的区域
- 将其设置为
RootVisual
装饰控件的一个Border
Background
VisualBrush
WM_MOUSEMOVE
将子 hWnd 收到的等转发到 WPF 主窗口
这样做的结果是 WPF 继续绘制Win32 内容后面的内容,但 AirRepair 的子窗口在单独的 Win32 控件中重新绘制 Win32 内容前面的相同内容。
一些重要的实现细节
获取父 hWnd
最初设置时Win32Host
,它可能有也可能没有 hWnd。PropertyChangedCallback
应该使用PresentationSource.AddSourceChangedHandler
/来检测可能的hWnd 更改,然后在回调PresentationSource.RemoveSourceChangedHandler
中更新自己的 hWnd 指针,以便有机会完成对事件的处理。Dispatcher.BeginInvoke
HwndHost
SourceChanged
创建子 hWnd
可以使用该类在托管代码中创建、创建子 hWnd 并将其挂接到托管代码中HwndSource
。当 Win32Host 的父 hWnd 不再可用时,请务必处理它。
定位子 hWnd
子 hWnd 的窗口位置(相对于其父级)可以计算为:
var compositionTarget = PresentationSource.FromVisual(this).CompositionTarget;
var position = compositionTarget.TransformToDevice(
this.TransformToVisual(Win32Host));
UIELement.LayoutUpdated
应该使用该事件来保持最新状态。
计算 hRgn 和不透明度
可选:如果仅支持矩形修复区域,则省略
当RepairArea
orOpacityMask
被设置并且子 hWnd 存在时,使用 aRenderTargetBitmap
绘制RepairArea
使用OpacityMask
然后从它创建 hRgn。如果RepairArea
为空,则使用矩形。如果OpacityMask
为空,则使用黑色。通过将RenderTargetBitmap
AirRepair 装饰器的坐标转换为设备坐标来设置大小。请注意,这不能正确处理变量OpacityMask
,例如动画画笔或正在更改VisualBrush
的变量。Visual
在子 hWnd 上绘制内容
使用VisualBrush
其Visual
是 AirRepair 装饰器,而不是装饰控件。这允许在不更改内容的情况下替换装饰控件。
childHwndSource.RootVisual =
new Border
{
Background = new VisualBrush
{
Visual = this,
ViewBoxUnits = BrushMappingMode.Absolute,
ViewPortUnits = BrushMappingMode.Absolute,
}
};
转发鼠标消息
添加一个钩子 using HwndSource.AddHook
,然后使用 Win32PostMessage
到容器中:
childHwndSource.AddHook((hwnd, msg, wParam, lParam, handled) =>
{
// Check if message needs forwarding and update parameters if necessary
switch(msg)
{
default:
return; // Not recognized - do not forward
case WM_MOUSEMOVE:
...
}
var target = PresentationSource.FromVisual(this).CompositionTarget as HwndTarget;
if(target!=null)
Win32Methods.PostMessage(target.Handle, msg, wParam, lParam);
};