Могу ли я наложить окно WPF поверх другого?

У меня есть окно WPF, которое содержит WindowsFormsHost элемент. Мне нужно рисовать вещи поверх этого элемента, но характер WindowsFormsHost означает, что он всегда находится на вершине стопки чертежей. Поскольку я не могу рисовать в том же окне WPF поверх WindowsFormsHost компонент, я могу наложить другое окно поверх него?

Я пробовал это в зачаточном состоянии, но у меня есть несколько проблем:

1) Я не могу остановить окна других приложений, находящихся между главным окном и окном наложения.

2) Когда я нажимаю Alt-Tab, в списке окон появляется оверлейное окно, что довольно уродливо.

В основном мне нужна концепция "дочернего окна" и окна, которое, по сути, появляется как часть другого окна. UserControls не будет работать для меня, так как WindowsFormsHost всегда будет рисовать поверх него.

Есть идеи?


Обновление [23 мая '11 в 10:13 ]

Спасибо вам обоим за ответы.

Я попробовал ChildWindow подход, а WindowsFormsHost элемент по-прежнему рисует сверху. Насколько я понимаю, только настоящее окно может рисовать поверх WindowsFormsHostчто-нибудь в том же окне будет идти под WindowsFormsHost,

Элемент с WindowsFormsHost будет по-прежнему рисовать под компонентом WinForms, они всегда рисуются сверху, и это не подлежит обсуждению...

Я думаю, что я ищу способ закрепить внешнее окно, чтобы оно действовало как часть главного окна. На Mac есть концепция настоящего "дочернего окна", я ищу что-то подобное.

5 ответов

Я работал над этой проблемой, используя Popup а не прозрачный Window

Обновить

Я закончил с подклассом Popup который я называю AirspacePopup,

Какие AirspacePopup делает

  • Следуйте его PlacementTarget,
  • Не всегда сверху, но размещается относительно Window в котором оно находится. Это решение взято из блога Криса Кавана.
  • Разрешено перемещать "за пределы экрана". Это достигается путем отсечения Popup и установка отрицательного Margin на своего ребенка, как только он выходит за пределы экрана. Это решение взято из этой статьи Stackru Рика Сладки

Вот пример, где AirspacePopup используется для рисования Ellipse на вершине WebBrowser Control (который фактически является WinForms Control), но он будет работать так же хорошо, как и любой другой WindowsFormsHost,

<Grid>
    <local:AirspacePopup PlacementTarget="{Binding ElementName=webBrowser}"
                         FollowPlacementTarget="True"
                         AllowOutsideScreenPlacement="True"
                         ParentWindow="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                         IsOpen="True"
                         AllowsTransparency="True"
                         Placement="Center"
                         Width="{Binding ElementName=googleBrowser, Path=ActualWidth}"
                         Height="{Binding ElementName=googleBrowser, Path=ActualHeight}">
        <Grid>
            <Ellipse Width="100" Height="100" Fill="Green" Margin="100"/>
        </Grid>
    </local:AirspacePopup>
    <WebBrowser Name="webBrowser" Loaded="WebBrowser_Loaded"/>
</Grid>

Простой код для навигации..

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void WebBrowser_Loaded(object sender, RoutedEventArgs e)
    {
        WebBrowser webbrowser = sender as WebBrowser;
        webbrowser.Navigate("http://www.stackru.com");
    }
}

AirspacePopup

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;

public class AirspacePopup : Popup
{
    public static readonly DependencyProperty IsTopmostProperty =
        DependencyProperty.Register("IsTopmost",
                                    typeof(bool),
                                    typeof(AirspacePopup),
                                    new FrameworkPropertyMetadata(false, OnIsTopmostChanged));

    public static readonly DependencyProperty FollowPlacementTargetProperty =
        DependencyProperty.RegisterAttached("FollowPlacementTarget",
                                            typeof(bool),
                                            typeof(AirspacePopup),
                                            new UIPropertyMetadata(false));

    public static readonly DependencyProperty AllowOutsideScreenPlacementProperty =
        DependencyProperty.RegisterAttached("AllowOutsideScreenPlacement",
                                            typeof(bool),
                                            typeof(AirspacePopup),
                                            new UIPropertyMetadata(false));

