tags:

views:

1866

answers:

4

Hi everyone, just curious if you know of any way to setup a drag boundary for a window?

It would be nice to have these properties:

Me.MinLeft = 10
Me.MinTop = 10
Me.MaxLeft = 150
Me.MaxTop = 150

Those are made up properties, btw, which would be nice to have.

I know I could probably setup a timer to fire ever 10th of a second and check the left and top and then move it back if it's over. But it would be more elegant to have the window act like it hit a wall and can't go any farther, like moving to the edge of the screen or something similar.

Edit: There seems to be some confusion somewhere, the point I'm trying to make is in the paragraph above, dragging, not re-sizing.

A: 

There are dependency properties for WPF's Window for this purpose.

Here they are:

  • Window.MaxWidth
  • Window.MaxHeight

These properties will constrain the size of the Window, just like the WinForm's Form.

eriawan
I'm speaking of dragging a window around the screen, not re-sizing it. Think of setting a region on the screen the window is only allowed to move round in, even though your screen is bigger. Like a drag container is best I can think of.
ScottN
I'm sitting here reading this again and wondering if I asked the question wrong and how you could think I was speaking of window size and not dragging, I don't even mention the word "size" or "re-size" in my question...
ScottN
Drag boundary? I think it's by default is draggable. Do you want drag handle like the ones in the Windows Forms?
eriawan
He wants to constraint the area within which a window can be dragged.
Drew Noakes
A: 

Maybe you could handle PreviewMouseMove (either the event or override the corresponding protected method) and set e.Handled = true whenever the mouse movement would cause the window to move outside the region you want to constrain it to.

This seems like the most logical, WPF-like way of doing this.

Drew Noakes
+1  A: 

Here is teh "magic" you need to create this functionality, all you have to do is set the Window_SourceInitialized method to the window's SourceInitialized event and insert you logic where the big comment is.

I combined this code from several sources, so there could be some syntax errors in it.

internal enum WM
{
   WINDOWPOSCHANGING = 0x0046,
}

[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 void Window_SourceInitialized(object sender, EventArgs ea)
{
   HwndSource hwndSource = (HwndSource)HwndSource.FromVisual((Window)sender);
   hwndSource.AddHook(DragHook);
}

private static IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled)
{
   switch ((WM)msg)
   {
      case WM.WINDOWPOSCHANGING:
      {
          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;
          }

          bool changedPos = false;

          // ***********************
          // Here you check the values inside the pos structure
          // if you want to override tehm just change the pos
          // structure and set changedPos to true
          // ***********************

          if (!changedPos)
          {
             return IntPtr.Zero;
          }

          Marshal.StructureToPtr(pos, lParam, true);
          handeled = true;
       }
       break;
   }

   return IntPtr.Zero;
}
Nir
+1  A: 

As I have no doubt that Nir's answer will work spending a little time implementing it, I was able to do what I wanted a little bit more elegant with this code:

Private Sub myWindow_LocationChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LocationChanged

    Dim primaryBounds As System.Drawing.Rectangle = Windows.Forms.Screen.PrimaryScreen.Bounds
    Dim windowBounds As System.Drawing.Rectangle = New System.Drawing.Rectangle(CInt(Me.Left), CInt(Me.Top), CInt(Me.Width), CInt(Me.Height))

    If (windowBounds.Left < 0) Then
        windowBounds = New System.Drawing.Rectangle(0, windowBounds.Top, windowBounds.Width, windowBounds.Height)
    ElseIf (windowBounds.Right > primaryBounds.Right) Then
        windowBounds = New System.Drawing.Rectangle(primaryBounds.Right - windowBounds.Width, windowBounds.Top, windowBounds.Width, windowBounds.Height)
    End If

    If (windowBounds.Top < 0) Then
        windowBounds = New System.Drawing.Rectangle(windowBounds.Left, 0, windowBounds.Width, windowBounds.Height)
    ElseIf (windowBounds.Bottom > primaryBounds.Bottom) Then
        windowBounds = New System.Drawing.Rectangle(windowBounds.Left, primaryBounds.Bottom - windowBounds.Height, windowBounds.Width, windowBounds.Height)
    End If

    Me.Left = windowBounds.Left
    Me.Top = windowBounds.Top

End Sub

This made the window being dragged stay within the primary screen (whole window), but you could easily change the bounds to whatever values you needed.

ScottN