Proposed solution
I suggest a simple "AirRepair" class with this signature:
public class AirRepair : Decorator { public HwndHost Win32Host ...
Used as follows:
<Grid> <WebBrowser x:Name="browser" ... /> <AirRepair Win32Host="{Binding ElementName=browser}" Margin="10" HorizontalAlignment="Left" ...> <TextBox ... /> </AirRepair> </Grid>
AirRepair can be used with WebBrowser , WindowsFormsHost or any other HwndHost . The area covered by the decorated control is displayed inside Win32 content and receives focus and mouse events. For controls that do not have a rectangular decoration, the displayed area can be set with the RepairArea and / or OpacityMask .
How it works
AirRepair solves airspace issues:
- Creating a child hWnd under a given
HwndHost using HwndSource - Setting hRgn to the appropriate area
- Configure
RootVisual on a Border whose Background is a VisualBrush Decorated Control - Redirection
WM_MOUSEMOVE etc. received by the hWnd child to the main WPF window
The result of this is that WPF continues to draw content behind Win32 content, but the AirRepair child window redraws the same content before the Win32 content in a separate Win32 control.
Some important implementation details
Getting parent hWnd
When Win32Host initially installed, it may or may not have hWnd. PropertyChangedCallback should use PresentationSource.AddSourceChangedHandler / PresentationSource.RemoveSourceChangedHandler to detect possible hWnd changes, and then update its own hWnd pointer in the Dispatcher.BeginInvoke HwndHost so that HwndHost can finish processing the SourceChanged event.
Creating a child hWnd
The hWnd child can be created, registered, and connected to managed code using the HwndSource class. Be sure to delete it when the parent Win32Host hWnd is no longer available.
Child placement hWnd
The position of the child hWnd window (relative to its parent) can be calculated as:
var compositionTarget = PresentationSource.FromVisual(this).CompositionTarget; var position = compositionTarget.TransformToDevice( this.TransformToVisual(Win32Host));
The UIELement.LayoutUpdated event should be used to update this update.
HRgn calculation and opacity
Optional: omit if only rectangular repair areas are supported
When RepairArea or OpacityMask and there is a child hWnd, use RenderTargetBitmap to draw RepairArea with OpacityMask , then create hRgn. If RepairArea is null, use a rectangle. If OpacityMask is null, use black. The RenderTargetBitmap size RenderTargetBitmap set by converting the coordinates of the AirRepair decorator to the coordinates of the device. Note that this does not properly handle the OpacityMask variable, such as an animated brush, or VisualBrush , a Visual change.
Drawing content on child hWnd
Use VisualBrush , whose Visual is an AirRepair decorator, not a decorated control. This allows you to replace the decorated control without changing the content.
childHwndSource.RootVisual = new Border { Background = new VisualBrush { Visual = this, ViewBoxUnits = BrushMappingMode.Absolute, ViewPortUnits = BrushMappingMode.Absolute, } };
Forwarding mouse messages
Add the hook using HwndSource.AddHook , then use Win32 PostMessage in the container:
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); };