MFC 的 CRectTracker 是處理矩形選取、調整大小、拖曳移動的經典工具類。在 .NET 早期缺乏對應功能時,開發者常需自行移植。本文展示一個完整的 C# 版 CRectTracker 實作,支援八向調整控點、橡膠帶選取、游標動態切換,可直接用於 WinForms 繪圖編輯器、報表設計器等場景。
public class CRectTracker
{
public Rectangle m_rect; // 追蹤矩形
public int m_nStyle; // 樣式旗標
public bool m_bAllowInvert; // 允許反向繪製
public enum StyleFlags
{
resizeInside = 0x01, // 內部調整控點
resizeOutside = 0x02, // 外部調整控點
hatchInside = 0x04, // 內部斜線填充
// ... 其他旗標
}
// 核心方法
public bool Track(Control ctrl, Point pt, bool allowInvert, object reserved);
public bool TrackRubberBand(Control ctrl, Point pt, bool allowInvert);
public int HitTest(Point pt);
public void OnDraw(Graphics g);
public bool SetCursor(Control ctrl, int nHitTest, Point pt);
public void GetTrueRect(ref Rectangle rect);
public void NormalizeRect(ref Rectangle rect);
}
public class CReportDoc
{
public CRectTracker m_tracker = null;
public CReportDoc(MainWindow mainWin)
{
m_tracker = new CRectTracker();
m_tracker.m_rect = new Rectangle(10, 10, 91, 91); // 初始位置與大小
m_tracker.m_nStyle = (int)CRectTracker.StyleFlags.resizeInside;
}
}
public class CReportView : Form
{
private CReportDoc myDoc;
private void InitializeComponent()
{
this.MouseDown += CReportView_MouseDown;
this.Paint += PaintHandler;
this.MouseMove += CReportView_MouseMove; // 替代 MFC OnSetCursor
}
private void PaintHandler(object sender, PaintEventArgs e)
{
myDoc.m_tracker.OnDraw(e.Graphics); // 繪製追蹤矩形與控點
}
private void CReportView_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
Point pt = new Point(e.X, e.Y);
Rectangle rectSave = new Rectangle();
myDoc.m_tracker.GetTrueRect(ref rectSave);
if (myDoc.m_tracker.HitTest(pt) < 0) // 點擊空白區 → 橡膠帶選取
{
CRectTracker tracker = new CRectTracker();
if (tracker.TrackRubberBand(this, pt, myDoc.m_bAllowInvert))
{
tracker.NormalizeRect(ref tracker.m_rect);
if (tracker.m_rect.IntersectsWith(myDoc.m_tracker.m_rect))
{
// 切換控點樣式:內縮↔外擴
if ((myDoc.m_tracker.m_nStyle & (int)CRectTracker.StyleFlags.resizeInside) != 0)
{
myDoc.m_tracker.m_nStyle &= ~(int)CRectTracker.StyleFlags.resizeInside;
myDoc.m_tracker.m_nStyle |= (int)CRectTracker.StyleFlags.resizeOutside;
}
else
{
myDoc.m_tracker.m_nStyle &= ~(int)CRectTracker.StyleFlags.resizeOutside;
myDoc.m_tracker.m_nStyle |= (int)CRectTracker.StyleFlags.resizeInside;
}
}
}
myDoc.UpdateAllViews(null); // 觸發重繪
}
else // 點擊現有追蹤器 → 拖曳/調整大小
{
if (myDoc.m_tracker.Track(this, pt, myDoc.m_bAllowInvert, null))
myDoc.UpdateAllViews(null);
}
}
private void CReportView_MouseMove(object sender, MouseEventArgs e)
{
Point pt = new Point(e.X, e.Y);
if (!myDoc.m_tracker.SetCursor(this, 0, pt))
this.Cursor = Cursors.Arrow; // 預設游標
}
}
回傳值對應:-1=外部、0-7=八個控點、8=矩形內部。此邏輯決定是啟動橡膠帶選取還是進入拖曳/縮放模式。
Track:處理現有矩形的移動與八向縮放,內部捕捉滑鼠移動直到按鍵釋放TrackRubberBand:從點擊點拉出新矩形,即時繪製虛線框回饋根據滑鼠懸停位置(控點/邊緣/內部)自動切換 Cursors.SizeNWSE、Cursors.SizeWE、Cursors.SizeAll 等,提升操作直覺性。
public void OnDraw(Graphics g)
{
// 繪製矩形邊框
g.DrawRectangle(Pens.Black, m_rect);
// 繪製八個控點
if ((m_nStyle & (int)StyleFlags.resizeOutside) != 0)
DrawHandles(g, true); // 外部控點
else if ((m_nStyle & (int)StyleFlags.resizeInside) != 0)
DrawHandles(g, false); // 內部控點
// 斜線填充
if ((m_nStyle & (int)StyleFlags.hatchInside) != 0)
g.FillRectangle(Brushes.HatchStyle.DiagonalCross, m_rect);
}
m_tracker 改為 List<CRectTracker>,配合 Shift/Ctrl 鍵實現群組操作Track 過程中檢測與其他物件邊緣距離,自動對齊Adorner 層繪製追蹤器,利用 Thumb 控制項處理拖曳邏輯ISerializable,將追蹤器狀態存入專案檔這個 C# 版 CRectTracker 完整復刻 MFC 核心功能,並適配 WinForms 事件模型。對於需要矩形選取、縮放、拖曳的繪圖類應用,它提供了開箱即用的解決方案。理解其 HitTest → Track/TrackRubberBand → OnDraw 核心流程,有助於擴展更複雜的幾何編輯功能。