在调用本机方法 MoveWindow 期间闪烁
Posted
技术标签:
【中文标题】在调用本机方法 MoveWindow 期间闪烁【英文标题】:Flickering during call to native method MoveWindow 【发布时间】:2021-03-31 16:53:55 【问题描述】:我有一个 WPF 应用程序,它使用 Process.Start() 方法打开一个新进程 (Notepad.exe)。我编写的代码阻止用户移动记事本窗口,其中窗口的 Top 属性不能小于 130 像素。我使用了来自 SO question 的信息,我最终使用以下代码来管理这个动作
这是我在移动目标窗口时定义的 windows 事件委托:
protected void TargetMoved(IntPtr hWinEventHook, Hook.SWEH_Events eventType, IntPtr hWnd, Hook.SWEH_ObjectId idObject, long idChild, uint dwEventThread, uint dwmsEventTime)
if (hWnd == targethWnd &&
eventType == Hook.SWEH_Events.EVENT_OBJECT_LOCATIONCHANGE &&
idObject == (Hook.SWEH_ObjectId)Hook.SWEH_CHILDID_SELF)
rect = Hook.GetWindowRect(hWnd);
int x = rect.Left;
int y = rect.Top;
int width = rect.Right - rect.Left;
int height = rect.Bottom - rect.Top;
if (rect.Left <= 0)
x = 0;
if (rect.Top <= 130)
y = 130;
UnsafeNativeMethods.MoveWindow(targethWnd, x, y, width, height, true);
这段代码工作正常,它使窗口保持在边界内,但有一个烦人的问题:当窗口被拖动到 130 像素标记以上时(当 Top 小于 130 时),窗口在之间来回闪烁我在哪里拖动它,它在哪里停止。
如何消除这种闪烁?
【问题讨论】:
你不能。它在所有 4 个方面都很烦人,由于标题栏,在顶部更加明显。真正的修复需要代码注入来子类化窗口并拦截 WM_MOVING 和 WM_SIZING,C# 不是合适的工具。并不是说任何语言都是这种hackorama的理想工具:) "如何消除这种闪烁?"简短的回答是你可能不能(合理地)。闪烁的发生是因为窗口被移动到新位置,然后你抓住它并将它移回原来的位置,并且像这样快速连续绘制两次会导致闪烁。所以不要那样做。 Notepad.exe 基本上只是一个带有最少包装的 Windows 编辑控件,所以不要使用它。只需创建您自己的包含编辑控件的窗口。在那里您可以处理WM_NCCALCSIZE
以确保窗口的位置/大小而不会闪烁。
【参考方案1】:
当你拖动一个窗口时,WM_WINDOWPOSCHANGING
和WM_WINDOWPOSCHANGED
将被发送到窗口过程,然后重新绘制所做的更改。如果这个窗口过程是你自己创建的,那么你可以处理这两条消息,并将left和top限制为0和130。
case WM_WINDOWPOSCHANGING:
case WM_WINDOWPOSCHANGED:
WINDOWPOS* pos = (PWINDOWPOS)lParam;
if (pos->x <= 0)
pos->x = 0;
if (pos->y <= 130)
pos->y = 130;
return 0;
如果这不是你的窗口过程,你需要SetWindowSubclass
,但是你不能跨线程子类化一个窗口,你还需要使用代码注入。
所以,对于记事本窗口,你不能简单地解决这个问题, 正如评论所说,您可以简单地将编辑控件集成到您自己的窗口中。然后像上面的代码一样处理窗口消息。
【讨论】:
以上是关于在调用本机方法 MoveWindow 期间闪烁的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Windows 的最大化窗口上无缝调用 MoveWindow?
在 FlowLayoutPanel 滚动期间,背景扭曲 + 闪烁