首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 自訂視窗拖曳與縮放:滑鼠座標捕捉與事件委派模式實戰

     編輯:關於C#

場景:無框視窗的自訂非用戶區互動

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

事件流程與座標系統

  1. MouseDown:記錄滑鼠相對視窗客戶區座標 (e.X, e.Y),依命中測試結果建立對應 MouseAction 實例賦值給欄位 mouse。
  2. MouseMove:若 mouse != null,呼叫 mouse.Action(Control.MousePosition.X, Control.MousePosition.Y, this)。關鍵:使用螢幕絕對座標 Control.MousePosition,而非客戶區座標,避免視窗移動/縮放時座標系變更導致抖動。
  3. MouseUp:釋放 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 等方法計算目標矩形區域。

架構優勢

  1. 開放封閉原則:新增互動行為(如雙擊置頂)只需新增 MouseAction 子類,不修改現有事件邏輯。
  2. 座標系隔離:命中測試用客戶區座標,執行行動用螢幕座標,職責清晰。
  3. 可測試性:MouseAction 子類可獨立單元測試,傳入模擬座標驗證 form.Width/Location 變化。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved