tags:

views:

330

answers:

3

Hi everyone

I have a WPF Window, and I want to determine when a user finishes moving a Window around on the desktop. I hooked up to the LocationChanged event and that's fine, but I can't figure out how to determine when the user stops moving the window (by releasing the left mouse button).

There's no event to help me determine that, something like a LocationChangedEnded event. I tried hooking up to MouseLeftButtonUp but that event is never fired.

Anyone has any ideas?

+1  A: 

Two possible approaches would be:

  1. You don't really know when the mouse button is raised. Instead, you wait for the window to stop sending those move events. Set up a short lived timer that starts ticking every time you receive a window move event. Reset the timer if it's already on. When you receive the timer event, e.g. after a few hundred millisecs, you could assume the user stopped moving the window. Even with a high resolution mouse, when holding down the left mouse button and trying to stay still, the jitter will keep sending move events. This approach is documented here.

  2. Attempt to capture mouse notifications from the non-client area of the window. You could set up a window message hook to capture window messages. Once the first window move event is seen, the hook could start looking for WM_NCLBUTTONUP events. This approach avoids the timer and the guessing. However, it makes assumptions about the ways Windows allows the user to position windows, and may fail in some cases, e.g. if the user moves the user with the keyboard only (Alt+Space, M, arrow keys).

Oren Trutner
A: 

You want to get the WM_WINDOWPOSCHANGED message, add this to your Window class:

internal enum WM
{
   WINDOWPOSCHANGING = 0x0047,
}

[StructLayout(LayoutKind.Sequential)]
internal struct WINDOWPOS
{
   public IntPtr hwnd;
   public IntPtr hwndInsertAfter;
   public int x;
   public int y;
   public int cx;
   public int cy;
   public int flags;
}

private override void OnSourceInitialized(EventArgs ea)
{
   HwndSource hwndSource = (HwndSource)HwndSource.FromVisual((Window)this);
   hwndSource.AddHook(DragHook);
}

private static IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled)
{
   switch ((WM)msg)
   {
      case WM.WINDOWPOSCHANGED:
      {
          WINDOWPOS pos = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));
          if ((pos.flags & (int)SWP.NOMOVE) != 0)
          {
              return IntPtr.Zero;
          }

          Window wnd = (Window)HwndSource.FromHwnd(hwnd).RootVisual;
          if (wnd == null)
          {
             return IntPtr.Zero;
          }

          // ** do whatever you need here **
          // the new window position is in the pos variable
          // just note that those are in Win32 "screen coordinates" not WPF device independent pixels

       }
       break;
   }

   return IntPtr.Zero;
}
Nir
A: 

You can listen for the WM_ENTERSIZEMOVE event, which should only fire when the move is started. While the user is dragging you may receive WM_MOVING and WM_MOVE events. The latter depends on the their system settings (e.g. the window moves as they drag, versus just dragging an outline). Finally, WM_EXITSIZEMOVE would indicate when they are done.

Tom Goff