Reputation: 11637
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?
Upvotes: 0
Views: 2151
Reputation: 29547
You would need WM_
NC
LBUTTONDOWN
(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.
Upvotes: 3
Reputation: 942498
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;
}
Upvotes: 2
Reputation: 163357
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.
Upvotes: 2