WPF Popup ZOrder

ぃ、小莉子 提交于 2019-12-04 15:28:53

I've tried to solve this issue as well, and have found no good solution. This seems to be the way it is supposed to work, and you can't override that.

The only solution I've come up with is to just use a regular layout panel and raise it's Z-Index, so it is the top-level control (this sort of simulates the Popup). The only time I have found that this won't work is when you have WinForms on the screen through WindowsFormsHosts. Those Winforms are always at a higher Z-Index than any of the WPF stuff. That is when you have to use a Popup to get around it.

So I dug through the framework source code to see where it actually causes the window to be topmost and it does this in a private nested class. However, it does not provide an option to either just be a child popup of the main window or to be the topmost window. Here is a hack to make it always a child popup window. One could easily add a dependency property and do some more magic to make it the top most.

using System;
using System.Reflection;
using System.Windows;
using System.Windows.Controls.Primitives;

namespace UI.Extensions.Wpf.Controls
{
    public class ChildPopup : Popup
    {
        static ChildPopup()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ChildPopup), new FrameworkPropertyMetadata(typeof(ChildPopup)));
        }

        public ChildPopup()
        {
            Type baseType = this.GetType().BaseType;
            dynamic popupSecHelper = GetHiddenField(this, baseType, "_secHelper");
            SetHiddenField(popupSecHelper, "_isChildPopupInitialized", true);
            SetHiddenField(popupSecHelper, "_isChildPopup", true);
        }

        protected dynamic GetHiddenField(object container, string fieldName)
        {
            return GetHiddenField(container, container.GetType(), fieldName);
        }

        protected dynamic GetHiddenField(object container, Type containerType, string fieldName)
        {
            dynamic retVal = null;
            FieldInfo fieldInfo = containerType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
            if (fieldInfo != null)
            {
                retVal = fieldInfo.GetValue(container);
            }
            return retVal;
        }

        protected void SetHiddenField(object container, string fieldName, object value)
        {
            SetHiddenField(container, container.GetType(), fieldName, value);
        }

        protected void SetHiddenField(object container, Type containerType, string fieldName, object value)
        {
            FieldInfo fieldInfo = containerType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
            if (fieldInfo != null)
            {
                fieldInfo.SetValue(container, value);
            }
        }
    }
}

Although I've not tried to do it, I've also read that this can be done using adorners...Matt Galbraith suggested it on the MSDN forum when asked the same question...in case anyone is still reading this thread.

That's how I solved it:

    private void Popup_Opened(object sender, EventArgs events)
    {
        Popup popup = (Popup)sender;

        // Get window to make popop follow it when user change window's location.
        Window w = Window.GetWindow(popup);
        // Popups are always on top, so when another window gets focus, we
        // need to close all popups.
        w.Deactivated += delegate (object s, EventArgs e)
        {
            popup.IsOpen = false;
        };
        // When our dialog gets focus again, we show it back.
        w.Activated += delegate (object s, EventArgs e)
        {
            popup.IsOpen = true;
        };
     }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!