我建议使用一个简单的“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
和/或 OpacityMask
属性指定要显示的区域。
工作原理
AirRepair 通过以下方式解决空间问题:
HwndHost
下创建一个子 hWnd,使用 HwndSource
RootVisual
设置为一个 Border
,其 Background
是装饰控件的 VisualBrush
WM_MOUSEMOVE
等转发到主 WPF 窗口Win32Host
时,它可能有也可能没有hWnd。 PropertyChangedCallback
应使用PresentationSource.AddSourceChangedHandler
/ PresentationSource.RemoveSourceChangedHandler
检测可能的hWnd更改,然后在Dispatcher.BeginInvoke
回调中更新自己的hWnd指针,以便HwndHost
有机会完成处理SourceChanged
事件。
创建子hWnd
可以使用HwndSource
类在托管代码中创建、父级和挂钩子。确保在Win32Host的父hWnd不再可用时将其处理掉。
定位子hWnd
子hWnd的窗口位置(相对于其父级)可以计算为: var compositionTarget = PresentationSource.FromVisual(this).CompositionTarget;
var position = compositionTarget.TransformToDevice(
this.TransformToVisual(Win32Host));
应使用UIELement.LayoutUpdated
事件来保持此内容的最新状态。
计算hRgn和不透明度
可选:如果仅支持矩形修复区域,则可省略
当设置了RepairArea
或OpacityMask
,并且子窗口句柄hWnd
存在时,使用RenderTargetBitmap
使用OpacityMask
绘制RepairArea
,然后从中创建。如果RepairArea
为空,则使用矩形。如果OpacityMask
为空,则使用黑色。 RenderTargetBitmap
大小通过将AirRepair修饰符的坐标转换为设备坐标来设置。请注意,这不能正确处理变量OpacityMask
,例如动画刷或VisualBrush
其Visual
正在更改。
在子窗口句柄上绘制内容
使用一个VisualBrush
,其Visual
是AirRepair修饰符,而不是已装饰控件。这允许替换已装饰控件而不更改内容。
childHwndSource.RootVisual =
new Border
{
Background = new VisualBrush
{
Visual = this,
ViewBoxUnits = BrushMappingMode.Absolute,
ViewPortUnits = BrushMappingMode.Absolute,
}
};
转发鼠标消息
使用HwndSource.AddHook
添加钩子,然后使用Win32 PostMessage
将消息发送到容器:
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);
};
http://karlshifflett.wordpress.com/2009/06/13/wpf-float-buttons-over-web-browser-control/