4

我想可以肯定地说 WPF 将其内容呈现为窗口背景。没有传统 HWND 意义上的子窗口。因此,当一个人在 WPF 应用程序中引入基于 HWND 的东西时,比如 WebBrowser,事情开始出现问题,因为它的视觉外观方面。

考虑一个窗口,它有一个带有两个孩子、WebBrowser 和其他东西(例如 TextBox)的 Grid。如果 WebBrowser 是一个红色圆圈,则 TextBox 将呈现在它上面。对于 WebBrowser,在任何地方都找不到 TextBox。这是因为 TextBox 被渲染为主窗口的背景,而 WebBrowser 实际上是主窗口的 HWND 子窗口遮蔽了背景。

所以一切都(不)好。如何实现期望的行为?我想在 WebBrowser 上呈现 TextBox。有没有人遇到过这个问题?

我正在考虑拥有第二个透明的顶级无边框 WPF 窗口,将其重新设置为父窗口,以便主窗口拥有它并执行一些其他技巧来实现它。

在我深入研究之前,我想知道是否有人有一个明显或更简单的解决方案?


由 Meleak 更新

我将此赏金提供给任何可以发布 Ray Burns Answer 实现的人AirRepair。我尝试过自己但徒劳无功

4

4 回答 4

2

阅读WPF 互操作:“空域”和窗口区域概述

于 2010-03-10T16:25:01.440 回答
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 可与WebBrowserWindowsFormsHost或任何其他HwndHost. 装饰控件覆盖的区域显示在 Win32 内容中,它接受焦点和鼠标事件。对于非矩形装饰控件,可以通过RepairAreaand/orOpacityMask属性指定要显示的区域。

这个怎么运作

AirRepair 通过以下方式解决空域问题:

  1. HwndHost在给定的使用下创建一个子 hWndHwndSource
  2. 将其 hRgn 设置为适当的区域
  3. 将其设置为RootVisual装饰控件的一个BorderBackgroundVisualBrush
  4. WM_MOUSEMOVE将子 hWnd 收到的等转发到 WPF 主窗口

这样做的结果是 WPF 继续绘制Win32 内容后面的内容,但 AirRepair 的子窗口在单独的 Win32 控件中重新绘制 Win32 内容前面的相同内容。

一些重要的实现细节

获取父 hWnd

最初设置时Win32Host,它可能有也可能没有 hWnd。PropertyChangedCallback应该使用PresentationSource.AddSourceChangedHandler/来检测可能的hWnd 更改,然后在回调PresentationSource.RemoveSourceChangedHandler中更新自己的 hWnd 指针,以便有机会完​​成对事件的处理。Dispatcher.BeginInvokeHwndHostSourceChanged

创建子 hWnd

可以使用该类在托管代码中创建、创建子 hWnd 并将其挂接到托管代码中HwndSource。当 Win32Host 的父 hWnd 不再可用时,请务必处理它。

定位子 hWnd

子 hWnd 的窗口位置(相对于其父级)可以计算为:

 var compositionTarget = PresentationSource.FromVisual(this).CompositionTarget;
 var position = compositionTarget.TransformToDevice(
                  this.TransformToVisual(Win32Host));

UIELement.LayoutUpdated应该使用该事件来保持最新状态。

计算 hRgn 和不透明度

可选:如果仅支持矩形修复区域,则省略

RepairAreaorOpacityMask被设置并且子 hWnd 存在时,使用 aRenderTargetBitmap绘制RepairArea使用OpacityMask然后从它创建 hRgn。如果RepairArea为空,则使用矩形。如果OpacityMask为空,则使用黑色。通过将RenderTargetBitmapAirRepair 装饰器的坐标转换为设备坐标来设置大小。请注意,这不能正确处理变量OpacityMask,例如动画画笔或正在更改VisualBrush的变量。Visual

在子 hWnd 上绘制内容

使用VisualBrushVisual是 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);
};
于 2010-03-11T00:07:25.070 回答
1

如果您正在寻找快速简便的解决方案,只需使用弹出控件即可,这是一个示例

http://karlshifflett.wordpress.com/2009/06/13/wpf-float-buttons-over-web-browser-control/

于 2010-09-10T17:49:00.770 回答
0

如果您专门针对 Web 浏览器,则已对此进行了几次尝试。 Chris Cavanagh基于 Chrome 创建了一个出色的解决方案。

于 2010-03-10T21:57:32.093 回答