解决Popup StayOpen=true时,永远置顶的问题
前言
Popup显示时会置顶显示。尤其是 Popup设置了StayOpen=true时,会一直置顶显示,问题更明显。
置顶显示问题现象:

解决方案
怎么解决问题?
获取绑定UserControl所在的窗口,窗口层级变化时,通知更新当前Popup的Tostmost属性。
1. 添加附加属性
在属性变更中,监听Loaded/UnLoaded事件,在加载后处理相应的逻辑。
private static readonly DependencyProperty TopmostInCurrentWindowProperty = DependencyProperty.RegisterAttached("TopmostInCurrentWindow",
typeof(bool), typeof(Popup), new FrameworkPropertyMetadata(false, OnTopmostInCurrentWindowChanged));
public static bool GetTopmostInCurrentWindow(DependencyObject obj)
{
return (bool)obj.GetValue(TopmostInCurrentWindowProperty);
}
public static void SetTopmostInCurrentWindow(DependencyObject obj, bool value)
{
obj.SetValue(TopmostInCurrentWindowProperty, value);
}
private static void OnTopmostInCurrentWindowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Popup popup)
{
_popup = popup;
popup.Loaded -= OnPopupLoaded;
popup.Unloaded -= OnPopupUnloaded;
if ((bool)e.NewValue)
{
popup.Loaded += OnPopupLoaded;
popup.Unloaded += OnPopupUnloaded;
}
}
}
2. 添加事件监听
- 在Popup.Loaded事件中,监听Popup所在窗口的唤醒事件。同时,Unloaded事件中注销所在窗口的事件监听。
- 在窗口唤醒事件监听逻辑中,设置当前popup选择性的置顶显示
- 添加Popup的MouseDown事件监听,点击Popup的内容后,Popup置顶显示、窗口层级也发相应的变化。
static void OnPopupLoaded(object sender, RoutedEventArgs e)
{
if (!(sender is Popup popup))
return; popup.Child?.AddHandler(UIElement.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(OnChildPreviewMouseLeftButtonDown), true); _parentWindow = Window.GetWindow(popup);
if (_parentWindow == null)
return; _parentWindow.Activated -= OnParentWindowActivated;
_parentWindow.Deactivated -= OnParentWindowDeactivated;
_parentWindow.Activated += OnParentWindowActivated;
_parentWindow.Deactivated += OnParentWindowDeactivated;
} static void OnPopupUnloaded(object sender, RoutedEventArgs e)
{
if (_parentWindow == null)
return;
_parentWindow.Activated -= OnParentWindowActivated;
_parentWindow.Deactivated -= OnParentWindowDeactivated;
} private static void OnParentWindowActivated(object sender, EventArgs e)
{
SetTopmostState(true);
} private static void OnParentWindowDeactivated(object sender, EventArgs e)
{
//Parent Window Deactivated
if (IsTopmost == false)
{
SetTopmostState(IsTopmost);
}
} static void OnChildPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
SetTopmostState(true); if (!_parentWindow.IsActive && IsTopmost == false)
{
_parentWindow.Activate();
}
}
3. 选择性置顶显示
- 记录/设置当前Popup的置顶显示状态
- 选择性置顶显示-可以显示在最顶层,也可以只显示在当前窗口的上层。
private static bool IsTopmost
{
get => _isTopmost;
set
{
_isTopmost = value;
SetTopmostState(value);
}
} private static void SetTopmostState(bool isTop)
{
if (_appliedTopMost.HasValue && _appliedTopMost == isTop)
{
return;
} if (_popup?.Child == null)
return; var hwndSource = (PresentationSource.FromVisual(_popup.Child)) as HwndSource; if (hwndSource == null)
return;
var hwnd = hwndSource.Handle; RECT rect; if (!GetWindowRect(hwnd, out rect))
return; if (isTop)
{
SetWindowPos(hwnd, HWND_TOPMOST, rect.Left, rect.Top, (int)_popup.Width, (int)_popup.Height, TOPMOST_FLAGS);
}
else
{
// 重新激活Topmost,需要bottom->top->notop
SetWindowPos(hwnd, HWND_BOTTOM, rect.Left, rect.Top, (int)_popup.Width, (int)_popup.Height, TOPMOST_FLAGS);
SetWindowPos(hwnd, HWND_TOP, rect.Left, rect.Top, (int)_popup.Width, (int)_popup.Height, TOPMOST_FLAGS);
SetWindowPos(hwnd, HWND_NOTOPMOST, rect.Left, rect.Top, (int)_popup.Width, (int)_popup.Height, TOPMOST_FLAGS);
} _appliedTopMost = isTop;
}
以下是窗口消息处理、私有字段:
通过user32.dll的GetWindowRect和SetWindowPos函数,处理Popup层级问题
#region 窗口消息
[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(-);
static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-);
static readonly IntPtr HWND_TOP = new IntPtr();
static readonly IntPtr HWND_BOTTOM = new IntPtr();
private const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOREDRAW = 0x0008;
const UInt32 SWP_NOACTIVATE = 0x0010;
const UInt32 SWP_NOOWNERZORDER = 0x0200;
const UInt32 SWP_NOSENDCHANGING = 0x0400;
//很重要,窗口切换等需要将popup显示层级重新刷新
const UInt32 TOPMOST_FLAGS =
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING;
#endregion
#region private fileds
private static bool? _appliedTopMost;
private static bool _alreadyLoaded;
private static Window _parentWindow;
private static Popup _popup;
private static bool _isTopmost;
#endregion
下载 此Demo
注:也可以通过自定义用户控件Popup实现,逻辑一样:
/// <summary>
/// 解决StayOpen=true时,永远置顶的问题
/// </summary>
public class MyPopup : Popup
{
public static readonly DependencyProperty IsTopmostProperty = DependencyProperty.Register("IsTopmost", typeof(bool), typeof(MyPopup), new FrameworkPropertyMetadata(false, OnIsTopmostChanged)); private bool? _appliedTopMost;
private bool _alreadyLoaded;
private Window _parentWindow; public bool IsTopmost
{
get { return (bool)GetValue(IsTopmostProperty); }
set { SetValue(IsTopmostProperty, value); }
} /// <summary>
/// ctor
/// </summary>
public MyPopup()
{
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)
{
SetTopmostState(true);
} void OnParentWindowDeactivated(object sender, EventArgs e)
{
Debug.WriteLine("Parent Window Deactivated"); if (IsTopmost == false)
{
SetTopmostState(IsTopmost);
}
} void OnChildPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{ SetTopmostState(true); if (!_parentWindow.IsActive && IsTopmost == false)
{
_parentWindow.Activate();
}
} private static void OnIsTopmostChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var thisobj = (MyPopup)obj; thisobj.SetTopmostState(thisobj.IsTopmost);
} protected override void OnOpened(EventArgs e)
{
SetTopmostState(IsTopmost);
base.OnOpened(e);
} private void SetTopmostState(bool isTop)
{
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; if (isTop)
{
SetWindowPos(hwnd, HWND_TOPMOST, rect.Left, rect.Top, (int)Width, (int)Height, TOPMOST_FLAGS);
}
else
{
// 重新激活Topmost,需要bottom->top->notop
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;
} [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(-);
static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-);
static readonly IntPtr HWND_TOP = new IntPtr();
static readonly IntPtr HWND_BOTTOM = new IntPtr(); 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 */ //很重要,窗口切换等需要将popup显示层级重新刷新
const UInt32 TOPMOST_FLAGS =
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING;
}
解决方案总结
添加如上附加属性或者用户控件Popup后,能解决置顶问题,Popup只会出现在所在窗口上层。
截图如下:

