Winforms – 单击/拖动表单中的任意位置,以便像在表单标题中单击一样移动它
我正在创build一个Winforms应用程序中使用的小模式窗体。 这基本上是一个进度栏。 但是我希望用户能够点击窗体中的任意位置,然后拖动它在桌面上移动,同时仍然显示在桌面上。
我怎样才能实现这种行为?
Microsoft知识库文章320687对此问题有详细的解答。
基本上,当被testing点位于窗体的客户区时,你重写WndProc方法来返回HTCAPTION到WM_NCHITTEST消息 – 这实际上是告诉Windows将点击看作完全相同表单的标题。
private const int WM_NCHITTEST = 0x84; private const int HTCLIENT = 0x1; private const int HTCAPTION = 0x2; protected override void WndProc(ref Message m) { switch(m.Msg) { case WM_NCHITTEST: base.WndProc(ref m); if ((int)m.Result == HTCLIENT) { m.Result = (IntPtr)HTCAPTION; } return; } base.WndProc(ref m); }
这是一个使用P / Invoke的方法。
public const int WM_NCLBUTTONDOWN = 0xA1; public const int HTCAPTION = 0x2; [DllImport("User32.dll")] public static extern bool ReleaseCapture(); [DllImport("User32.dll")] public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); void Form_Load(object sender, EventArgs e) { this.MouseDown += new MouseEventHandler(Form_MouseDown); } void Form_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { ReleaseCapture(); SendMessage(Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0); } }
下面的代码假定ProgressBarForm窗体具有一个ProgressBar控件,将Dock属性设置为Fill
public partial class ProgressBarForm : Form { private bool mouseDown; private Point lastPos; public ProgressBarForm() { InitializeComponent(); } private void progressBar1_MouseMove(object sender, MouseEventArgs e) { if (mouseDown) { int xoffset = MousePosition.X - lastPos.X; int yoffset = MousePosition.Y - lastPos.Y; Left += xoffset; Top += yoffset; lastPos = MousePosition; } } private void progressBar1_MouseDown(object sender, MouseEventArgs e) { mouseDown = true; lastPos = MousePosition; } private void progressBar1_MouseUp(object sender, MouseEventArgs e) { mouseDown = false; } }
被接受的答案是一个很酷的技巧,但是如果窗体被填充停靠的子控件覆盖,例如面板(或派生),它并不总是工作,因为这个控件会占用大部分的Windows消息。
下面是一个简单的方法,在这种情况下也是有效的:派生出有问题的控件(使用这个类而不是标准的),这样的句柄鼠标消息:
private class MyTableLayoutPanel : Panel // or TableLayoutPanel, etc. { private Point _mouseDown; private Point _formLocation; private bool _capture; // NOTE: we cannot use the WM_NCHITTEST / HTCAPTION trick because the table is in control, not the owning form... protected override void OnMouseDown(MouseEventArgs e) { _capture = true; _mouseDown = e.Location; _formLocation = ((Form)TopLevelControl).Location; } protected override void OnMouseUp(MouseEventArgs e) { _capture = false; } protected override void OnMouseMove(MouseEventArgs e) { if (_capture) { int dx = e.Location.X - _mouseDown.X; int dy = e.Location.Y - _mouseDown.Y; Point newLocation = new Point(_formLocation.X + dx, _formLocation.Y + dy); ((Form)TopLevelControl).Location = newLocation; _formLocation = newLocation; } } }
VC ++ 2010版本(FlySwat的):
#include <Windows.h> namespace DragWithoutTitleBar { using namespace System; using namespace System::Windows::Forms; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Data; using namespace System::Drawing; public ref class Form1 : public System::Windows::Forms::Form { public: Form1(void) { InitializeComponent(); } protected: ~Form1() { if (components) { delete components; } } private: System::ComponentModel::Container ^components; HWND hWnd; #pragma region Windows Form Designer generated code void InitializeComponent(void) { this->SuspendLayout(); this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(640, 480); this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::None; this->Name = L"Form1"; this->Text = L"Form1"; this->Load += gcnew EventHandler(this, &Form1::Form1_Load); this->MouseDown += gcnew System::Windows::Forms::MouseEventHandler(this, &Form1::Form1_MouseDown); this->ResumeLayout(false); } #pragma endregion private: System::Void Form1_Load(Object^ sender, EventArgs^ e) { hWnd = static_cast<HWND>(Handle.ToPointer()); } private: System::Void Form1_MouseDown(Object^ sender, System::Windows::Forms::MouseEventArgs^ e) { if (e->Button == System::Windows::Forms::MouseButtons::Left) { ::ReleaseCapture(); ::SendMessage(hWnd, /*WM_NCLBUTTONDOWN*/ 0xA1, /*HT_CAPTION*/ 0x2, 0); } } }; }