开发者

Popup always stays on top

开发者 https://www.devze.com 2023-04-10 04:56 出处:网络
I\'ve WPF application which has one Main window on which I\'m opening a Popup window. The problem I\'m facing is the Popup window always stays on top. If I open some other application(any windows appl

I've WPF application which has one Main window on which I'm opening a Popup window. The problem I'm facing is the Popup window always stays on top. If I open some other application(any windows application) the main window goes into background but the Popup windows remain on top.However if I minimize the Mainwindow the P开发者_如何学运维opup also minimizes.

Popup always stays on top

Please help on overcoming this issue.

Update:

I am opening the Popup as below

 myPopup.IsOpen = true;


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
}


Popups do - as far as i know - not suppot such a behavior, their intended usage is for ComboxBox-dropdowns and the like as far as i can tell. To realize something like that you can use a normal Window and set its Owner to the main window on which it should be dependent. This will cause the popup-window to stay on top of its owner & to minimize together with the owner.

e.g.

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

(Windows cannot be re-opened once closed, so to reuse a window you just have to Hide() it when the user tries to close it (handle Closing event and cancel using event args))


H.B. is correct - WPF Popup control was not intended do be not top-most. On the other hand - check out the following blog post on using user32 for achieving your goal: http://chriscavanagh.wordpress.com/2008/08/13/non-topmost-wpf-popup/


Are you creating the PopUp using the WPF control? The way this control paints popup leads to the behaviour you are describing. Consider creating a new Window showing the content you´d like to display.


Check the Window.Topmost property whitch is indicates that window should be always on top.

PopupWindow wnd = new PopupWindow();
wnd.ShowDialog();


This solved my problem.

PopupForm pf = new PopupForm();
pf.Owner = this;
pf.Topmost = false;
pf.ShowDialog()


By default popups were designed to behaves as you comment, to be topmost always. Altering this behavior using hacks or things like that will complicate your life since you will have to lead with some other problems that for sure will arise.

Anyway, too late, but may be this can help you or others: https://chriscavanagh.wordpress.com/2008/08/13/non-topmost-wpf-popup/

Note that this has some issues.... read all the comments published there. Among other issues, it makes all childs to be non-topmost so comboboxes are affected. Comboboxes will have problems with it as they rely on having a topmost popup for when they are drop down, so if you have any combobox inside your popup it will break.

Also there are other problems related to the popup position, when you resize and then move the main window where the popup is it, popup remains in the same position.

People there are proposing some approaches for these issues. I don't know if all are resolved but you can give it a try or try to resolve them if it is up to you.

Other people trying to face with this are explaining their proposals here also (although none of them seems to be the perfect solution, it looks like there is not a perfect one, all of them have drawnbacks, but again as I have said, trying to alter the default behavior for which a thing was designed for will lead into continuous problems) : WPF Popup ZOrder


Create your PopUp window modal to the parent control only, as demonstrated by Billy Holli and Brad Leach. That way it will stay on top of the application/window which opened the popup control, but will not stay on top of other windows/applications any longer.

0

精彩评论

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

关注公众号