    public static readonly DependencyProperty ParentWindowProperty =
        DependencyProperty.RegisterAttached("ParentWindow",
                                            typeof(Window),
                                            typeof(AirspacePopup),
                                            new UIPropertyMetadata(null, ParentWindowPropertyChanged));

    private static void OnIsTopmostChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        AirspacePopup airspacePopup = source as AirspacePopup;
        airspacePopup.SetTopmostState(airspacePopup.IsTopmost);
    }

    private static void ParentWindowPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        AirspacePopup airspacePopup = source as AirspacePopup;
        airspacePopup.ParentWindowChanged();
    }

    private bool? m_appliedTopMost;
    private bool m_alreadyLoaded;
    private Window m_parentWindow;

    public AirspacePopup()
    {
        Loaded += OnPopupLoaded;
        Unloaded += OnPopupUnloaded;

        DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(PlacementTargetProperty, typeof(AirspacePopup));
        descriptor.AddValueChanged(this, PlacementTargetChanged);
    }

    public bool IsTopmost
    {
        get { return (bool)GetValue(IsTopmostProperty); }
        set { SetValue(IsTopmostProperty, value); }
    }
    public bool FollowPlacementTarget
    {
        get { return (bool)GetValue(FollowPlacementTargetProperty); }
        set { SetValue(FollowPlacementTargetProperty, value); }
    }
    public bool AllowOutsideScreenPlacement
    {
        get { return (bool)GetValue(AllowOutsideScreenPlacementProperty); }
        set { SetValue(AllowOutsideScreenPlacementProperty, value); }
    }
    public Window ParentWindow
    {
        get { return (Window)GetValue(ParentWindowProperty); }
        set { SetValue(ParentWindowProperty, value); }
    }

    private void ParentWindowChanged()
    {
        if (ParentWindow != null)
        {
            ParentWindow.LocationChanged += (sender, e2) =>
            {
                UpdatePopupPosition();
            };
            ParentWindow.SizeChanged += (sender, e2) =>
            {
                UpdatePopupPosition();
            };
        }
    }
    private void PlacementTargetChanged(object sender, EventArgs e)
    {
        FrameworkElement placementTarget = this.PlacementTarget as FrameworkElement;
        if (placementTarget != null)
        {
            placementTarget.SizeChanged += (sender2, e2) =>
            {
                UpdatePopupPosition();
            };
        }
    }

    private void UpdatePopupPosition()
    {
        FrameworkElement placementTarget = this.PlacementTarget as FrameworkElement;
        FrameworkElement child = this.Child as FrameworkElement;

        if (PresentationSource.FromVisual(placementTarget) != null &&
            AllowOutsideScreenPlacement == true)
        {
            double leftOffset = CutLeft(placementTarget);
            double topOffset = CutTop(placementTarget);
            double rightOffset = CutRight(placementTarget);
            double bottomOffset = CutBottom(placementTarget);
            Debug.WriteLine(bottomOffset);
            this.Width = Math.Max(0, Math.Min(leftOffset, rightOffset) + placementTarget.ActualWidth);
            this.Height = Math.Max(0, Math.Min(topOffset, bottomOffset) + placementTarget.ActualHeight);

            if (child != null)
            {
                child.Margin = new Thickness(leftOffset, topOffset, rightOffset, bottomOffset);
            }
        }
        if (FollowPlacementTarget == true)
        {
            this.HorizontalOffset += 0.01;
            this.HorizontalOffset -= 0.01;
        }
    }
    private double CutLeft(FrameworkElement placementTarget)
    {
        Point point = placementTarget.PointToScreen(new Point(0, placementTarget.ActualWidth));
        return Math.Min(0, point.X);
    }
    private double CutTop(FrameworkElement placementTarget)
    {
        Point point = placementTarget.PointToScreen(new Point(placementTarget.ActualHeight, 0));
        return Math.Min(0, point.Y);
    }
    private double CutRight(FrameworkElement placementTarget)
    {
        Point point = placementTarget.PointToScreen(new Point(0, placementTarget.ActualWidth));
        point.X += placementTarget.ActualWidth;
        return Math.Min(0, SystemParameters.VirtualScreenWidth - (Math.Max(SystemParameters.VirtualScreenWidth, point.X)));
    }
    private double CutBottom(FrameworkElement placementTarget)
    {
        Point point = placementTarget.PointToScreen(new Point(placementTarget.ActualHeight, 0));
        point.Y += placementTarget.ActualHeight;
        return Math.Min(0, SystemParameters.VirtualScreenHeight - (Math.Max(SystemParameters.VirtualScreenHeight, point.Y)));
    }

    private void OnPopupLoaded(object sender, RoutedEventArgs e)
    {
        if (m_alreadyLoaded) 
            return;

        m_alreadyLoaded = true;

        if (Child != null)
        {
            Child.AddHandler(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(OnChildPreviewMouseLeftButtonDown), true);
        }

        m_parentWindow = Window.GetWindow(this);

        if (m_parentWindow == null) 
            return;

        m_parentWindow.Activated += OnParentWindowActivated;
        m_parentWindow.Deactivated += OnParentWindowDeactivated;
    }

    private void OnPopupUnloaded(object sender, RoutedEventArgs e)
    {
        if (m_parentWindow == null)
            return;
        m_parentWindow.Activated -= OnParentWindowActivated;
        m_parentWindow.Deactivated -= OnParentWindowDeactivated;
    }

    private void OnParentWindowActivated(object sender, EventArgs e)
    {
        SetTopmostState(true);
    }

    private void OnParentWindowDeactivated(object sender, EventArgs e)
    {
        if (IsTopmost == false)
        {
            SetTopmostState(IsTopmost);
        }
    }

    private void OnChildPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        SetTopmostState(true);
        if (!m_parentWindow.IsActive && IsTopmost == false)
        {
            m_parentWindow.Activate();
        }
    }

    protected override void OnOpened(EventArgs e)
    {
        SetTopmostState(IsTopmost);
        base.OnOpened(e);
    }

    private void SetTopmostState(bool isTop)
    {
        // Don’t apply state if it’s the same as incoming state
        if (m_appliedTopMost.HasValue && m_appliedTopMost == isTop)
        {
            return;
        }

        if (Child == null) 
            return;

        var hwndSource = (PresentationSource.FromVisual(Child)) as HwndSource;

        if (hwndSource == null) 
            return;
        var hwnd = hwndSource.Handle;

        RECT rect;

        if (!GetWindowRect(hwnd, out rect)) 
            return;

        Debug.WriteLine("setting z-order " + isTop);

        if (isTop)
        {
            SetWindowPos(hwnd, HWND_TOPMOST, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS);
        }
        else
        {
            // Z-Order would only get refreshed/reflected if clicking the
            // the titlebar (as opposed to other parts of the external
            // window) unless I first set the popup to HWND_BOTTOM
            // then HWND_TOP before HWND_NOTOPMOST
            SetWindowPos(hwnd, HWND_BOTTOM, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS);
            SetWindowPos(hwnd, HWND_TOP, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS);
            SetWindowPos(hwnd, HWND_NOTOPMOST, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS);
        }

        m_appliedTopMost = isTop;
    }

    #region P/Invoke imports & definitions
    #pragma warning disable 1591 //Xml-doc
    #pragma warning disable 169 //Never used-warning
    // ReSharper disable InconsistentNaming
    // Imports etc. with their naming rules

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT

    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

    [DllImport("user32.dll")]
    private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
    int Y, int cx, int cy, uint uFlags);

    static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
    static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
    static readonly IntPtr HWND_TOP = new IntPtr(0);
    static readonly IntPtr HWND_BOTTOM = new IntPtr(1);

    private const UInt32 SWP_NOSIZE = 0x0001;
    const UInt32 SWP_NOMOVE = 0x0002;
    const UInt32 SWP_NOZORDER = 0x0004;
    const UInt32 SWP_NOREDRAW = 0x0008;
    const UInt32 SWP_NOACTIVATE = 0x0010;

    const UInt32 SWP_FRAMECHANGED = 0x0020; /* The frame changed: send WM_NCCALCSIZE */
    const UInt32 SWP_SHOWWINDOW = 0x0040;
    const UInt32 SWP_HIDEWINDOW = 0x0080;
    const UInt32 SWP_NOCOPYBITS = 0x0100;
    const UInt32 SWP_NOOWNERZORDER = 0x0200; /* Don’t do owner Z ordering */
    const UInt32 SWP_NOSENDCHANGING = 0x0400; /* Don’t send WM_WINDOWPOSCHANGING */

    const UInt32 TOPMOST_FLAGS = 
        SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING;

    // ReSharper restore InconsistentNaming
    #pragma warning restore 1591
    #pragma warning restore 169
    #endregion
}

