WPF渲染问题与HWND儿童的方式

时间:2010-03-10 16:18:53

标签: wpf rendering webbrowser-control hwnd

我认为可以肯定地说WPF将其内容呈现为窗口背景。传统的HWND意义上没有儿童窗户。因此,当一个人在WPF应用程序中引入一些基于HWND的东西时,就像WebBrowser一样,事情开始出现视觉外观方面的错误。

考虑一个具有两个子网格的窗口,WebBrowser和其他东西,例如:文本框。如果WebBrowser是一个红色圆圈,TextBox将在它上面呈现。对于WebBrowser,在任何地方都找不到TextBox。这是因为TextBox被渲染为主窗口的背景,而WebBrowser实际上是遮挡背景的主窗口的HWND子节点。

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

我正在考虑使用第二个透明的顶级无边界WPF窗口,重新设置它以使主窗口拥有它并做一些其他技巧来实现它。

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


Meleak更新

我正在向任何可以发布Ray Burns答案AirRepair的实施方案的人提供此奖励。我试过自己,但徒劳无功

4 个答案:

答案 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可与WebBrowserWindowsFormsHost或任何其他HwndHost一起使用。装饰控件覆盖的区域显示在Win32内容中,它接受焦点和鼠标事件。对于非矩形装饰控件,可以通过RepairArea和/或OpacityMask属性指定要显示的区域。

工作原理

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

  1. 使用HwndHost
  2. 在给定HwndSource下创建子项目
  3. 将其hRgn设置为适当的区域
  4. 将其RootVisual设为Border,其Background是装饰控件的VisualBrush
  5. 将孩子收到的WM_MOUSEMOVE转发到主WPF窗口
  6. 结果是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和不透明度

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

    设置RepairAreaOpacityMask并且子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创建了一个出色的解决方案。