Visual C++ 屬性視窗標題列的「釘選」按鈕(保持視窗置頂/取消置頂)是實用的 UI 元素。本文展示如何從 CButton 衍生 CSButton 實現此功能,包含雙態位圖切換、視窗 Z-Order 控制完整邏輯。
CSButton 類別繼承 CButtonIDB_BITMAP1(釘下)、IDB_BITMAP2(未釘下)CBitmap bitmap, bitmap2; BOOL m_state; CDC memdc, memdc2;void CSButton::PreSubclassWindow() {
m_state = FALSE;
bitmap.LoadBitmap(IDB_BITMAP1);
bitmap2.LoadBitmap(IDB_BITMAP2);
CDC *pDC = GetDC();
memdc.CreateCompatibleDC(pDC);
memdc.SelectObject(&bitmap);
memdc2.CreateCompatibleDC(pDC);
memdc2.SelectObject(&bitmap2);
CButton::PreSubclassWindow();
}
void CSButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) {
CRect client = lpDrawItemStruct->rcItem;
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CBrush brush;
brush.CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));
pDC->FillRect(client, &brush);
if (m_state) {
// 釘下狀態:視窗置頂
pDC->StretchBlt(client.left, client.top, client.Width(), client.Height(),
&memdc, 0, 0, 24, 21, SRCCOPY);
GetParent()->SetWindowPos(&CWnd::wndTopMost, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
} else {
// 未釘下狀態:視窗取消置頂
pDC->StretchBlt(client.left, client.top, client.Width(), client.Height(),
&memdc2, 0, 0, 24, 21, SRCCOPY);
GetParent()->SetWindowPos(&CWnd::wndNoTopMost, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
}
}
void CSButton::OnLButtonDown(UINT nFlags, CPoint point) {
m_state = !m_state; // 切換狀態
CButton::OnLButtonDown(nFlags, point);
}
memdc/memdc2 預先載入位圖,DrawItem 直接 StretchBlt 繪製,效能最佳SetWindowPos 搭配 wndTopMost/wndNoTopMost 實現置頂切換BS_OWNERDRAW 樣式(可在資源編輯器或 PreSubclassWindow 中設定)WM_KEYDOWN)GetState()/SetState() 公開介面供外部控制CImageList) 取代雙 CDC 簡化資源管理CSButton 以不足 50 行核心程式碼完整實現釘選按鈕功能,展示了 MFC 自繪按鈕結合視窗操作的經典模式。原始碼結構清晰,可直接整合專案或作為其他雙態按鈕開發的參考範本。