Всплывающее окно всегда остается на вершине - PullRequest
15 голосов
/ 30 сентября 2011

У меня есть приложение WPF, в котором есть одно главное окно, в котором я открываю всплывающее окно.Проблема, с которой я сталкиваюсь, состоит в том, что всплывающее окно всегда остается сверху.Если я открываю какое-то другое приложение ( любое приложение Windows ), главное окно переходит в фоновый режим, но всплывающие окна остаются сверху. Однако если я сверну Mainwindow , всплывающее окно также свернется.

Below image shows the problem. The PopUp window is appearing on top of Notepad application

Пожалуйста, помогите решить эту проблему.

Обновление:

Я открываю всплывающее окно, как показано ниже

 myPopup.IsOpen = true;

Ответы [ 7 ]

19 голосов
/ 29 августа 2013
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;

/// <summary>
/// Popup with code to not be the topmost control
/// </summary>
public class NonTopmostPopup : Popup
{
    /// <summary>
    /// Is Topmost dependency property
    /// </summary>
    public static readonly DependencyProperty IsTopmostProperty = DependencyProperty.Register("IsTopmost", typeof(bool), typeof(NonTopmostPopup), new FrameworkPropertyMetadata(false, OnIsTopmostChanged));

    private bool? _appliedTopMost;
    private bool _alreadyLoaded;
    private Window _parentWindow;

    /// <summary>
    /// Get/Set IsTopmost
    /// </summary>
    public bool IsTopmost
    {
        get { return (bool)GetValue(IsTopmostProperty); }
        set { SetValue(IsTopmostProperty, value); }
    }

    /// <summary>
    /// ctor
    /// </summary>
    public NonTopmostPopup()
    {
        Loaded += OnPopupLoaded;
        Unloaded += OnPopupUnloaded;
    }


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

        _alreadyLoaded = true;

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

        _parentWindow = Window.GetWindow(this);

        if (_parentWindow == null) 
            return;

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

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

    void OnParentWindowActivated(object sender, EventArgs e)
    {
        Debug.WriteLine("Parent Window Activated");
        SetTopmostState(true);
    }

    void OnParentWindowDeactivated(object sender, EventArgs e)
    {
        Debug.WriteLine("Parent Window Deactivated");

        if (IsTopmost == false)
        {
            SetTopmostState(IsTopmost);
        }
    }

    void OnChildPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        Debug.WriteLine("Child Mouse Left Button Down");

        SetTopmostState(true);

        if (!_parentWindow.IsActive && IsTopmost == false)
        {
            _parentWindow.Activate();
            Debug.WriteLine("Activating Parent from child Left Button Down");
        }
    }

    private static void OnIsTopmostChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var thisobj = (NonTopmostPopup)obj;

        thisobj.SetTopmostState(thisobj.IsTopmost);
    }

    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 (_appliedTopMost.HasValue && _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);
        }

        _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
}
12 голосов
/ 30 сентября 2011

Popups делать - насколько я знаю - не поддерживать такое поведение, их предполагаемое использование для ComboxBox -падающих и тому подобное, насколько я могу судить.Чтобы реализовать нечто подобное, вы можете использовать обычный Window и установить его Owner в главное окно, от которого оно должно зависеть.Это заставит всплывающее окно остаться поверх его владельца и свернуть вместе с владельцем.

например,

public class ChildWindow: Window
{
    public ChildWindow(Window owner)
    {
        this.Owner = owner;
    }
}
var popup = new ChildWindow(mainWindow);
popup.Show();

(Windows не может быть повторнооткрывается после закрытия, поэтому для повторного использования окна вам просто нужно Hide(), когда пользователь пытается закрыть его (обработать событие Closing и отменить с помощью аргументов события))

11 голосов
/ 30 сентября 2011

HB правильно - WPF Popup control не был предназначен, не быть самым верхним.С другой стороны - прочитайте следующий пост в блоге об использовании user32 для достижения вашей цели: http://chriscavanagh.wordpress.com/2008/08/13/non-topmost-wpf-popup/

0 голосов
/ 11 сентября 2015

Это решило мою проблему.

PopupForm pf = new PopupForm();
pf.Owner = this;
pf.Topmost = false;
pf.ShowDialog()
0 голосов
/ 30 сентября 2011

Создайте модальное окно всплывающего окна только для родительского элемента управления, как продемонстрировали Билли Холли и Брэд Лич .Таким образом, он останется поверх приложения / окна, открывшего всплывающий элемент управления, но больше не будет оставаться поверх других окон / приложений.

0 голосов
/ 30 сентября 2011

Вы создаете всплывающее окно, используя элемент управления WPF? То, как этот элемент управления отображает всплывающее окно, приводит к описанному вами поведению. Подумайте о создании нового окна, в котором будет отображаться содержимое, которое вы хотите отобразить.

0 голосов
/ 30 сентября 2011

Проверьте свойство Window.Topmost, означающее, что окно должно быть всегда сверху.

PopupWindow wnd = new PopupWindow();
wnd.ShowDialog();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...