views:

56

answers:

3

Alright, so i built myself a control, and id like to make it so when somebody launchs my app, they can drag on my control just as if they were dragging on the title bar of the application. What is the best was to do this?

My experiments so far: Ive played with the mousedown,up,move events, and tried to decifer when the mouse has been moved and such, but i just cant get the form to move with the mouse

+5  A: 

The most effective way to do this is to handle the WM_NCHITTEST notification.

Override the form's WndProc method and add the following code:

if (m.Msg == 0x0084) {              //WM_NCHITTEST
    var point = new Point((int)m.LParam);
    if(someRect.Contains(PointToClient(point))
        m.Result = new IntPtr(2);   //HT_CAPTION
}

However, I don't think the message will be sent if there is a control at that point.

SLaks
I'm intrigued... can you elaborate? How on earth does this do what is being asked?
echo
@echo: It tells Windows that that part of the form is the titlebar, which can be dragged. Note that this will also allow the form to be maximized by double-clicking that part.
SLaks
Hah, neat trick, I've never seen this one before. Took me a minute to figure out what was going on here!
Aaronaught
+4  A: 

In addition to my other answer, you can do this manually in a Control like this:

Point dragOffset;

protected override void OnMouseDown(MouseEventArgs e) {
    base.OnMouseDown(e);
    if (e.Button == MouseButtons.Left) {
        dragOffset = this.PointToScreen(e.Location);
        var formLocation = FindForm().Location;
        dragOffset.X -= formLocation.X;
        dragOffset.Y -= formLocation.Y;
    }
}

protected override void OnMouseMove(MouseEventArgs e) {
    base.OnMouseMove(e);

    if (e.Button == MouseButtons.Left) {
        Point newLocation = this.PointToScreen(e.Location);

        newLocation.X -= dragOffset.X;
        newLocation.Y -= dragOffset.Y;

        FindForm().Location = newLocation;
    }
}

EDIT: Tested and fixed - this now actually works.

SLaks
+2  A: 

If you want to make a part of the form behave like the caption, the WM_NCHITTEST trick that SLaks gave is the way to go. But if you want to make a child window be able to drag the form and there is another way.

Basically if you send a WM_SYSCOMMAND message to DefWindowProc with the MOUSE_MOVE command id, then Windows will go into drag mode. This basically how the caption does it, but by cutting out the middle man, we can initiate this drag from a child window, and we don't get all of the other caption behaviors.

public class form1 : Form 
{
  ...

  [DllImport("user32.dll")]
  static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, UIntPtr wParam, IntPtr lParam);
  [DllImport("user32.dll")]
  static extern bool ReleaseCapture(IntPtr hwnd);

  const uint WM_SYSCOMMAND = 0x112;
  const uint MOUSE_MOVE = 0xF012;      

  public void DragMe()
  {
     DefWindowProc(this.Handle, WM_SYSCOMMAND, (UIntPtr)MOUSE_MOVE, IntPtr.Zero);
  }


  private void button1_MouseDown(object sender, MouseEventArgs e)
  {
     Control ctl = sender as Control;

     // when we get a buttondown message from a button control
     // the button has capture, we need to release capture so
     // or DragMe() won't work.
     ReleaseCapture(ctl.Handle);

     this.DragMe(); // put the form into mousedrag mode.
  }
John Knoeller