在windows開發當中做界面的主要技術之一就是使用MFC,通常我們看到的QQ,360,暴風影音這些漂亮的界面都可以用MFC來實現。今天我們來說一下如何用MFC美化對話框,默認情況下,對話框的背景如下:
那麼,我們如何將它的背景變成如下界面呢,而且還要保留對話框的移動功能,漂亮背景如下:
爲了實現美化對話框背景的效果,我們需要讓我們的對話框響應WM_CTLCOLOR消息,每當我們的對話框或者它的子控件需要重繪時,我們的對話框都會收到這個消息,
因此,我們需要爲對話框添加WM_CTLCOLOR的消息響應函數,完成對消息的處理,WM_CTLCOLOR的響應函數定義如下:
HBRUSH CMFCDialogUIDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
if (pWnd == this)
{
return m_bkBrush;
}
return hbr;
}
當我們的對話框需要重繪的時候,我們的對話框就會收到WM_CTLCOLOR消息,然後我們的對話框處理函數會調用OnCtlColor函數來處理該消息,在這個函數中,pDC代表我們要繪製的上下文環境,pWnd代表我們要繪製的窗口指針,nCtlColor代表我們要繪製的窗口類型,在函數的內部我們首先調用父類的OnCtlColor,目的是爲了對該消息做默認處理,這是MFC消息響應函數的慣用寫法,但是在我們這裏,我們不能使用默認處理返回的畫刷,所以我們需要做一個特殊的判斷,如果pWnd指向的窗口地址是當前對話框,那麼我們就返回m_bkBrush,這個畫刷是一個圖像畫刷,它會在我們的對話框客戶區繪製我們想讓它顯示的圖片。下面我們看一下m_bkBrush是如何創建的,首先在我們的對話框的頭文件中增加一個CBrush變量,變量名是m_bkBrush,然後在對話框的OnInitDialog中初始化它,OnInitDialog的定義如下:
BOOL CMFCDialogUIDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 將“關於...”菜單項添加到系統菜單中。
// IDM_ABOUTBOX 必須在系統命令範圍內。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 設置此對話框的圖標。 當應用程序主窗口不是對話框時,框架將自動
// 執行此操作
SetIcon(m_hIcon, TRUE); // 設置大圖標
SetIcon(m_hIcon, FALSE); // 設置小圖標
// TODO: 在此添加額外的初始化代碼
CString strBmpPath = _T(".\\res\\Background.png");
CImage img;
img.Load(strBmpPath);
//MoveWindow(0, 0, img.GetWidth(), img.GetHeight());
CBitmap bmpTmp;
bmpTmp.Attach(img.Detach());
m_bkBrush.CreatePatternBrush(&bmpTmp);
return TRUE; // 除非將焦點設置到控件,否則返回 TRUE
}
現在我們看一下,在OnInitDialog中,我們都做了什麼,首先,我們創建了一個CString變量strBmpPath,用它指向我們的圖片文件,然後我們創建了一個CImage變量img,這個變量可以方便的加載各種格式的圖像文件,所以我們使用它的目的是爲了方便加載png格式的文件,MoveWindow的目的是爲了調整我們的對話框客戶區的大小,使客戶區的大小與圖片的大小一致,然後我們創建了一個CBitmap類型變量bmpTmp,使用它是因爲CBrush的成員函數CreatePatternBrush的參數要求輸入這種類型的參數,所以必須將img轉換成CBitmap,轉換的方法是bmpTmp.Attach(img.Detach()),img.Detach會釋放圖像的句柄,並且返回這個句柄,bmpTmp使用Attach綁定img返回的圖像句柄,從而完成了對象類型的轉換,最後調用CreatePatternBrush,這個函數的功能是使用傳遞給它的圖像創建一個圖像畫刷,然後在OnCtlColor中,使用它填充對話框的背景,程序運行效果如下:
現在雖然程序的客戶區已經變成了對話框的背景,但是對話框原來的標題欄和背景圖片的標題欄重複,看起來很彆扭,通過設置對話框的Border屬性可以消除原來的標題欄,設置如下:
Border:None
再次編譯,運行程序,效果如下:
現在的對話框背景已經和我們設想的基本一致,還有一點小瑕疵,大家仔細觀察對話框的底邊,左下角和右下角有多於的像素,下面我們通過代碼消除它。
在OnInitDialog尾部追加如下代碼:
CRgn rgnTmp;
RECT rc;
GetClientRect(&rc);
rgnTmp.CreateRoundRectRgn(rc.left + 3, rc.top + 3, rc.right - rc.left - 3, rc.bottom-rc.top -3, 6, 6);
SetWindowRgn(rgnTmp, TRUE);
通過以上的代碼可以讓對話框變成一個圓角矩形,這樣就可以去掉邊角的點,程序最終運行效果如下:
現在這個對話框的背景已經完全符合我們的要求,但是它現在不能拖動,因爲它的標題欄是假的,所以,我們最後一個目標就是讓這個窗口可以拖動,如何才能讓它移動呢?
Windows只允許我們拖動對話框的標題欄,當我們的鼠標在對話框上拖動的時候,對話框會收到一個WM_NCHITTEST消息,默認的消息處理函數會判斷當前的鼠標是否在對話框的標題欄,如果在就返回HTCAPTION標誌,否則就返回其它標誌,當返回HTCAPTION標誌的情況下,系統就會允許對話框拖動,所以我們可以欺騙windows系統,讓WM_NCHITTEST的響應函數永遠返回HTCAPTION標誌就可以了,爲對話框添加WM_NCHITTEST響應函數,代碼如下:
LRESULT CMFCDialogUIDlg::OnNcHitTest(CPoint point)
{
// TODO: 在此添加消息處理程序代碼和/或調用默認值
LRESULT ret = CDialogEx::OnNcHitTest(point);
return (ret == HTCLIENT) ? HTCAPTION : ret;
}
//模擬標題欄拖動
LRESULT CMainDlg::OnNcHitTest(CPoint point)
{
// TODO: Add your message handler code here and/or call default
UINT nHitTest = CDialogEx::OnNcHitTest(point);
if ((nHitTest == HTCLIENT) && (::GetAsyncKeyState(MK_LBUTTON) < 0))
{
CRect rectDlg;
GetWindowRect(rectDlg);//獲得窗體的大小
if (point.y < rectDlg.top + 30)
{
nHitTest = HTCAPTION;
}
else
{
nHitTest = HTNOWHERE;
}
}
return nHitTest;
//return CDialogEx::OnNcHitTest(point);
}
重新編譯代碼,現在的對話框背景已經美化完成,並且這個對話框可以拖動。
下篇文章,我們會爲這個對話框添加美化的按鈕。