Winforms - 单击/拖动表单中的任意位置以移动它,就像在表单标题中单击一样
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30184/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Winforms - Click/drag anywhere in the form to move it as if clicked in the form caption
提问by Joseph Daigle
I am creating a small modal form that is used in Winforms application. It is basically a progress bar of sorts. But I would like the user to be able to click anywhere in the form and drag it to move it around on the desktop while it is still being displayed.
我正在创建一个在 Winforms 应用程序中使用的小型模态表单。它基本上是一个进度条。但我希望用户能够在表单中的任意位置单击并拖动它以在它仍在显示时在桌面上移动它。
How can I implement this behavior?
我该如何实现这种行为?
回答by Timothy Fries
Microsoft KB Article 320687has a detailed answer to this question.
Microsoft KB 文章 320687对此问题有详细解答。
Basically, you override the WndProc method to return HTCAPTION to the WM_NCHITTEST message when the point being tested is in the client area of the form -- which is, in effect, telling Windows to treat the click exactly the same as if it had occured on the caption of the form.
基本上,当被测试的点位于窗体的客户区时,您重写 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);
}
回答by FlySwat
Here is a way to do it using a P/Invoke.
这是一种使用 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);
}
}
回答by aku
The following code assumes that the ProgressBarForm form has a ProgressBar control with Dockproperty set to Fill
以下代码假设 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;
}
}
回答by Simon Mourier
The accepted answer is a cool trick, but it doesn't always work if the Form is covered by a Fill-docked child control like a Panel (or derivates) for example, because this control will eat all most Windows messages.
接受的答案是一个很酷的技巧,但如果表单被填充停靠的子控件(例如面板(或派生))覆盖,则它并不总是有效,因为该控件将吃掉所有大多数 Windows 消息。
Here is a simple approach that works also in this case: derive the control in question (use this class instead of the standard one) an handle mouse messages like this:
这是一种在这种情况下也适用的简单方法:派生有问题的控件(使用此类而不是标准类)处理鼠标消息,如下所示:
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;
}
}
}
回答by Willdorf
VC++ 2010 Version (of FlySwat's):
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);
}
}
};
}

