以编程方式开始移动表单

发布于 2024-07-11 05:20:16 字数 440 浏览 4 评论 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 事件中尝试了上面的代码,移动光标然后按下按钮1。

有什么建议么?

I am trying to make a form move (using the titlebar) from a button click.

I thought this would be simple using 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)

However, although this sends the message if the cursor is in the forms client area, it does not seem to send it to the forms titlebar (the form captures the event somehow, despite the cursor being on the titlebar not in the client area).

I have tried the above code in both mousedown and click events on the button, moving the cursor and then pressing on the button1.

Any suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

日裸衫吸 2024-07-18 05:20:16

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

通常,允许用户在单击标题栏以外的其他位置时移动表单的方法是处理 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 来帮助我翻译它。

You would need WM_NCLBUTTONDOWN (and pass HTCAPTION as wParam). I'm still not entirely sure this would accomplish what you're trying to do, though.

Typically, the way to allow the user to move your form when clicking somewhere other than the title bar is to process the WM_NCHITTEST message and return HTCAPTION when the cursor is over the area from which you'd like to initiate moving. However, if this area is occupied by a child control, you also have to process WM_NCHITTEST from the child control and return HTTRANSPARENT.


Incidentally, an easier—if slightly less correct—way to accomplish this is to do as Mehrdad Afshari suggested, and just set the form's Location property. You commented to him that "it needs to move on the mouse move", and that's exactly what you can and should do.

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

The problem with this approach is that it bypasses Windows' code for moving a top-level window. This means that if the user has not selected the "Show window contents while dragging" option in the Display Properties dialog, this will effectively ignore that setting (it won't show a drag outline). There may be other drawbacks that I haven't thought of as well.

On the whole, though, this is a simple, easy way to accomplish this that is a fully .NET solution which doesn't rely on any platform invoke (so it should be portable to Mono on Unix).


Oops. I just realized that I gave you C# example code, but your code seems to be VB.NET. I guess what you would need would be:

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

This may not be the most succinct way to express this in VB.NET. I used Reflector to help me translate it.

鸠书 2024-07-18 05:20:16

wm_LButtonDown 消息的 LParam 值接收客户端坐标中的鼠标位置。 标题栏位于非客户区域,因此请使用 wm_NCLButtonDown消息。 我之前已经看到该消息作为此问题的答案,但我希望有一个更直接的途径:发送 wm_SysCommand 消息 到窗口,并指定 sc_Move 标志。

The LParam value for the wm_LButtonDown message receives the mouse position in client coordinates. The title bar is in the non-client area, so use the wm_NCLButtonDown message. I've seen that message given as an answer to this question before, but there's a more direct route that I would have expected to work: Send a wm_SysCommand message to the window, and specify sc_Move flag.

等数载,海棠开 2024-07-18 05:20:16

迈赫达德是对的,没有必要这样做。 鼠标被捕获,因此您永远无法将其移动得太快。 示例代码:

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

Mehrdad is right, no need to do this. The mouse is captured so you can never move it too quickly. Sample code:

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文