在圖形編輯器中,滑鼠拖曳選取區域時的「橡皮筋框」效果,需在滑鼠移動時即時繪製、刪除前一幀框線。ControlPaint.DrawReversibleFrame 使用 XOR 光柵運算,重複呼叫同一座標即可切換顯示/隱藏,無需重繪背景,是實作選取框的高效方案。本文解析其在矩形繪製類別中的應用。
public class Rectangle : Shape
{
protected Point m_Start;
protected Point m_End;
protected Color m_Color;
public Rectangle(Point start, Point end, Color fgColor)
{
m_Start = start;
m_End = end;
m_Color = fgColor;
}
public override void Draw(Form canvas)
{
if (canvas == null) return;
InitializeGraphics(canvas); // 繼承自 Shape 基底類別
// 將用戶端座標轉換為螢幕座標
Point startPoint = canvas.PointToScreen(m_Start);
Point endPoint = canvas.PointToScreen(m_End);
MainForm mainForm = (MainForm)canvas;
Color bgColor = GetBackgroundColor(m_Color); // 取得反相顏色
// 計算矩形尺寸
Size rectSize = new Size(m_End.X - m_Start.X, m_End.Y - m_Start.Y);
System.Drawing.Rectangle rectToDraw = new System.Drawing.Rectangle(startPoint, rectSize);
// 關鍵:可逆框線繪製
ControlPaint.DrawReversibleFrame(rectToDraw, bgColor, FrameStyle.Thick);
}
}
public static void DrawReversibleFrame(
Rectangle rectangle, // 螢幕座標矩形
Color backColor, // 背景色(用於 XOR 運算)
FrameStyle style // 框線樣式
);
PointToScreen 轉換Thick(粗框)、Dashed(虛線)
private Color GetBackgroundColor(Color foreground)
{
// 簡單反相:255 - R/G/B
return Color.FromArgb(255 - foreground.R, 255 - foreground.G, 255 - foreground.B);
}
確保框線在任何背景下可見。
public class SelectionTool
{
private Point _startPoint;
private Rectangle _currentRect;
private bool _isDragging;
private Form _canvas;
public void OnMouseDown(Point clientPoint)
{
_startPoint = clientPoint;
_isDragging = true;
}
public void OnMouseMove(Point clientPoint)
{
if (!_isDragging) return;
// 刪除舊框(再次呼叫 XOR 刪除)
if (_currentRect != Rectangle.Empty)
DrawReversibleFrame(_currentRect);
// 計算新框
_currentRect = GetScreenRect(_startPoint, clientPoint);
// 繪製新框
DrawReversibleFrame(_currentRect);
}
public void OnMouseUp(Point clientPoint)
{
if (!_isDragging) return;
// 刪除最後一幀框
if (_currentRect != Rectangle.Empty)
DrawReversibleFrame(_currentRect);
_isDragging = false;
_currentRect = Rectangle.Empty;
// 此處處理最終選取邏輯
ProcessSelection(GetClientRect(_startPoint, clientPoint));
}
private void DrawReversibleFrame(Rectangle screenRect)
{
Color bgColor = GetBackgroundColor(Color.Red);
ControlPaint.DrawReversibleFrame(screenRect, bgColor, FrameStyle.Thick);
}
private Rectangle GetScreenRect(Point start, Point end)
{
Point screenStart = _canvas.PointToScreen(start);
Point screenEnd = _canvas.PointToScreen(end);
return new Rectangle(
Math.Min(screenStart.X, screenEnd.X),
Math.Min(screenStart.Y, screenEnd.Y),
Math.Abs(screenStart.X - screenEnd.X),
Math.Abs(screenStart.Y - screenEnd.Y)
);
}
}
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| DrawReversibleFrame | 無需重繪背景、效能極佳、API 簡單 | 僅支援矩形框線、顏色受 XOR 限制 | 簡單選取框、裁剪框 |
| 雙重緩衝 + OnPaint | 完整控制繪圖內容、支援任意形狀 | 需管理繪圖狀態、重繪開銷大 | 複雜編輯器、多圖層 |
| Overlay 透明視窗 | 獨立視窗層級、不干擾主視窗 | 視窗管理複雜、跨螢幕問題 | 跨應用程式選取、螢幕截圖 |
PointToScreen,Client 座標會導致位置錯誤MouseUp、MouseLeave、表單失去焦點時必須清除殘留框線ControlPaint.DrawReversibleFrame 是實作橡皮筋選取框的經典高效方案,利用 XOR 光柵運算特性實現無殘留的即時視覺回饋。掌握螢幕座標轉換、反相顏色計算、拖曳生命週期管理三大關鍵,即可在圖形編輯器、表單設計器、截圖工具中建立專業級選取體驗。