万古长存置顶难点的Popup控件必赢体育

作者:必赢体育官方

As Marco Zhou has said in the msdn forum ():
Windows will only send WM_IME_SETCONTEXT message to the active window, Popup by default is designed to be shown with WM_EX_NOACTIVE which means that it doesn't in active state when displaying, that's why IME could not work correctly in this regard, to workaround this issue, you could try set the Popup as the active window using win32 SetActiveWindow() API.

/// <summary>
/// 解决StayOpen=true时,永远置顶问题的Popup控件
/// </summary>
public class EasiNotePopup : Popup
{
public static readonly DependencyProperty IsTopmostProperty = DependencyProperty.Register("IsTopmost", typeof(bool), typeof(EasiNotePopup), new FrameworkPropertyMetadata(false, OnIsTopmostChanged));

So the workaround is use MyPopup instead of Popup:

private bool? _appliedTopMost;
private bool _alreadyLoaded;
private Window _parentWindow;

public class MyPopup : Popup
{
    [DllImport("user32.dll")]
    static extern IntPtr SetActiveWindow(IntPtr hWnd);

public bool IsTopmost
{
get { return (bool)GetValue(IsTopmostProperty); }
set { SetValue(IsTopmostProperty, value); }
}

    static MyPopup()
    {
        EventManager.RegisterClassHandler(
            typeof(MyPopup),
            Popup.PreviewGotKeyboardFocusEvent,
            new KeyboardFocusChangedEventHandler(OnPreviewGotKeyboardFocus),
            true);
    }

/// <summary>
/// ctor
/// </summary>
public EasiNotePopup()
{
Loaded += OnPopupLoaded;
Unloaded += OnPopupUnloaded;
}

    private static void OnPreviewGotKeyboardFocus(Object sender, KeyboardFocusChangedEventArgs e)
    {
        var textBox = e.NewFocus as TextBoxBase;
        if (textBox != null)
        {
            var hwndSource = PresentationSource.FromVisual(textBox) as HwndSource;
            if (hwndSource != null)
            {
                SetActiveWindow(hwndSource.Handle);
            }
        }
    }
}

void OnPopupLoaded(object sender, RoutedEventArgs e)
{
if (_alreadyLoaded)
return;

官方解决方案

_alreadyLoaded = true;

使用的话自己建一个类代码如上,控件内local:MyPopup 等同于Popup,亲测好使,实用。

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 = (EasiNotePopup)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(-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 */

//很重要,窗口切换等需要将popup显示层级重新刷新
const UInt32 TOPMOST_FLAGS =
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING;
}

本文由必赢体育发布,转载请注明来源

关键词: