HitTest across Windows?
Asked Answered
T

1

7

Ok, so my previous question did not produce any useful answers, so I'll try to come from a different direction.

My application has, potentially, several windows. Given a point in screen coordinates, I need to find which window it "falls" onto - i.e. find the Window that is foremost of all windows containing said point.

If they were Visuals inside one window, I would use VisualTreeHelper.HitTest. But since they are different windows, it's not clear what to give as the first argument to that method.

Transparent answered 13/8, 2010 at 1:40 Comment(0)
A
8

This is not possible using pure WPF, as WPF does not expose the Z Order of its windows. In fact, WPF works hard to maintain the illusion that windows never actually obscure one another.

If you're willing make Win32 calls, the solution is simple:

public Window FindWindowAt(Point screenPoint)  // WPF units (96dpi), not device units
{
  return (
    from win in SortWindowsTopToBottom(Application.Current.Windows.OfType<Window>())
    where new Rect(win.Left, win.Top, win.Width, win.Height).Contains(screenPoint)
    select win
  ).FirstOrDefault();
}

public static IEnumerable<Window> SortWindowsTopToBottom(IEnumerable<Window> unsorted) 
{ 
  var byHandle = unsorted.ToDictionary(win => 
    ((HwndSource)PresentationSource.FromVisual(win)).Handle); 

  for(IntPtr hWnd = GetTopWindow(IntPtr.Zero); hWnd!=IntPtr.Zero; hWnd = GetWindow(hWnd, GW_HWNDNEXT))
    if(byHandle.ContainsKey(hWnd)) 
      yield return byHandle[hWnd]; 
} 

const uint GW_HWNDNEXT = 2; 
[DllImport("User32")] static extern IntPtr GetTopWindow(IntPtr hWnd); 
[DllImport("User32")] static extern IntPtr GetWindow(IntPtr hWnd, uint wCmd); 

If your windows may be transparent you should also use VisualTreeHelper.HitTest in the "where" clause of FindWindowAt().

Anthracnose answered 13/8, 2010 at 4:43 Comment(2)
Thank you very much for your answer. I will accept it, as it is the only answer so far, and it definitely works, if only at the expense of requiring Full Trust :-( One question remains, however: why exactly are you sure that WPF does not allow this?Transparent
Any Win32 application can change Z Order using SetWindowPos. There are two ways WPF could receive this Z Order information from Win32: GetTopWindow/GetNextWindow as shown above, or by handling WM_WINDOWPOSCHANGED and storing state. There are no references to GetTopWindow in the WPF assemblies. Inspecting objects in the debugger shows that WPF doesn't store Z Order information either. WPF can't tell you what it doesn't know itself.Anthracnose

© 2022 - 2024 — McMap. All rights reserved.