Skip to content

Instantly share code, notes, and snippets.

@jkhk
Forked from flq/NonTopmostPopup.cs
Created July 5, 2012 11:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jkhk/3053290 to your computer and use it in GitHub Desktop.
Save jkhk/3053290 to your computer and use it in GitHub Desktop.
WPF: Popup that is only topmost with respect to parent window. Taken from the comments at http://chriscavanagh.wordpress.com/2008/08/13/non-topmost-wpf-popup/ (Joe Gershgorin) which was in not easy to digest state. Specific for popup and Windows Forms hos
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Forms;
using System.Windows.Forms.Integration;
using System.Windows.Input;
using System.Windows.Interop;
namespace Honeywell.Desktop.TimeControl
{
public class PopupEx : Popup
{
public static readonly DependencyProperty IsTopmostProperty =
DependencyProperty.Register("IsTopmost",
typeof(bool),
typeof(PopupEx),
new FrameworkPropertyMetadata(false, OnIsTopmostChanged));
public bool IsTopmost
{
get { return (bool)GetValue(IsTopmostProperty); }
set { SetValue(IsTopmostProperty, value); }
}
private bool? _appliedTopMost;
private bool _alreadyLoaded;
private Form _parentForm;
public PopupEx()
{
Loaded += PopupNonTopmostLoaded;
Unloaded += PopupNonTopmostUnloaded;
}
private void PopupNonTopmostUnloaded(object sender, RoutedEventArgs e)
{
if (_parentForm == null)
{
return;
}
_parentForm.Activated -= OnParentWindowActivated;
_parentForm.Deactivate -= OnParentWindowDeactivated;
}
void PopupNonTopmostLoaded(object sender, RoutedEventArgs e)
{
if (_alreadyLoaded)
{
return;
}
_alreadyLoaded = true;
if (Child != null)
{
Child.AddHandler(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(ChildPreviewMouseLeftButtonDown), true);
}
var wpfHandle = PresentationSource.FromVisual(this) as HwndSource;
if (wpfHandle == null)
{
return;
}
var elementHost = Control.FromChildHandle(wpfHandle.Handle) as ElementHost;
if (elementHost == null)
{
return;
}
_parentForm = elementHost.FindForm();
if (_parentForm == null)
{
return;
}
_parentForm.Activated += OnParentWindowActivated;
_parentForm.Deactivate += OnParentWindowDeactivated;
}
private void OnParentWindowDeactivated(object sender, EventArgs e)
{
if (IsTopmost == false)
{
SetTopmostState(IsTopmost);
}
}
private void OnParentWindowActivated(object sender, EventArgs e)
{
SetTopmostState(true);
}
void ChildPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
SetTopmostState(true);
if (!_parentForm.Focused && IsTopmost == false)
{
_parentForm.Activate();
}
}
private static void OnIsTopmostChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var popup = obj as PopupEx;
if (popup != null)
{
popup.SetTopmostState(popup.IsTopmost);
}
}
protected override void OnOpened(EventArgs e)
{
_appliedTopMost = null;
SetTopmostState(IsTopmost);
base.OnOpened(e);
}
private void SetTopmostState(bool isTop)
{
if (_appliedTopMost.HasValue && _appliedTopMost == isTop)
{
// Don’t apply state if it’s the same as incoming state
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, HwndTopmost, rect.Left, rect.Top, (int)Width, (int)Height, TopmostFlags);
}
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, HwndBottom, rect.Left, rect.Top, (int)Width, (int)Height, TopmostFlags);
SetWindowPos(hwnd, HwndTop, rect.Left, rect.Top, (int)Width, (int)Height, TopmostFlags);
SetWindowPos(hwnd, HwndNotopmost, rect.Left, rect.Top, (int)Width, (int)Height, TopmostFlags );
}
_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 HwndTopmost = new IntPtr(-1);
static readonly IntPtr HwndNotopmost = new IntPtr(-2);
static readonly IntPtr HwndTop = new IntPtr(0);
static readonly IntPtr HwndBottom = new IntPtr(1);
const UInt32 SwpNosize = 0x0001;
const UInt32 SwpNomove = 0x0002;
const UInt32 SwpNozorder = 0x0004;
const UInt32 SwpNoredraw = 0x0008;
const UInt32 SwpNoactivate = 0x0010;
const UInt32 SwpFramechanged = 0x0020; /* The frame changed: send WM_NCCALCSIZE */
const UInt32 SwpShowwindow = 0x0040;
const UInt32 SwpHidewindow = 0x0080;
const UInt32 SwpNocopybits = 0x0100;
const UInt32 SwpNoownerzorder = 0x0200; /* Don’t do owner Z ordering */
const UInt32 SwpNosendchanging = 0x0400; /* Don’t send WM_WINDOWPOSCHANGING */
const UInt32 TopmostFlags = SwpNoactivate | SwpNoownerzorder | SwpNosize | SwpNomove | SwpNoredraw | SwpNosendchanging;
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment