【发布时间】:2016-03-05 16:42:36
【问题描述】:
我有一个 NativeWindow,我已经覆盖了 WndProc 函数来处理 WM_WINDOWPOSCHANGING 消息,以便在移动我的窗口时我会将其粘贴到最近的窗口的边框上桌面。
我遇到的问题是我的窗口停靠在其他顶部窗口的背景中的窗口上,例如,如果我打开了一个资源管理器窗口并且在资源管理器窗口下方有其他窗口,那么我的窗口可以停靠到位于另一个窗口下方的窗口,该窗口的 z 顺序级别低于资源管理器窗口。我想避免这种情况。
问题的演示:
在上面的 GIF 中,有我的窗口 (Form1)、Visual Studio IDE 窗口、资源管理器窗口和名为“Hot Corners”的应用程序窗口。当我将“Hot Corners”窗口发送到背景时,我仍然可以将我的窗口粘附到“Hot Corners”边框。我想避免这种情况。
注意在 Visual Studio 的捕获输出窗口中的调试信息。
我在 Wikipedia 上阅读了有关 Z-Ordering 的信息,还看到了 this 示例和 MSDN 文档 here 和 here,但是,我仍然不明白如何实现这一点。
当我尝试将我的窗口粘附到其他窗口时,我需要确定该目标窗口是否低于其他窗口,以避免我解释的问题。
我希望我能很好地解释问题并清楚我需要什么,在我的窗口上方的 GIF 中不应该坚持“热角”窗口,因为它不可见,因为资源管理器窗口在上方。
这是相关代码,该方法将我的窗口(@987654332@)作为参数,我在过滤 WM_WINDOWPOSCHANGING 消息时获得的 WINDOWPOS 结构的句柄我的窗口的WndProc 过程,最后一个参数threshold 是我的窗口的边界与其他窗口之间的最小空间。
Protected Overridable Sub DockToNearestWindowBorder(ByVal window As IWin32Window,
ByVal windowPosHandle As IntPtr,
ByVal threshold As Integer)
Dim windowPos As WindowPos =
CType(Marshal.PtrToStructure(windowPosHandle, GetType(WindowPos)), WindowPos)
If (windowPos.Y = 0) OrElse (windowPos.X = 0) Then
' Nothing to do.
Exit Sub
End If
' Enumerate all the visible windows in the current desktop.
Dim desktopWindows As New List(Of IntPtr)()
Dim callBack As EnumWindowsProc =
Function(hwnd As IntPtr, lParam As IntPtr) As Boolean
If (NativeMethods.IsWindowVisible(hwnd)) Then
desktopWindows.Add(hwnd)
End If
Return True
End Function
If (NativeMethods.EnumDesktopWindows(IntPtr.Zero, callBack, IntPtr.Zero)) Then
' Window rectangles
Dim srcRect As Rectangle
Dim tgtRect As Rectangle
NativeMethods.GetWindowRect(window.Handle, srcRect)
For Each hwnd As IntPtr In desktopWindows
' This is just for testing purposes.
Dim pid As Integer
NativeMethods.GetWindowThreadProcessId(hwnd, pid)
If Process.GetProcessById(pid).ProcessName.EndsWith("vshost") Then
Continue For
End If
NativeMethods.GetWindowRect(hwnd, tgtRect)
' Right border of the source window
If ((windowPos.X + srcRect.Width) <= (tgtRect.Left + threshold)) AndAlso
((windowPos.X + srcRect.Width) >= (tgtRect.Left - threshold)) AndAlso
((windowPos.Y) <= (tgtRect.Y + tgtRect.Height)) AndAlso
((windowPos.Y + srcRect.Height) >= (tgtRect.Y)) Then
windowPos.X = (tgtRect.Left - srcRect.Width)
Console.WriteLine("Window adhered to: " & Process.GetProcessById(pid).ProcessName)
' This is not working as expected.
' If hwnd = NativeMethods.GetWindow(window.Handle, GetWindowCmd.HwndNext) Then
' windowPos.X = (tgtRect.Left - srcRect.Width)
' Exit For
' End If
End If
Next hwnd
End If
' Marshal it back.
Marshal.StructureToPtr(structure:=windowPos, ptr:=windowPosHandle, fDeleteOld:=True)
End Sub
请注意,在上面的代码中,我只显示了将窗口的右边框粘附到其他窗口的威胁,这是为了避免增加此问题的代码,以及缺少 P/Invokes 的相同原因.
【问题讨论】:
-
EnumWindows 出了什么问题?
-
@David Heffernan 谢谢,它帮助了我,但我最好使用 EnumDesktopWindows func。但是,我发现了一个大问题,就是我正在获取位于其他窗口背景中的窗口,因此无论目标窗口是否在其他窗口的背景中,我的窗口都停靠到任何窗口,知道该怎么做过滤以了解哪些窗口仅比我的窗口低一个“前景级别”?例如,如果桌面上有 2 个资源管理器窗口,一个在另一个之上,我只想威胁上面的窗口。我会用我的进度更新这个问题。谢谢评论。
-
我不太明白。你的问题是关于执行速度的。现在您似乎更关心如何处理列表,这似乎有点令人困惑。您能否缩小问题的重点。大概 EnumDesktopWindows 更好,因为您只希望活动桌面上的窗口。
-
使用WindowFromPoint 找出特定坐标处的可见窗口。
-
如果您有两个窗口不 相互重叠,例如一个在桌面的左侧,另一个在桌面的右侧,情况会怎样。您是否希望能够将您的表单停靠到两者或具有更高 z-order 的一个?
标签: .net vb.net winapi window z-order