我已经构建了一个自定义控件,我想让人们点击并拖动我的控件,就好像他们在窗口标题栏上拖动一样。这样做的最佳方式是什么?
到目前为止,当窗口需要移动时,我没有成功地利用鼠标按下,向上和移动事件来解密。
答案 0 :(得分:10)
除了我的其他答案,您可以在Control中手动执行此操作:
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;
}
}
编辑:经过测试和修复 - 现在确实有效。
答案 1 :(得分: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
}
但是,如果此时有控件,我认为不会发送消息。
答案 2 :(得分:3)
如果你想让表单的一部分表现得像标题,那么SLaks提供的WM_NCHITTEST
技巧就是你要走的路。但是,如果你想让一个子窗口能够拖动窗体,还有另一种方法。
基本上,如果使用MOUSE_MOVE命令id向DefWindowProc发送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.
}