模拟拖动窗口标题栏的控件

7

我建立了一个自定义控件,想让人们像拖动窗口标题栏一样在我的控件上单击并拖动。最好的方法是什么?

到目前为止,我未能利用鼠标按下、放开和移动事件来解析何时需要移动窗口。

3个回答

11

除了我的其他答案,您可以在控件中手动执行以下操作:

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;
    }
}

编辑:已测试并修复 - 现在可以正常工作。


5

最有效的方法是处理 WM_NCHITTEST 通知。

覆盖窗体的 WndProc 方法并添加以下代码:

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
}

然而,如果在那个点上有一个控件,我认为消息将不会被发送。

我很感兴趣...你能详细说明一下吗?这个到底是如何完成所要求的任务的? - echo
@echo: 它告诉Windows,该窗体的该部分是标题栏,可以拖动。请注意,这也将允许通过双击该部分最大化窗体。 - SLaks
哈哈,巧妙的技巧,我以前从未见过这样的。花了我一分钟才弄清楚这里正在发生什么! - Aaronaught

4
如果您想让表单的一部分像标题一样工作,那么SLaks提供的WM_NCHITTEST技巧是正确的方法。但是,如果您想让一个子窗口能够拖动表单,则有另一种方法。
基本上,如果您向DefWindowProc发送一个带有MOUSE_MOVE命令ID的WM_SYSCOMMAND消息,则Windows将进入拖动模式。这基本上就是标题如何实现的,但是通过去掉中间人,我们可以从子窗口启动此拖动,并且我们不会得到所有其他标题行为。
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.
  }

1
这对我有用,但我必须更改ReleaseCapture以不带参数。请参见ReleaseCapture - Erroneous

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接