Предлагаемое решение
Я предлагаю простой класс "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 решает проблемы воздушного пространства путем:
- Создание дочернего hWnd под заданным
HwndHost
с использованием HwndSource
- Установка его hRgn для соответствующей области
- Установка для
RootVisual
значения Border
, для которого Background
является VisualBrush
элемента управления оформлением
- Пересылка
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);
};