После многих испытаний различных решений:

  1. Microsoft.DwayneNeed ( https://microsoftdwayneneed.codeplex.com/)

    Плюсы:
    • Работает хорошо, насколько я могу судить (еще не проверял, но это выглядело так)
    • Падение производительности, кажется, довольно низкое.

    Минусы:
    • Относительно большая библиотека.

  2. AirspaceFixer ( https://github.com/chris84948/AirspaceFixer)

    Плюсы:
    • Работает хорошо (еще не проверял, но это выглядело так)

    Минусы:
    • Есть небольшой хит производительности. (Больше, чем с Microsoft.DwayneNeed, насколько я могу судить)

  3. AirspacePopup от Фредрика Хедблада ( /questions/39330094/mogu-li-ya-nalozhit-okno-wpf-poverh-drugogo/39330097#39330097)

    Плюсы:
    • Простой код В основном:D

    Минусы:
    • В полноэкранном режиме часть дна отсутствует.
    • Проблемы с несколькими мониторами: при перемещении из одного окна в другое наложение не полностью поверх окна winformshost.

Для меня лучшим решением было использовать Microsoft.DwayneNeed. (Я использовал его, чтобы получить CefSharp-Winforms в WPF-приложении)

Потому что заставить его работать не так просто, это небольшой урок:

  1. Перейдите на https://microsoftdwayneneed.codeplex.com/SourceControl/latest
  2. Нажмите скачать.
  3. Откройте Microsoft.DwayneNeed.sln
  4. Скомпилируйте это.
  5. Ссылка Microsoft.DwayneNeed.dll в созданной папке Debug или Releas.
  6. добавлять
     XMLNS: Interop="CLR-имена:Microsoft.DwayneNeed.Interop; сборка =Microsoft.DwayneNeed 
    к вашему окну.
  7. добавлять
    
    
     
    к вашей сетке.

Пример:

<Window x:Class="Toll.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:interop="clr-namespace:Microsoft.DwayneNeed.Interop;assembly=Microsoft.DwayneNeed"
  xmlns:local="clr-namespace:Toll"
  mc:Ignorable="d"
  Title="MainWindow" Width="1500" Height="800" Closing="Window_Closing">
 <Grid Name="root">
  <interop:AirspaceDecorator AirspaceMode="Redirect"
         Background="White"
         IsInputRedirectionEnabled="True"
         IsOutputRedirectionEnabled="True">
   <WindowsFormsHost x:Name="windowsFormsHost1" Visibility="Visible" />
  </interop:AirspaceDecorator>
  
  <Ellipse Width="100" Height="100" Fill="Green" Margin="100"/>
 </Grid>
</Window>

Вы могли бы сделать "наложение" часть в WindowsFormsHost, Таким образом, у вас может быть дочерний элемент, который должен быть поверх другого содержимого размещенного элемента.

В типичном приложении MVP, использующем CAL или агрегатор событий, вам, возможно, придется создать другое окно с именем popup или child (ChildWindow.XAML) и есть метод get в (ChildWindow.XAML.CS) лайк

public static ChildWindow Get()
        {
            ChildWindow dialogBox = new ChildWindow();
            return dialogBox;
        } 

Имейте свойство в maniwindow, которое может при необходимости возвращать тип Childwindow. лайк

public bool ShowChildWindow
{
    get
    {
        return PopUpDialog.Get().ShowDialog().GetValueOrDefault();
    }
}

Надеюсь это поможет,

Я бы предложил использовать библиотеку MahApps.

Я боролся с этой проблемой в течение некоторого времени и обнаружил, что библиотека MahApps исправила проблему воздушного пространства без какой-либо конфигурации с моей стороны. Для получения дополнительной информации см. Их FAQ по этому вопросу:

https://github.com/MahApps/MahApps.Metro/wiki/FAQ#1-why-is-so-and-so-winforms-control-invisible-or-not-rendering-why-is-the-webbrowser- или-другой-контроль покрывающего-мой-выпадающий или-другое-контроль-воздушное пространство

Другие вопросы по тегам