2010年9月27日 星期一

利用 SetWindowRgn 製作非矩型視窗

在 XP 預設的外觀樣式下,正常的視窗或對話框是上方頂點圓圓的配下方直角,
若有需要做出非預設樣式的視窗時,可以利用 CWnd::SetWindowRgn() 這個 function
這可以用來指定視窗的全部範圍與型狀,
在型狀之外的部份會自動為我們加上背景的遮罩。

只要是繼承自CWnd的元件都可以使用這個 function
但需注意的是並不是所有的元件 SetWindowRgn 都會幫我們加上遮罩,
例如 CButton 就必須手動加上背景圖片,CDialog 就會自動遮罩。

因為遮罩必須隨著視窗縮放時改變,所以習慣將 SetWindowRgn 放在 OnSize() 中
以下是產生一個四邊皆為圓角的 Dialog 之簡單範例


BOOL CreateRgn(CRgn& rgn,RECT& rcBound, int nRectCount, RECT* paRect);

void CDlg::OnSize(UINT nType, int cx, int cy)
{
  CDialog::OnSize(nType, cx, cy);
  if(/*不做遮罩的情況,例如已將 Dialog 切至全螢幕狀態*/)
  {
    // 不做遮罩
    SetWindowRgn(NULL, TRUE);
  }
  else
  {
    CRgn rgn;
    RECT rc[1+5] =
    {
      {0, 0, cx, cy}, /* 全部視窗區域 */
      {0, 3, 1, cy - 3}, /* 左側第一行 */
      {1, 1, 3, cy - 1}, /* 左側第二行 */
      {3, 0, cx - 3, cy}, /* 中間區域 */
      {cx - 3, 1, cx - 1, cy - 1}, /* 右側第二行 */
      {cx - 1, 3, cx, cy - 3}, /* 右側第一行 */
    };
    CreateRgn(rgn, rc[0], 7, rc+1);
    SetWindowRgn((HRGN)rgn, TRUE);
    rgn.DeleteObject();
  }
  Invalidate(); // 強制重繪
}



BOOL CreateRgn(CRgn& rgn, RECT& rcBound, int nRectCount, RECT* paRect)
{
  //rgn must be a clean CRgn
  ASSERT(nRectCount>0);
  RGNDATA* p = (RGNDATA*)malloc(sizeof(RGNDATAHEADER) + sizeof(RECT) * nRectCount);
  p->rdh.dwSize = sizeof(RGNDATAHEADER);
  p->rdh.iType = RDH_RECTANGLES;
  p->rdh.nCount = nRectCount;
  p->rdh.rcBound = rcBound;
  p->rdh.nRgnSize = 0;
  memcpy(p->Buffer, paRect, sizeof(RECT) * nRectCount);
  BOOL bResult = rgn.CreateFromData(NULL, sizeof(RGNDATAHEADER) + sizeof(RECT) * nRectCount, p);
  free(p);
  return bResult;
}

在 OnSize() 中寫上這段 code 就可以產生出這種四個角都是圓角的 Dialog

沒有留言:

張貼留言