絕大多數 Windows 應用程式皆採用標準矩形視窗,雖然實作簡單、風格簡潔,適合文件處理類與簡單遊戲應用,但在娛樂性程式中顯得缺乏生氣。若能以非矩形視窗取代傳統矩形框架,將大幅提升程式趣味性。典型例子如 Windows Media Player 新版控制面板,依選取面板形狀呈現播放器外型,遠比舊版矩形介面生動。實作非矩形視窗並不困難,掌握核心原理後即可創建各式有趣視窗。
所有 Windows 視窗皆位於一個稱為「Region」的區域中。若視窗大小超出 Region 範圍,Windows 會自動裁切超出部分使其不可見。建立非矩形視窗僅需兩步驟:
第二步極為簡單,僅需呼叫 API 函式 SetWindowRgn:
int SetWindowRgn(HWND hWnd, HRGN hRgn, BOOL bRedraw);
MFC 中對應 CWnd::SetWindowRgn(HRGN hRgn, BOOL bRedraw)。參數含義相同:hWnd 為目標視窗控制代碼,hRgn 為已建立的 Region 控制代碼,bRedraw 決定是否重繪。
第一步視非矩形複雜度而異。MFC 中 Region 物件由 CRgn 類別實現,其成員函式幾乎皆有對應的 SDK API 函式。
CRgn 提供四種基本建立函式:
CreateRectRgn:矩形 RegionCreateEllipticRgn:圓形/橢圓 RegionCreateRoundRectRgn:圓角矩形 RegionCreatePolygonRgn:多邊形 Region將下列程式碼加入 MFC 對話方塊專案的 OnInitDialog 中:
BOOL CTestDlg::OnInitDialog() {
CDialog::OnInitDialog();
CRgn rgn;
rgn.CreateEllipticRgn(0, 0, 200, 100); // 左上(0,0) 右下(200,100)
SetWindowRgn(rgn, TRUE);
return TRUE;
}

此法可建立更豐富形狀,流程如下:
CDC::BeginPath() 開始追蹤繪圖過程,SetBkMode(TRANSPARENT) 設定透明背景,繼而呼叫 Arc、LineTo、TextOut 等繪圖函式,最後以 EndPath() 結束追蹤。PathToRegion(HDC hdc),將裝置環境中的路徑轉為 Region 控制代碼。按鈕點擊事件中實作「Hello」文字形狀視窗:
void CTestDlg::OnTest() {
HRGN wndRgn;
CClientDC dc(this);
CFont mFont;
if (dc.m_hDC != NULL) {
VERIFY(mFont.CreateFont(200, 50, 0, 0, FW_HEAVY, TRUE, FALSE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "宋體"));
dc.BeginPath();
dc.SetBkMode(TRANSPARENT); // 必須設定透明模式
CFont* pOldFont = dc.SelectObject(&mFont);
dc.TextOut(0, 0, "Hello");
dc.SelectObject(pOldFont);
dc.EndPath();
wndRgn = ::PathToRegion(dc.m_hDC);
this->SetWindowRgn(wndRgn, TRUE);
}
}

此法可實現任意圖片形狀視窗,步驟較複雜:
CRgn::CombineRgn 以 RGN_OR 模式合併所有小 Region 形成完整形狀。void SetupRegion(CDC* pDC, CBitmap& cBitmap, COLORREF TransColor) {
CDC memDC;
memDC.CreateCompatibleDC(pDC);
CBitmap* pOldMemBmp = memDC.SelectObject(&cBitmap);
CRgn wndRgn;
wndRgn.CreateRectRgn(0, 0, 0, 0); // 初始空 Region
BITMAP bit;
cBitmap.GetBitmap(&bit);
for (int y = 0; y <= bit.bmHeight; y++) {
CRgn rgnTemp;
int iX = 0;
do {
// 跳過透明色尋找非透明色起始點
while (iX <= bit.bmWidth && memDC.GetPixel(iX, y) == TransColor) iX++;
int iLeftX = iX;
// 尋找下一個透明色作為結束點
while (iX <= bit.bmWidth && memDC.GetPixel(iX, y) != TransColor) ++iX;
// 建立單列高度的矩形 Region
rgnTemp.CreateRectRgn(iLeftX, y, iX, y + 1);
// 合併至主 Region
wndRgn.CombineRgn(&wndRgn, &rgnTemp, RGN_OR);
rgnTemp.DeleteObject(); // 必須刪除暫存 Region
} while (iX <= bit.bmWidth);
}
memDC.SelectObject(pOldMemBmp);
CWnd* pWnd = pDC->GetWindow();
pWnd->SetWindowRgn(wndRgn, TRUE);
pWnd->SetForegroundWindow();
}
搭配 OnEraseBkgnd 繪製該點陣圖,即可獲得與圖片形狀完全一致的視窗。

| 方法 | 複雜度 | 適用場景 |
|---|---|---|
| 基本幾何函式 | 最低 | 簡單幾何形狀(圓、矩形、圓角矩形、多邊形) |
| 繪圖路徑法 | 中等 | 文字、曲線、複合幾何圖形 |
| 點陣圖掃描法 | 最高 | 任意複雜圖片形狀 |
依需求選擇適當方法:簡單幾何形狀用第一法;需文字或曲線組合用第二法;任意圖片外型用第三法。