自訂標題列、無邊框視窗 (FormBorderStyle.None) 或需讓 Panel、Label、PictureBox 等任意控件成為「拖曳把手」時,預設視窗拖曳行為失效,需自行處理滑鼠事件。
MouseDown:記錄滑鼠相對視窗左上角的偏移量MouseMove:按住左鍵移動時,依目前螢幕座標減去偏移量,更新 Form.LocationMouseUp:釋放滑鼠,重設旗標using System;
using System.Drawing;
using System.Windows.Forms;
public static class FormDragHelper
{
private static bool _isMouseDown;
private static Point _mouseOffset;
private static Form _targetForm;
///
/// 綁定指定控件的拖曳事件,使其能拖動所屬視窗
///
public static void BindDrag(Control control)
{
if (control == null) return;
_targetForm = control.FindForm();
if (_targetForm == null) return;
control.MouseDown += OnMouseDown;
control.MouseMove += OnMouseMove;
control.MouseUp += OnMouseUp;
}
private static void OnMouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
Control ctrl = (Control)sender;
int offsetX = -e.X;
int offsetY = -e.Y;
// 若控件非 Form 本身,需加上控件在 Form 內的相對位置
if (!(ctrl is Form))
{
offsetX -= ctrl.Left;
offsetY -= ctrl.Top;
}
// 有標題列時,需扣除邊框與標題列高度
if (_targetForm.FormBorderStyle != FormBorderStyle.None)
{
offsetX -= SystemInformation.FrameBorderSize.Width;
offsetY -= SystemInformation.FrameBorderSize.Height + SystemInformation.CaptionHeight;
}
_mouseOffset = new Point(offsetX, offsetY);
_isMouseDown = true;
}
private static void OnMouseMove(object sender, MouseEventArgs e)
{
if (!_isMouseDown) return;
Point screenPos = Control.MousePosition;
screenPos.Offset(_mouseOffset);
_targetForm.Location = screenPos;
}
private static void OnMouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) _isMouseDown = false;
}
}
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
// 讓標題列 Panel、Logo 圖片、甚至整個 Form 都能拖曳
FormDragHelper.BindDrag(this.panelTitleBar);
FormDragHelper.BindDrag(this.picLogo);
FormDragHelper.BindDrag(this); // 整個表單
}
}
Control.MousePosition 回傳虛擬螢幕座標,跨螢幕拖曳自動支援。-= 解除訂閱。透過簡單的滑鼠事件與座標換算,即可賦予任何控件拖曳視窗的能力,極大提升自訂 UI 視窗的使用體驗。