The WPF Popup control is always “topmost” over other application windows. If you’re happy with a dirty workaround to remove the Topmost state, you can derive your own Popup control similar to this:
public class PopupNonTopmost : Popup { protected override void OnOpened( EventArgs e ) { var hwnd = ( (HwndSource)PresentationSource.FromVisual( this.Child ) ).Handle; RECT rect; if ( GetWindowRect( hwnd, out rect ) ) { SetWindowPos( hwnd, -2, rect.Left, rect.Top, (int)this.Width, (int)this.Height, 0 ); } } #region P/Invoke imports & definitions [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", EntryPoint = "SetWindowPos" )] private static extern int SetWindowPos( IntPtr hWnd, int hwndInsertAfter, int x, int y, int cx, int cy, int wFlags ); #endregion }
This is based on code by digitalnetbizz, with the addition of calling GetWindowRect for the current window position. Obviously it’d be nicer if Popup had a Topmost DependencyProperty you could use. Here’s my attempt which borrows the Window element’s TopmostProperty:
public class PopupNonTopmost : Popup { public static DependencyProperty TopmostProperty = Window.TopmostProperty.AddOwner( typeof( PopupNonTopmost ), new FrameworkPropertyMetadata( false, OnTopmostChanged ) ); public bool Topmost { get { return (bool)GetValue( TopmostProperty ); } set { SetValue( TopmostProperty, value ); } } private static void OnTopmostChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e ) { ( obj as PopupNonTopmost ).UpdateWindow(); } protected override void OnOpened( EventArgs e ) { UpdateWindow(); } private void UpdateWindow() { var hwnd = ( (HwndSource)PresentationSource.FromVisual( this.Child ) ).Handle; RECT rect; if ( GetWindowRect( hwnd, out rect ) ) { SetWindowPos( hwnd, Topmost ? -1 : -2, rect.Left, rect.Top, (int)this.Width, (int)this.Height, 0 ); } } #region P/Invoke imports & definitions [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", EntryPoint = "SetWindowPos" )] private static extern int SetWindowPos( IntPtr hWnd, int hwndInsertAfter, int x, int y, int cx, int cy, int wFlags ); #endregion }
I’ve not tested this extensively (it compiled
), so just let me know if it’s not happy! Likewise if I’ve missed a glaringly obvious shiny new Topmost property in WPF, let me know


21 comments
Comments feed for this article
October 17, 2008 at 5:18 am
Dave Capps
The code works well … except … when you set the popup to non-topmost it resets all the child elements to non-topmost as well. Which wouldn’t seem like a problem, but Combobox controls rely on having a topmost popup for when you drop them down, so any comboboxs inside your popup break.
I’ve tried iterating through all visual children of the popup and setting combobox popups to be topmost again, but when I do it seems to set the owner popup back to be topmost. Which is really bizarre, I can’t see why that would happen.
Any ideas…?
October 17, 2008 at 6:36 am
Chris Cavanagh
Dave – That’s definitely not what I’d expect to happen
I’ll take a closer look and update here if I find anything…
December 22, 2008 at 2:24 pm
brian
In your call to SetWindowPos, I think the -1 and -2 should be reversed.
SetWindowPos( hwnd, Topmost ? -1 : -2,
should be
SetWindowPos( hwnd, Topmost ? -2 : -1,
June 14, 2009 at 4:54 pm
Joe Gershgorin
This solution almost got me there. It traded one problem for another. I have a draggable Popup, and with the above code it’s always under other windows, even if the popup has focus.
I came up with solution that I’m mostly happy with. To make it ideal I really wish the GotFocus/LostFocus events of the popup control behaved/fired as described.
My solution, sets the PopUp OnTopMost on leftbuttondown, and sets it back to the istopmost property when another window gets focus. I find out if a another window gets focus by hooking into the PopUp’s parent window deactivated event. To make sure the deactivated event gets fired I first have to activate it on mouse leftbuttondown, which means on the parent window comes to front also (the only possible downside, but no biggie for me).
The side effect of this solution is that it should also solve Dave’s Combobox issue.
Here’s my solution:
public class BetterPopup : Popup
{
public static readonly DependencyProperty IsTopmostProperty =
DependencyProperty.Register(
“IsTopmost”,
typeof(bool),
typeof(BetterPopup),
new FrameworkPropertyMetadata(false, OnIsTopmostChanged));
private bool? _appliedTopMost;
public bool IsTopmost
{
get { return (bool)GetValue(IsTopmostProperty); }
set { SetValue(IsTopmostProperty, value); }
}
private bool _alreadyLoaded;
public BetterPopup()
{
this.Loaded += new RoutedEventHandler(PopupNonTopmost_Loaded);
}
private Window _parentWindow;
void PopupNonTopmost_Loaded(object sender, RoutedEventArgs e)
{
if (!_alreadyLoaded)
{
_alreadyLoaded = true;
if (this.Child != null)
{
this.Child.AddHandler(UIElement.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(Child_PreviewMouseLeftButtonDown), true);
}
_parentWindow = Window.GetWindow(this);
if (_parentWindow != null)
{
_parentWindow.Activated += new EventHandler(_parentWindow_Activated);
_parentWindow.Deactivated += new EventHandler(ParentWindow_Deactivated);
}
}
}
void _parentWindow_Activated(object sender, EventArgs e)
{
Console.WriteLine(“Parent Window Activated”);
SetTopmostState(true);
}
void ParentWindow_Deactivated(object sender, EventArgs e)
{
Console.WriteLine(“Parent Window Deactivated”);
if (IsTopmost == false)
{
SetTopmostState(IsTopmost);
}
}
void Child_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Console.WriteLine(“Child Mouse Left Button Down”);
SetTopmostState(true);
if (!_parentWindow.IsActive && IsTopmost == false)
{
_parentWindow.Activate();
Console.WriteLine(“Activating Parent from child Left Button Down”);
}
}
private static void OnIsTopmostChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var thisobj = obj as BetterPopup;
thisobj.SetTopmostState(thisobj.IsTopmost);
}
protected override void OnOpened(EventArgs e)
{
SetTopmostState(IsTopmost);
}
private void SetTopmostState(bool isTop)
{
// Don’t apply state if it’s the same as incoming state
if (_appliedTopMost.HasValue && _appliedTopMost == isTop)
{
return;
}
if (this.Child != null)
{
var hwndSource = (PresentationSource.FromVisual(this.Child)) as HwndSource;
if (hwndSource != null)
{
var hwnd = hwndSource.Handle;
RECT rect;
if (GetWindowRect(hwnd, out rect))
{
Console.WriteLine(“setting z-order ” + isTop);
if (isTop)
{
SetWindowPos(hwnd, HWND_TOPMOST, rect.Left, rect.Top, (int)this.Width, (int)this.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)this.Width, (int)this.Height, TOPMOST_FLAGS);
SetWindowPos(hwnd, HWND_TOP, rect.Left, rect.Top, (int)this.Width, (int)this.Height, TOPMOST_FLAGS);
SetWindowPos(hwnd, HWND_NOTOPMOST, rect.Left, rect.Top, (int)this.Width, (int)this.Height, TOPMOST_FLAGS );
}
_appliedTopMost = isTop;
}
}
}
}
#region P/Invoke imports & definitions
[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);
const UInt32 SWP_NOSIZE = 0×0001;
const UInt32 SWP_NOMOVE = 0×0002;
const UInt32 SWP_NOZORDER = 0×0004;
const UInt32 SWP_NOREDRAW = 0×0008;
const UInt32 SWP_NOACTIVATE = 0×0010;
const UInt32 SWP_FRAMECHANGED = 0×0020; /* The frame changed: send WM_NCCALCSIZE */
const UInt32 SWP_SHOWWINDOW = 0×0040;
const UInt32 SWP_HIDEWINDOW = 0×0080;
const UInt32 SWP_NOCOPYBITS = 0×0100;
const UInt32 SWP_NOOWNERZORDER = 0×0200; /* Don’t do owner Z ordering */
const UInt32 SWP_NOSENDCHANGING = 0×0400; /* Don’t send WM_WINDOWPOSCHANGING */
const UInt32 TOPMOST_FLAGS = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING;
#endregion
}
June 14, 2009 at 10:57 pm
Chris Cavanagh
Joe – Thanks for figuring this out and taking the time to comment! It’s much appreciated
June 29, 2009 at 5:17 am
Varun
This approach works fine however when I set some margin or Corner Radius to my popup window then it shows ugly black border around the popup window.
June 30, 2009 at 3:32 pm
Joe Gershgorin
Thanks for the original posting Chris.
Varun, do you have the AllowsTransparency property set to true?:
http://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.popup.allowstransparency.aspx
September 13, 2009 at 9:48 pm
wpfbabu
hi Chris,
i very happy that the above code helps me a lot to make my PopUp non-topmost ,if u don’t mind could you also make a code for the popup minimization in wpf
where there is no proper answer in any of the websites so far..
September 13, 2009 at 11:23 pm
Chris Cavanagh
wpfbabu – This page on pinvoke.net might help: http://www.pinvoke.net/default.aspx/user32/ShowWindow.html
Once you get the window handle…:
var hwnd = ( (HwndSource)PresentationSource.FromVisual( this.Child ) ).Handle;
You should be able to call ShowWindow with WindowShowStyle.ShowMinimized:
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
…
ShowWindow( hwnd, WindowShowStyle.ShowMinimized );
Hope this helps!
January 19, 2010 at 3:06 pm
Keoz
Awesome you guys are my heroes I want to be like you when I grow up
January 28, 2010 at 9:42 am
Mark
One minor change to Joe Gershgorin elegant solution. It doesn’t unwire a couple of events and causes a memory leak.
Make sure to add the this.Unloaded event.
public BetterPopup()
{
this.Loaded += new RoutedEventHandler(PopupNonTopmost_Loaded);
this.Unloaded += new RoutedEventHandler(BetterPopup_Unloaded);
}
void BetterPopup_Unloaded(object sender, RoutedEventArgs e)
{
if (_parentWindow != null)
{
_parentWindow.Activated -= new EventHandler(_parentWindow_Activated);
_parentWindow.Deactivated -= new EventHandler(ParentWindow_Deactivated);
}
}
January 28, 2010 at 10:05 am
Chris Cavanagh
Mark – Thanks for spotting that!
October 7, 2010 at 7:06 am
Martin
Hi
This worked very well as an Attached property
public static class PopupBehaviour
{
public static readonly DependencyProperty
TopmostProperty =
DependencyProperty.RegisterAttached(“Topmost”,
typeof(bool),
typeof(PopupBehaviour),
new FrameworkPropertyMetadata
(true,TopmostChanged));
…
<Popup PopupBehaviour.Topmost="False"
November 19, 2010 at 4:23 am
J0hn
Hi,
You may use an ‘Int32Rect’ instead of the ‘RECT’ structure
January 31, 2011 at 1:44 pm
Joshua Zimler
Just a minor comment to add: the OnOpened function should also include a call to the base class. Otherwise, the Opened event will not be fired.
Code looks as follows:
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
SetTopmostState(IsTopmost);
}
April 5, 2011 at 2:11 am
Frank Quednau
Hi,
since the solution in the comment by Joe Gershgorin is not easily digested due to formatting, I have made a gist out of it. It also includes the addendum by Mark regarding the explicit event unregistering.
https://gist.github.com/903202
January 5, 2012 at 5:11 am
Sven
Very great solution! Thank you very much! I’m opening a FileDialog inside a WPF Popup and that’s a great solution.
January 11, 2012 at 8:43 am
Matt Becker
Works perfectly. I’m using it over a WebBrowser control to simulate changing it’s opacity. Thanks!
February 9, 2012 at 9:58 am
Ivan
Also, you don’t need to specify x, y, cx and cy if you use SWP_NOSIZE and SWP_NOMOVE flags for SetWindowPos
July 26, 2012 at 9:09 am
Anonimi
Hi,
You can set the property “StaysOpen” to false.
November 30, 2012 at 4:17 am
Laurence Bunnage
Thanks guys
I had same problem and would never have solved it without this thread.