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.)

akjoshi
  • 15,374
  • 13
  • 103
  • 121
Vitalij
  • 4,587
  • 9
  • 42
  • 65
  • One way I do for this is overlay another window ontop with opacity and transparent, works well so long as using fullscreen stuff – Mark Homer Nov 25 '15 at 15:29

6 Answers6

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.

chris84948
  • 3,940
  • 3
  • 22
  • 25
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;
    }
}
sky-dev
  • 6,190
  • 2
  • 33
  • 32
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.

CodeNaked
  • 40,753
  • 6
  • 122
  • 148
  • 8
    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](http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2644120-bring-back-the-hwndhost-isredirected-and-compositi), and [this](http://social.msdn.microsoft.com/Forums/vstudio/en-US/3a171a12-399c-450b-8ed9-9320e8a6dfce/wpf-45-beta-hwndhostisredirected-and-compositionmode-removed-permanently?forum=wpf) – johnildergleidisson Apr 14 '14 at 22:59
  • 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. – Juho Apr 13 '18 at 12:23
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?

Community
  • 1
  • 1
AVIDeveloper
  • 2,954
  • 1
  • 25
  • 32
4

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

Tim
  • 857
  • 6
  • 13
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.

Erik
  • 11
  • 1