我认为可以肯定地说WPF将其内容呈现为窗口背景。传统的HWND意义上没有儿童窗户。因此,当一个人在WPF应用程序中引入一些基于HWND的东西时,就像WebBrowser一样,事情开始出现视觉外观方面的错误。
考虑一个具有两个子网格的窗口,WebBrowser和其他东西,例如:文本框。如果WebBrowser是一个红色圆圈,TextBox将在它上面呈现。对于WebBrowser,在任何地方都找不到TextBox。这是因为TextBox被渲染为主窗口的背景,而WebBrowser实际上是遮挡背景的主窗口的HWND子节点。
所以一切都好(不)。如何实现理想的行为?我想在WebBrowser上呈现TextBox。有人遇到过这个问题吗?
我正在考虑使用第二个透明的顶级无边界WPF窗口,重新设置它以使主窗口拥有它并做一些其他技巧来实现它。
在我深入研究之前,我想知道是否有人有明显或更简单的解决方案?
我正在向任何可以发布Ray Burns答案AirRepair
的实施方案的人提供此奖励。我试过自己,但徒劳无功
答案 0 :(得分:2)
答案 1 :(得分:1)
建议的解决方案
我建议使用这个签名的简单“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
HwndSource
下创建子项目
RootVisual
设为Border
,其Background
是装饰控件的VisualBrush
WM_MOUSEMOVE
转发到主WPF窗口结果是WPF继续在后面绘制 Win32内容,但AirRepair的子窗口在单独的Win32内容的前面中重绘相同的内容 Win32控件。
一些重要的实施细节
获取父级hWnd
最初设置Win32Host
时,它可能有也可能没有hWnd。 PropertyChangedCallback
应使用PresentationSource.AddSourceChangedHandler
/ PresentationSource.RemoveSourceChangedHandler
来检测可能的hWnd更改,然后在Dispatcher.BeginInvoke
回调中更新自己的hWnd指针,以便HwndHost
有机会完成处理SourceChanged
事件。
创建子hWnd
可以使用HwndSource
类在托管代码中创建子级hWnd,并将其挂起。当Win32Host的父hWnd不再可用时,请务必将其丢弃。
定位孩子hWnd
子hWnd的窗口位置(相对于其父窗口)可以计算为:
var compositionTarget = PresentationSource.FromVisual(this).CompositionTarget;
var position = compositionTarget.TransformToDevice(
this.TransformToVisual(Win32Host));
应该使用UIELement.LayoutUpdated
事件来保持最新状态。
计算hRgn和不透明度
可选:如果仅支持矩形修复区域,则省略
设置RepairArea
或OpacityMask
并且子hWnd存在后,使用RenderTargetBitmap
使用RepairArea
绘制OpacityMask
,然后创建hRgn从中。如果RepairArea
为null,请使用矩形。如果OpacityMask
为空,请使用黑色。通过将AirRepair装饰器的坐标转换为设备坐标来设置RenderTargetBitmap
大小。请注意,这不能正确处理变量OpacityMask
,例如动画画笔或VisualBrush
正在更改的Visual
。
在子hWnd上绘制内容
使用其VisualBrush
是AirRepair装饰器的Visual
,而不是装饰控件。这允许在不改变内容的情况下替换修饰的控件。
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);
};
答案 2 :(得分:1)
如果您正在寻找快速简便的解决方案,只需使用弹出控件,这是一个示例
http://karlshifflett.wordpress.com/2009/06/13/wpf-float-buttons-over-web-browser-control/
答案 3 :(得分:0)
如果您专门针对网络浏览器,那么就会有一些尝试。 Chris Cavanagh基于Chrome创建了一个出色的解决方案。