在 WinForms 中實現自訂外觀視窗(如皮膚化、無標題列)時,必須自行處理:標題列拖曳移動、邊框/角縮放、系統按鈕(最小化/最大化/關閉)點擊。核心在於 滑鼠座標判斷 與 行為封裝。
將視窗劃分為 9 宮格 + 標題列 + 系統按鈕區:
Top_Left, Top_Right, Bottom_Left, Bottom_Right(縮放)Middle_Left, Middle_Right, Top_Middle, Bottom_Middle(縮放)Top_Middle 區域(拖曳移動)定義抽象基類 MouseAction,每種互動對應一個具體子類:
public abstract class MouseAction
{
public abstract void Action(int screenX, int screenY, Form form);
}
// 範例:右邊框縮放
public class MouseSizeRight : MouseAction
{
private int _initialX;
public MouseSizeRight(int initialX) { _initialX = initialX; }
public override void Action(int screenX, int screenY, Form form)
{
form.Width = screenX - _initialX;
form.Invalidate();
}
}
// 範例:標題列拖曳移動
public class MouseDrag : MouseAction
{
private int _offsetX, _offsetY;
public MouseDrag(int hitX, int hitY) { _offsetX = hitX; _offsetY = hitY; }
public override void Action(int screenX, int screenY, Form form)
{
form.Location = new Point(screenX - _offsetX, screenY - _offsetY);
}
}
e.X, e.Y),依命中測試結果建立對應 MouseAction 實例賦值給欄位 mouse。mouse != null,呼叫 mouse.Action(Control.MousePosition.X, Control.MousePosition.Y, this)。關鍵:使用螢幕絕對座標 Control.MousePosition,而非客戶區座標,避免視窗移動/縮放時座標系變更導致抖動。mouse = null。private int LEFT = 5, RIGHT = 5, BOTTOM = 5, TOP = 5, TITLE_WIDTH = 45;
private int _hitX, _hitY;
private MouseAction _mouseAction;
protected override void OnMouseDown(MouseEventArgs e)
{
_hitX = e.X; _hitY = e.Y;
// 命中測試範例:左上角縮放區
if ((e.X <= LEFT + 10 && e.Y <= TOP) || (e.Y <= TOP + 10 && e.X <= LEFT))
{
_mouseAction = new MouseSizeTopLeft(Location.X, Location.Y, Width, Height);
return;
}
// 系統關閉按鈕
if (e.X > Width - 20 && e.Y > 6 && ...)
{
Close(); return;
}
// 標題列拖曳
if (e.Y < TITLE_WIDTH)
{
_mouseAction = new MouseDrag(_hitX, _hitY);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
this.Cursor = GetCursorType(e.X, e.Y); // 更新游標外觀
if (_mouseAction != null)
{
_mouseAction.Action(Control.MousePosition.X, Control.MousePosition.Y, this);
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
_mouseAction = null;
}
// 標題列雙擊最大化/還原
protected override void OnDoubleClick(EventArgs e)
{
if (_hitY > TOP && _hitY < TITLE_WIDTH)
{
WindowState = WindowState == FormWindowState.Normal
? FormWindowState.Maximized
: FormWindowState.Normal;
Invalidate();
}
}
GetCursorType 依滑鼠位置回傳 Cursors.SizeNWSE, Cursors.SizeWE, Cursors.SizeNS 等,提升 UX。MinimumSize = new Size(200, 200) 防止縮成一點。TextureBrush 繪製九宮格圖片,DrawMiddle_Left 等方法計算目標矩形區域。MouseAction 子類,不修改現有事件邏輯。MouseAction 子類可獨立單元測試,傳入模擬座標驗證 form.Width/Location 變化。