开发者

Render WPF control on top of WindowsFormsHost

I know that default WPF behavior is to render WPF controls and then on top render W开发者_如何学运维inForms, 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.)


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.


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.


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;
    }
}


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?


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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