對話框承載大量控件(如下拉選單、編輯框)時,單一螢幕無法完整顯示。傳統做法在 WM_VSCROLL 訊息中手動計算並移動每個控件座標,不僅程式碼冗長,還易出現「滾動不乾淨」、隱藏控件無法恢復顯示等問題。
CScrollView 封裝了滾動邏輯:建立一個可滾動的視圖區域,超出可視範圍的內容自動隱藏,透過捲軸瀏覽。將其作為子視圖嵌入對話框,即可像操作普通控件一樣管理內部元件。
專案右鍵 → Add Class → MFC Class,基類選 CScrollView,命名為 CMyScrollView。

// ScrollViewTestDlg.h
class CScrollViewTestDlg : public CDialogEx {
CMyScrollView* m_pMyView; // 滾動視圖指標
// ...
};
BOOL CScrollViewTestDlg::OnInitDialog() {
CDialogEx::OnInitDialog();
// 建立視圖物件
m_pMyView = (CMyScrollView*)(RUNTIME_CLASS(CMyScrollView)->CreateObject());
CCreateContext context;
context.m_pCurrentDoc = NULL;
// 建立視窗:初始大小 100x100,稍後調整
m_pMyView->Create(NULL, NULL, WS_BORDER,
CRect(0, 0, 100, 100), this, WM_USER + 1, &context);
((CView*)m_pMyView)->OnInitialUpdate(); // 重要:觸發初始化
m_pMyView->ShowWindow(SW_SHOW);
return TRUE;
}
OnInitialUpdate 會根據視窗大小自動計算捲軸參數。若視窗較小(如 200x200),系統會認為無需捲軸。必須在 OnInitialUpdate 之後呼叫 SetScrollSizes 強制設定虛擬大小:
SIZE size;
size.cx = 500; // 虛擬寬度
size.cy = 500; // 虛擬高度
m_pMyView->SetScrollSizes(MM_TEXT, size); // 必須在 OnInitialUpdate 之後呼叫

// 成員變數
CMFCButton* m_button;
// OnInitDialog 續寫
m_button = new CMFCButton();
m_button->Create(_T("Hello"), WS_VISIBLE,
CRect(5, 5, 80, 250), // 座標相對於 CMyScrollView 客戶區
m_pMyView, WM_USER + 1);

CheckScrollBars(BOOL& bHasHorzBar, BOOL& bHasVertBar):檢查捲軸是否存在。GetDeviceScrollPosition():取得目前捲軸位置(CPoint)。SetScaleToFitSize(SIZE sizeTotal):依內容自動調整視圖縮放。OnInitialUpdate,否則捲軸參數不會初始化。SetScrollSizes 一定要在 OnInitialUpdate 之後呼叫,否則會被覆蓋。CScrollView 的客戶區原點 (0,0),與對話框座標無關。delete m_pMyView; delete m_button; 避免記憶體洩漏。使用 CScrollView 作為容器,將複雜的滾動邏輯封裝於框架層,開發者只需專注於業務控件的佈局。此法不僅程式碼簡潔,更能完美處理隱藏控件的顯示/隱藏狀態切換,是 MFC 對話框大量控件佈局的最佳實踐。