0

我正在尝试通过单击按钮来移动表单(使用标题栏)。

我认为使用 SendMessage 会很简单:

Const WM_LBUTTONDOWN As Integer = &H201

Button1.Capture = False
Cursor.Position = Me.Location + New Size(50, 8)

SendMessage(Me.Handle, WM_LBUTTONDOWN, CType(1, IntPtr), IntPtr.Zero)

但是,尽管如果光标位于表单客户区域中,这会发送消息,但它似乎不会将其发送到表单标题栏(表单以某种方式捕获事件,尽管光标位于标题栏而不是客户区域)。

我已经在按钮上的 mousedown 和 click 事件中尝试了上面的代码,移动光标然后按下 button1。

有什么建议么?

4

3 回答 3

3

您将需要(并传递为)。不过,我仍然不完全确定这会完成你想要做的事情。WM_NCLBUTTONDOWNHTCAPTIONwParam

通常,允许用户在单击标题栏以外的位置时移动表单的方法是处理WM_NCHITTEST消息并HTCAPTION在光标位于您要开始移动的区域上方时返回。但是,如果该区域被子控件占用,您还必须WM_NCHITTEST从子控件处理并返回HTTRANSPARENT


顺便说一句,完成此操作的更简单(如果稍微不那么正确)的方法是按照 Mehrdad Afshari的建议进行操作,并且只需设置表单的Location属性。您对他评论说“它需要在鼠标移动时移动”,而这正是您可以而且应该做的。

class MyForm : Form{
    Point downAt;

    MyForm(){
        Label lbl      = new Label();
        lbl.AutoSize   = true;
        lbl.BackColor  = Color.Blue;
        lbl.ForeColor  = Color.White;
        lbl.Location   = new Point(50, 50);
        lbl.Text       = "Drag me to move this form.";
        lbl.Parent     = this;
        lbl.MouseDown += (s, e)=>downAt = e.Location;
        lbl.MouseMove += (s, e)=>{if(lbl.Capture) Location += (Size)e.Location - (Size)downAt;};
    }
}

这种方法的问题在于它绕过了 Windows 用于移动顶级窗口的代码。这意味着如果用户没有在“显示属性”对话框中选择“拖动时显示窗口内容”选项,这将有效地忽略该设置(它不会显示拖动轮廓)。可能还有其他我没有想到的缺点。

不过,总的来说,这是一种简单易行的实现方式,它是一个完全 .NET 的解决方案,不依赖于任何平台调用(因此它应该可以移植到 Unix 上的 Mono)。


哎呀。我刚刚意识到我给了你 C# 示例代码,但你的代码似乎是 VB.NET。我想你需要的是:

Sub New()
    Dim lbl As New Label
    lbl.AutoSize  = True
    lbl.BackColor = Color.Blue
    lbl.ForeColor = Color.White
    lbl.Location  = New Point(50, 50)
    lbl.Text      = "Drag me to move this form."
    lbl.Parent    = Me
    AddHandler lbl.MouseDown, Function(ByVal s As Object, ByVal e As MouseEventArgs)
        Me.downAt = e.Location
    End Function
    AddHandler lbl.MouseMove, Function(ByVal s As Object, ByVal e As MouseEventArgs)
        If lbl.Capture Then
            Me.Location = Me.Location + DirectCast(e.Location, Size) - DirectCast(Me.downAt, Size)
        End If
    End Function
End Sub

这可能不是在 VB.NET 中表达这一点的最简洁的方式。我使用Reflector来帮助我翻译它。

于 2009-01-11T19:56:38.360 回答
2

消息的LParamwm_LButtonDown接收客户端坐标中的鼠标位置。标题栏在非客户区,所以使用wm_NCLButtonDownmessage。我之前已经看到该消息作为此问题的答案,但我希望有一条更直接的路线:向窗口发送wm_SysCommand消息,并指定sc_Move标志。

于 2009-01-11T20:01:52.860 回答
2

Mehrdad 是对的,没必要这么做。鼠标已被捕获,因此您永远不能太快移动它。示例代码:

Point mLastPos;
private void button1_MouseMove(object sender, MouseEventArgs e) {
  if (e.Button == MouseButtons.Left) {
    this.Location = new Point(this.Location.X + e.X - mLastPos.X,
      this.Location.Y + e.Y - mLastPos.Y);
  }
  // NOTE: else is intentional!
  else mLastPos = e.Location;
}
于 2009-01-11T20:13:30.637 回答