I have a NativeWindow
, I've overridden the WndProc
function to process WM_WINDOWPOSCHANGING message for when moving my window I'll adhere it to the borders of the nearest window in the desktop.
The problem I have is that my window is docking to windows that are in the background of other top-windows, for example if I have an explorer window open and other window below the explorer window, then my window can dock to the window that is below of the other window, which is a z-order level lower than the explorer window. I want to avoid this.
A demostration of the problem:
In the GIF above, there is my window (Form1), the Visual Studio IDE window, an explorer window, and a window of an application with name "Hot Corners". When I send the "Hot Corners" window to background, I still can adhere my window to "Hot corners" borders. I want to avoid this.
Note the debug info in the captured Output window of Visual Studio.
I read about Z-Ordering on Wikipedia, and I also seen this example and the MSDN docs here and here, however, I still not understand how to achieve this.
When I attempt to adhere my window to other window, I need to determine if that target window is below other windows or isnt, to avoid the issue that I explained.
I hope that I explained good the problem and is clear what I need, in the GIF above my window shouldn't adhere to the "Hot Corners" window because is not visible, because the explorer window is above.
This is the relevant code, the method takes as arguments my window (a Form
), the handle to a WINDOWPOS struct that I get when filtering the WM_WINDOWPOSCHANGING
message in the WndProc
procedure of my window, and the last parameter, threshold
, is the minimum space required between the bounds of my window to other windows to adhere it.
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
Note that in the code above I only shown the threatment to adhere the right border of my window to other windows, this is to avoid increasing the code for this question, and the same reason for the missing P/Invokes.
You can use the GetTopWindow function to search all child windows of a parent window and return a handle to the child window that is highest in z-order. The GetNextWindow function retrieves a handle to the next or previous window in z-order.
HWND data types are "Handles to a Window", and are used to keep track of the various objects that appear on the screen. To communicate with a particular window, you need to have a copy of the window's handle. HWND variables are usually prefixed with the letters "hwnd", just so the programmer knows they are important.
An overlapped window is a top-level window (non-child window) that has a title bar, border, and client area; it is meant to serve as an application's main window. It can also have a window menu, minimize and maximize buttons, and scroll bars.
Given a window handle, you should be able to determine whether the window is fully or partially obscured by other windows using a few Win32 functions:
Call GetWindowDC()
to retrieve a device context handle (HDC
) that includes the entire window, including the non-client areas (e.g., title bar, menus, borders, etc.)
Call GetClipBox()
with the HDC
returned above. This will return the smallest bounding rectangle that is actually visible (i.e., on screen and not covered by other windows). Also, the return value can tell you whether the window is completely obscured (NULLREGION
).
Don't forget to call ReleaseDC()
.
API reference: https://msdn.microsoft.com/en-us/library/dd144865%28v=vs.85%29.aspx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With