Проблема рендеринга WPF с HWND в пути - PullRequest
4 голосов
/ 10 марта 2010

Полагаю, можно с уверенностью сказать, что WPF отображает свое содержимое в качестве фона окна. Там нет дочерних окон в традиционном смысле HWND. Таким образом, когда кто-то вводит что-то на основе HWND в приложении WPF, например, WebBrowser, все начинает идти не так, как с точки зрения внешнего вида.

Рассмотрим окно с сеткой с двумя дочерними элементами, веб-браузером и чем-то еще, например, Текстовое окно. Если бы вместо этого WebBrowser был красным кружком, TextBox отображался бы поверх него. В случае WebBrowser, TextBox нигде не должен быть найден. Это связано с тем, что TextBox отображается как фон главного окна, а WebBrowser фактически является HWND-потомком главного окна, скрывающего фон.

Так что все (не) хорошо. Как достичь желаемого поведения? Я хочу, чтобы TextBox отображался поверх WebBrowser. Кто-нибудь сталкивался с этой проблемой?

Я думаю о том, чтобы иметь второе прозрачное окно WPF верхнего уровня без полей, переопределить его так, чтобы оно было у главного окна, и выполнить некоторые другие приемы, чтобы это произошло.

Прежде чем копаться, мне было интересно, есть ли у кого-нибудь очевидное или более простое решение?


Обновление от Meleak

Я предлагаю эту Баунти всем, кто может опубликовать реализацию Ray Burns Answer AirRepair. Я попробовал себя, но тщетно

Ответы [ 4 ]

2 голосов
/ 10 марта 2010
1 голос
/ 10 сентября 2010

если вы ищете быстрое и простое решение, просто используйте всплывающее управление, вот пример

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

1 голос
/ 11 марта 2010

Предлагаемое решение

Я предлагаю простой класс "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 решает проблемы воздушного пространства путем:

  1. Создание дочернего hWnd под заданным HwndHost с использованием HwndSource
  2. Установка его hRgn для соответствующей области
  3. Установка для RootVisual значения Border, для которого Background является VisualBrush элемента управления оформлением
  4. Пересылка WM_MOUSEMOVE и т. Д., Полученные ребенком hWnd, в главное окно WPF

В результате WPF продолжает рисовать контент за контентом Win32, но дочернее окно AirRepair перерисовывает то же содержимое перед контентом Win32 в отдельном элементе управления Win32 .

Некоторые важные детали реализации

Получение родительского hWnd

Если изначально установлен Win32Host, он может иметь или не иметь hWnd. PropertyChangedCallback должен использовать PresentationSource.AddSourceChangedHandler / PresentationSource.RemoveSourceChangedHandler для обнаружения возможных изменений hWnd, затем обновлять свой собственный указатель hWnd в обратном вызове Dispatcher.BeginInvoke, чтобы HwndHost имел возможность завершить обработку события SourceChanged.

Создание потомка hWnd

Дочерний hWnd может быть создан, обработан и подключен в управляемом коде с использованием класса HwndSource. Обязательно утилизируйте его, когда родительский hWnd Win32Host больше не доступен.

Положение ребенка 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 равно нулю, используйте прямоугольник. Если 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,
    }
  };

Пересылка сообщений мыши

Добавьте хук, используя 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);
};
0 голосов
/ 11 марта 2010

Если вы специально настроены на веб-браузер, было несколько попыток сделать это. Крис Кавана создал отличное решение на основе Chrome.

...