Formas de pago: haga clic / arrastre en cualquier lugar del formulario para moverlo como si hubiera hecho clic en el título del formulario

Estoy creando un pequeño formulario modal que se utiliza en la aplicación Winforms. Básicamente es una especie de barra de progreso. Pero me gustaría que el usuario pueda hacer clic en cualquier parte del formulario y arrastrarlo para moverlo en el escritorio mientras todavía se muestra.

¿Cómo puedo implementar este comportamiento?

El artículo 320687 de Microsoft KB tiene una respuesta detallada a esta pregunta.

Básicamente, reemplaza el método WndProc para devolver HTCAPTION al mensaje WM_NCHITTEST cuando el punto que se está probando está en el área de cliente del formulario, lo que, de hecho, le dice a Windows que trate el clic exactamente igual que si hubiera ocurrido en el título de la forma.

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

Aquí hay una manera de hacerlo usando una 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); } } 

El siguiente código supone que el formulario ProgressBarForm tiene un control ProgressBar con la propiedad Dock configurada para 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; } } 

La respuesta aceptada es un truco genial, pero no siempre funciona si el Formulario está cubierto por un control secundario de relleno como un Panel (o derivados), por ejemplo, porque este control se comerá todos los mensajes de Windows.

Aquí hay un enfoque simple que también funciona en este caso: obtenga el control en cuestión (use esta clase en lugar del estándar) y maneje los mensajes del mouse como este:

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

Versión de VC ++ 2010 (de FlySwat):

 #include  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(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); } } }; }