Render WPF control on top of WindowsFormsHost
Asked Answered
Z

6

15

I know that default WPF behavior is to render WPF controls and then on top render WinForms, but are there any way to render WPF on top of WindowsFormsHost?

Edit: I have found a temp hack as well. When wpf control overlaps WindowsFormsHost, I change the size of the WindowsFormsHost (This only works when you have rectangular object which overlaps, doesn't work for other shapes.)

Zen answered 12/5, 2011 at 13:42 Comment(1)
One way I do for this is overlay another window ontop with opacity and transparent, works well so long as using fullscreen stuffDurkin
F
8

This "airspace" issue is suppose to be fixed in WPF vNext. There are a couple solutions out there, such as here, here, and here.

One way to do this is to host the WPF content in a transparent Popup or Window, which overlays the Interop content.

Filings answered 12/5, 2011 at 13:51 Comment(2)
NOTE: I comment this as an attempt to not get the hopes up for whoever reads this, the "airspace" problem in @CodeNaked's posted article had a fix (hwndhost.IsRedirected) implemented in .NET 4.5 beta, but then dropped in the final release for whatever reason. Read this, and thisGullah
Transparent Window is actually a good solution (though still a hack). It requires few lines of code behind to keep the position and size, etc, correct but it seems to work very reliably.Dissuade
B
10

Late to the party, I know, but I recently came across this issue using a WebBrowser control.

The final fix was to create a screenshot of the web browser whenever I hosted a modal dialog over the top. Since this was a little fiddly, I turned it into a Github project, hopefully this helps a little -

https://github.com/chris84948/AirspaceFixer

(It's on Nuget too, under AirspaceFixer)

Once you have the project all you need to do is this

xmlns:asf="clr-namespace:AirspaceFixer;assembly=AirspaceFixer"

<asf:AirspacePanel FixAirspace="{Binding FixAirspace}">
    <WebBrowser x:Name="Browser" />
</asf:AirspacePanel>

Where FixAirspace is the dependency property that switches from the "real" view of the content, to the screenshot or "fake" view.

Bergstein answered 23/11, 2016 at 22:7 Comment(0)
F
8

This "airspace" issue is suppose to be fixed in WPF vNext. There are a couple solutions out there, such as here, here, and here.

One way to do this is to host the WPF content in a transparent Popup or Window, which overlays the Interop content.

Filings answered 12/5, 2011 at 13:51 Comment(2)
NOTE: I comment this as an attempt to not get the hopes up for whoever reads this, the "airspace" problem in @CodeNaked's posted article had a fix (hwndhost.IsRedirected) implemented in .NET 4.5 beta, but then dropped in the final release for whatever reason. Read this, and thisGullah
Transparent Window is actually a good solution (though still a hack). It requires few lines of code behind to keep the position and size, etc, correct but it seems to work very reliably.Dissuade
T
8

Try this on for size:

<hacks:AirspaceOverlay>
    <hacks:AirspaceOverlay.OverlayChild>
        <Canvas ToolTip = "A tooltip over a DirectX surface" Background="#01000000" Name="Overlay" />
    </hacks:AirspaceOverlay.OverlayChild>
    <controls:OpenGLControlWrappingWindowsFormsHost />
</hacks:AirspaceOverlay>


// Adapted from http://blogs.msdn.com/b/pantal/archive/2007/07/31/managed-directx-interop-with-wpf-part-2.aspx & http://www.4mghc.com/topics/69774/1/in-wpf-how-can-you-draw-a-line-over-a-windowsformshost
public class AirspaceOverlay : Decorator
{
    private readonly Window _transparentInputWindow;
    private Window _parentWindow;

    public AirspaceOverlay()
    {
        _transparentInputWindow = CreateTransparentWindow();
        _transparentInputWindow.PreviewMouseDown += TransparentInputWindow_PreviewMouseDown;
    }

    public object OverlayChild
    {
        get { return _transparentInputWindow.Content; }
        set { _transparentInputWindow.Content = value; }
    }

    private static Window CreateTransparentWindow()
    {
        var transparentInputWindow = new Window();

        //Make the window itself transparent, with no style.
        transparentInputWindow.Background = Brushes.Transparent;
        transparentInputWindow.AllowsTransparency = true;
        transparentInputWindow.WindowStyle = WindowStyle.None;

        //Hide from taskbar until it becomes a child
        transparentInputWindow.ShowInTaskbar = false;

        //HACK: This window and it's child controls should never have focus, as window styling of an invisible window 
        //will confuse user.
        transparentInputWindow.Focusable = false;

        return transparentInputWindow;
    }

    void TransparentInputWindow_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        _parentWindow.Focus();
    }

    protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
    {
        base.OnRenderSizeChanged(sizeInfo);
        UpdateOverlaySize();
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);
        if (_transparentInputWindow.Visibility != Visibility.Visible)
        {
            UpdateOverlaySize();
            _transparentInputWindow.Show();
            _parentWindow = GetParentWindow(this);
            _transparentInputWindow.Owner = _parentWindow;
            _parentWindow.LocationChanged += ParentWindow_LocationChanged;
            _parentWindow.SizeChanged += ParentWindow_SizeChanged;
        }
    }

    private static Window GetParentWindow(DependencyObject o)
    {
        var parent = VisualTreeHelper.GetParent(o);
        if (parent != null)
            return GetParentWindow(parent);
        var fe = o as FrameworkElement;
        if (fe is Window)
            return fe as Window;
        if (fe != null && fe.Parent != null)
            return GetParentWindow(fe.Parent);  
        throw new ApplicationException("A window parent could not be found for " + o); 
    }

    private void ParentWindow_LocationChanged(object sender, EventArgs e)
    {
        UpdateOverlaySize();
    }

    private void ParentWindow_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        UpdateOverlaySize();
    }

    private void UpdateOverlaySize()
    {
        var hostTopLeft = PointToScreen(new Point(0, 0));
        _transparentInputWindow.Left = hostTopLeft.X;
        _transparentInputWindow.Top = hostTopLeft.Y;
        _transparentInputWindow.Width = ActualWidth;
        _transparentInputWindow.Height = ActualHeight;
    }
}
Tundra answered 14/5, 2014 at 18:31 Comment(0)
T
7

Here's a link to the best answer I've seen on this subject so far: Can I overlay a WPF window on top of another?

Tananarive answered 9/7, 2011 at 0:4 Comment(0)
U
4

If anyone finds themselves unsatisfied with the hacks, setting the Visibility of the WindowsFormsHost to Collapsed or Hidden is always an option.

Urmia answered 3/3, 2016 at 21:18 Comment(0)
P
1

I came across this issue while trying to create a MDI style interface hosting WinForms controls while porting a win forms app to WPF.

I managed to solve it by doing something like this: WindowsFormsHost -> ElementHost -> WindowsFormsHost -> my win forms controls.

Its super ugly, but it creates windows layers for the WPF content to be under the WinForms content.

Paripinnate answered 4/5, 2020 at 23:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.