解决Popup StayOpen=true时,永远置顶的问题的更多相关文章
- css解决无论页面长短footer永远置底
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Popup 解决置顶显示问题
原文:Popup 解决置顶显示问题 前言 Popup显示时会置顶显示.尤其是 Popup设置了StayOpen=true时,会一直置顶显示,问题更明显. 置顶显示问题现象: 解决方案 怎么解决问题? ...
- WPF popup置顶
在程序写一个popup发现他会在置顶,在网上找了两大神代码http://www.cnblogs.com/Leaco/p/3164394.html http://blog.csdn.net/baijin ...
- 解决popup不随着window一起移动的问题
原文:解决popup不随着window一起移动的问题 当我们设置Popup的StayOpen="True"时,会发现移动窗体或者改变窗体的Size的时候,Popup并不会跟随着一起 ...
- 窗口置顶 - 仿TopWind
前置学习:低级鼠标hook,获得鼠标状态. 这个在原来获得鼠标状态的基础上,加上一个事件处理即可. TopWind就是一个可以置顶窗口的文件,避免复制粘贴的时候的来回切换(大窗口与小窗口),算是一个实 ...
- vc 使窗口置顶 在最前面
bool SetWindowTop(CWnd* pWnd){ if(!pWnd) { return false; } if(pWnd->GetExStyle()&WS_EX_TOPM ...
- 2018-5-28-WPF-popup置顶
title author date CreateTime categories WPF popup置顶 lindexi 2018-05-28 09:58:53 +0800 2018-2-13 17:2 ...
- 让WPF的Popup不总置顶的解决方案
使用WPF的Popup的时候会发现有一个问题,它总是会置顶,只要Popup的StayOpen不设置为False,它就一直呆在最顶端,挡住其他的窗口. 解决方案是继承Popup重新定义控件PopupEx ...
- WPF Popup 置顶问题
原文 WPF Popup 置顶问题 问题: 使用wpf的popup,当在popup中弹出MessageBox或者打开对话框的时候,popup总是置顶,并遮住MessageBox或对话框. 解决: 写如 ...
随机推荐
- mysql 查询select语句汇总
数据准备: 创建表: create table students( id int unsigned primary key auto_increment not null, name varchar( ...
- CentOS7配置php7.0支持redis
配置之前应该是环境已经搭好了,phpinfo的页面可以加载出来. 使用git clone下载git上的phpredis扩展包 [root@VM_103_117_centos ]#git clone ...
- 关于block的循环引用的问题
在block的循环引用的问题上我们都知道如果在block内部修改外部的变量的时候,要加__block以防止循环引用的问题,但是如果block是当前对象的一个属性的时候,要修改当前对象的一个属性的时候就 ...
- python——常用模块
python--常用模块 1 什么是模块: 模块就是py文件 2 import time #导入时间模块 在Python中,通常有这三种方式来表示时间:时间戳.元组(struct_time).格式化的 ...
- Android:认识R类、findViewById方法查找组件、@string查找字符、@color查找颜色、@drawable查找图片、@dimen某个组件尺寸定义、项目引入资源国际化
导入 之前都是断断续续的看了一些于如何使用android开发的文章.资料等,到目前位置很多基础的东西都不清楚,于是去学习了别人的课程,才了认识了R类.findViewById方法查找组件.项目引入资源 ...
- issubclass判断前面是不是后面的子类
issubclass(sub,sup) 判断前面是不是后面的子类
- supervisor安装使用和我踩过的坑
什么是supervisor: supervisor是一款用python编写的进程管理工具,主要运行于linux系统,不支持windows系统,目前还不能运行于python3下. step1:安装sup ...
- Ubuntu+vscode打不开
前沿: vscode链接 参考链接 问题: 之前在Ubuntu上安装chrome, 结果chrome没装成功, 还把vscode给qiu坏了, 貌似是当时安装chrome时提示要升级一个包. 后来发现 ...
- 学习React系列(二)——深入了解JSX
1.JX实际上是React.createElement(component,props,...children)的语法糖 2.JSX判断是否为react组件的依据是标签首字母为大写(所以要求用户自定义 ...
- 前端之旅HTML与CSS篇之block与inline的区别
display:block;和display:inline;的区别block元素特点: 1)处于常规流中时,如果width没有设置,会自动填充满父容器 2)可以应用margin/padding 3)在 ...