MFC的Button和Static控件
生活随笔
收集整理的這篇文章主要介紹了
MFC的Button和Static控件
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
最近要寫(xiě)一個(gè)MFC的對(duì)話框程序,發(fā)現(xiàn)要把MFC的對(duì)話框?qū)懙挠猩庶c(diǎn)并不容易,不像在C#里設(shè)置屬性指就好,而是要自己去寫(xiě)一些代碼完成對(duì)話框的繪畫(huà)操作。比如一個(gè)簡(jiǎn)單的鼠標(biāo)移入、移出操作,都要自己去寫(xiě)代碼。由于我只用到了Button和Static兩種控件,一切看上去還是比較順利,所以談?wù)勛约旱慕?jīng)驗(yàn)。
1、對(duì)話框的背景MFC中沒(méi)有屬性能夠設(shè)定對(duì)話框的背景顏色或是圖片,需要我們?cè)诔绦蛑羞M(jìn)行操作。首先,需要實(shí)現(xiàn)WM_CTLCOLOR的消息操作,通過(guò)這個(gè)消息我們能夠控制對(duì)話框以及Static控件(包括Group)的背景色、前景色。該消息的處理函數(shù)原型如下:HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);其中,通過(guò)pDC能夠文件的顏色、背景模式,通過(guò)pWnd可以獲取正在繪制的控件ID,通過(guò)nCtlColor可以判斷當(dāng)前正在繪制的控件類型。這里我需要控制對(duì)話框的背景,所以要進(jìn)行如下操作:HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);? ? if (nCtlColor == CTLCOLOR_DLG){return m_backgroundBrush; ? ?//返回對(duì)話框背景的畫(huà)刷}else if (nCtlColor == CTLCOLOR_STATIC){pDC->SetBkMode(TRANSPARENT); ? ?//所有Static控件的背景色為透明? ? ? ? if (pWnd->GetDlgCtrlID() == IDC_NOTE)? ? ? ? { ? ??pDC->SetTextColor(RGB(255, 255, 255)); ? ?//針對(duì)特殊的static控件,設(shè)置單獨(dú)的文字顏色? ? ? ? }}return?hbr; //不是要自繪的控件,返回默認(rèn)值
2、按鈕(Button)控件一開(kāi)始很奇怪,在WM_CTLCOLOR的消息處理函數(shù)進(jìn)行如下操作竟然沒(méi)有用:? ? ? ? ? ??if (pWnd->GetDlgCtrlID() == IDB_TEST) ?//按鈕文字顏色? ? ? ? ? ??{? ? ? ? ? ? ? ? pDC->SetTextColor(RGB(0, 0, 255));
? ? ? ? ? ??}? ? 原來(lái)按鈕控件的顏色、背景等屬性無(wú)法通過(guò)WM_CTLCOLOR消息實(shí)現(xiàn),要改變這些屬性,就必須要自己從CButton類繼承一個(gè)類,然后改寫(xiě)其繪畫(huà)函數(shù)——DrawItem。通過(guò)DrawItem函數(shù)的參數(shù)可以獲取控件的大小、狀態(tài)、類型、繪畫(huà)DC等信息,有了它們,重繪Button就簡(jiǎn)單了。下面給出簡(jiǎn)單實(shí)例:void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct){ if (lpDrawItemStruct->CtlType != ODT_BUTTON) ?//由于繼承自CButton,這一句肯定成立 { return; }
CRect rect = ?lpDrawItemStruct->rcItem; CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC); UINT state = lpDrawItemStruct->itemState;
TCHAR strText[MAX_PATH] = {0}; ::GetWindowText(m_hWnd, strText, MAX_PATH);
//獲取按鈕的狀態(tài) if (state & ODS_FOCUS) { m_bSelected = TRUE; } else { m_bSelected = FALSE; }
//根據(jù)按鈕的狀態(tài)填充按鈕的底色 if (m_bOver) { pDC->FillRect(&rect, &m_overBrush);? } else if (m_bSelected) { pDC->FillRect(&rect, &m_selectedBrush);? } else { pDC->FillRect(&rect, &m_normalBrush);? }
// 畫(huà)按鈕標(biāo)題,水平和垂直都居中。把DC當(dāng)作一個(gè)畫(huà)布 // 如果要使DT_VCENTER(垂直居中)有效,必須同時(shí)設(shè)置DT_SINGLELINE(單行)風(fēng)格 if (_tcslen(strText) > 0) { pDC->SelectObject(m_font); pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(RGB(255, 255, 255)); pDC->DrawText(strText, &lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_CENTER | DT_VCENTER);? }}但是寫(xiě)了以上代碼,發(fā)現(xiàn)還是不管用,原來(lái),還需要設(shè)置按鈕的屬性為自繪類型,即需要設(shè)置BS_OWNERDRAW屬性。這個(gè)可以在對(duì)話框的OnInitDialog函數(shù)或Button類的PreSubClassWindow函數(shù)中,調(diào)用Button的SetButtonStyle方法設(shè)置,如下:UINT uStyle = GetButtonStyle(); ? ? ? // 得到按鈕的愿風(fēng)格? ? SetButtonStyle(uStyle | BS_OWNERDRAW); ? ? // 加入自畫(huà)風(fēng)格為了判斷按鈕當(dāng)前的狀態(tài),如是否有鼠標(biāo)停留、焦點(diǎn)等,就需要跟蹤鼠標(biāo)的移動(dòng),這個(gè)可以通過(guò)WM_MOUSEMOVE消息實(shí)現(xiàn)。但是鼠標(biāo)移入、移出的操作無(wú)法通過(guò)Vc的Wizard實(shí)現(xiàn),在Vs2010里可以通過(guò)Wizard添加,但是函數(shù)不會(huì)被調(diào)用。為了能夠知道鼠標(biāo)移入、移出操作,需要在WM_MOUSEMOVE消息中增加如下代碼,已跟蹤鼠標(biāo)事件:if (!m_bTrack) ? ?//記錄是否已經(jīng)設(shè)置跟蹤事件 { TRACKMOUSEEVENT ent = {0}; ent.dwFlags = TME_HOVER | TME_LEAVE; ent.cbSize = sizeof(TRACKMOUSEEVENT); ent.dwHoverTime = 10; ent.hwndTrack = this->m_hWnd; m_bTrack = TrackMouseEvent(&ent); }通過(guò)以上代碼,我們就可以收到WM_MOUSEHOVER、WM_MOUSELEAVE事件,通過(guò)將鼠標(biāo)停留時(shí)間ent.dwHoverTime(觸發(fā)WM_MOUSEHOVER的時(shí)間)設(shè)置為足夠短,我們就可以模擬出鼠標(biāo)移入事件。為了能夠處理這兩個(gè)消息,需要通過(guò)MFCWizard增加WM_MOUSEHOVER、WM_MOUSELEAVE的事件處理函數(shù)。? ??void CMyButton::OnMouseHover(UINT nFlags, CPoint point){ m_bOver = TRUE; InvalidateRect(NULL, TRUE);
CButton::OnMouseHover(nFlags, point);}? ? void CMyButton::OnMouseLeave(){ m_bTrack = FALSE; m_bOver = FALSE; InvalidateRect(NULL, FALSE);
CButton::OnMouseLeave();}
3、Static控件雖然可以通過(guò)對(duì)話框的WM_CTLCOLOR消息設(shè)置Static控件的字體顏色和背景模式,但是如果要更好地控制其字體、背景等屬性,以及添加鼠標(biāo)事件,就要自己寫(xiě)代碼了。方法很簡(jiǎn)單,和Button類似,需要自己寫(xiě)一個(gè)繼承自CStatic的類,然后改寫(xiě)DrawItem方法,實(shí)現(xiàn)自己的繪畫(huà)方法。? ? 只是有一點(diǎn)需要注意,默認(rèn)情況下Static是不發(fā)送消息的,即鼠標(biāo)點(diǎn)擊、移動(dòng)等事件是沒(méi)有效果的,需要設(shè)置SS_NOTIFY屬性,這可以在繼承Static類的PreSubClassWindow中進(jìn)行入操作設(shè)置:ModifyStyle(SS_TYPEMASK, SS_OWNERDRAW|SS_NOTIFY);以上代碼同時(shí)設(shè)置自繪和消息通知屬性。
4、無(wú)標(biāo)題對(duì)話框的移動(dòng)在Windows的應(yīng)用程序中,都可以通過(guò)點(diǎn)擊標(biāo)題欄拖拽窗口進(jìn)行移動(dòng),但是如果窗口沒(méi)有標(biāo)題欄怎么辦呢,即將對(duì)話框的Border屬性設(shè)置為None,如何實(shí)現(xiàn)窗口移動(dòng)呢?在我的對(duì)話框應(yīng)用中,在窗口頂部放了一個(gè)Static控件,想把它作為窗口的標(biāo)題欄,通過(guò)拖拽Static空間拖動(dòng)窗口。有兩種方法可以實(shí)習(xí)這個(gè)目的。一個(gè)很容易想到的方法是寫(xiě)一個(gè)繼承自CStatic類的自繪控件,然后關(guān)聯(lián)該類的對(duì)象到充當(dāng)標(biāo)題欄的Static控件。通過(guò)處理Static控件的WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP消息,就可以得知鼠標(biāo)是否按下、移動(dòng)、松開(kāi),然后在WM_MOUSEMOVE的處理函數(shù)計(jì)算鼠標(biāo)移動(dòng)的距離,然后獲得父窗口的指針,通過(guò)MoveWindow函數(shù)移動(dòng)父窗口。顯然,這種方法比較復(fù)雜,需要進(jìn)行的操作也比較繁瑣。? ? 既然點(diǎn)擊標(biāo)題欄能夠移動(dòng)窗口,而點(diǎn)擊其他地方卻效果不同,那么windows肯定通過(guò)什么標(biāo)志來(lái)判斷要采取什么操作,我們應(yīng)該能夠不需要自己去實(shí)現(xiàn)移動(dòng)窗口,而是告訴Windows,點(diǎn)擊Static這個(gè)“標(biāo)題”時(shí)就是點(diǎn)擊真正的標(biāo)題欄。控件在響應(yīng)點(diǎn)擊操作之前,都會(huì)發(fā)送WM_NCHITTEST消息,用于判斷鼠標(biāo)點(diǎn)擊的位置,而消息處理函數(shù)的返回值就指出了位置。WM_NCHITTEST的處理函數(shù)如下:? ??LRESULT CWinAppDlg::OnNcHitTest(CPoint point){ CRect rect; GetDlgItem(IDC_TITLE)->GetClientRect(rect); ClientToScreen(rect);
return rect.PtInRect(point) ? HTCAPTION : CDialogEx::OnNcHitTest(point);}在上面的代碼中,首先獲得Static控件IDC_TITLE的位置,然后判斷鼠標(biāo)點(diǎn)擊位置是否位于控件內(nèi),如果是,則返回HTCAPTION,告訴Windows點(diǎn)擊的是標(biāo)題欄,否則返回默認(rèn)值。顯然這種移動(dòng)窗口的做法要簡(jiǎn)單的多,而且它利用MFC的原理進(jìn)行操作,更穩(wěn)定也更具兼容性。這幾句代碼也有一個(gè)限制,即標(biāo)題欄只能是Static控件,因?yàn)镾tatic默認(rèn)是不接收事件、消息,所以WM_NCHITTEST由對(duì)話框進(jìn)行處理,如果是Button等其他控件,WM_NCHITTEST就會(huì)發(fā)送到控件而不是對(duì)話框上。
1、對(duì)話框的背景MFC中沒(méi)有屬性能夠設(shè)定對(duì)話框的背景顏色或是圖片,需要我們?cè)诔绦蛑羞M(jìn)行操作。首先,需要實(shí)現(xiàn)WM_CTLCOLOR的消息操作,通過(guò)這個(gè)消息我們能夠控制對(duì)話框以及Static控件(包括Group)的背景色、前景色。該消息的處理函數(shù)原型如下:HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);其中,通過(guò)pDC能夠文件的顏色、背景模式,通過(guò)pWnd可以獲取正在繪制的控件ID,通過(guò)nCtlColor可以判斷當(dāng)前正在繪制的控件類型。這里我需要控制對(duì)話框的背景,所以要進(jìn)行如下操作:HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);? ? if (nCtlColor == CTLCOLOR_DLG){return m_backgroundBrush; ? ?//返回對(duì)話框背景的畫(huà)刷}else if (nCtlColor == CTLCOLOR_STATIC){pDC->SetBkMode(TRANSPARENT); ? ?//所有Static控件的背景色為透明? ? ? ? if (pWnd->GetDlgCtrlID() == IDC_NOTE)? ? ? ? { ? ??pDC->SetTextColor(RGB(255, 255, 255)); ? ?//針對(duì)特殊的static控件,設(shè)置單獨(dú)的文字顏色? ? ? ? }}return?hbr; //不是要自繪的控件,返回默認(rèn)值
2、按鈕(Button)控件一開(kāi)始很奇怪,在WM_CTLCOLOR的消息處理函數(shù)進(jìn)行如下操作竟然沒(méi)有用:? ? ? ? ? ??if (pWnd->GetDlgCtrlID() == IDB_TEST) ?//按鈕文字顏色? ? ? ? ? ??{? ? ? ? ? ? ? ? pDC->SetTextColor(RGB(0, 0, 255));
? ? ? ? ? ??}? ? 原來(lái)按鈕控件的顏色、背景等屬性無(wú)法通過(guò)WM_CTLCOLOR消息實(shí)現(xiàn),要改變這些屬性,就必須要自己從CButton類繼承一個(gè)類,然后改寫(xiě)其繪畫(huà)函數(shù)——DrawItem。通過(guò)DrawItem函數(shù)的參數(shù)可以獲取控件的大小、狀態(tài)、類型、繪畫(huà)DC等信息,有了它們,重繪Button就簡(jiǎn)單了。下面給出簡(jiǎn)單實(shí)例:void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct){ if (lpDrawItemStruct->CtlType != ODT_BUTTON) ?//由于繼承自CButton,這一句肯定成立 { return; }
CRect rect = ?lpDrawItemStruct->rcItem; CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC); UINT state = lpDrawItemStruct->itemState;
TCHAR strText[MAX_PATH] = {0}; ::GetWindowText(m_hWnd, strText, MAX_PATH);
//獲取按鈕的狀態(tài) if (state & ODS_FOCUS) { m_bSelected = TRUE; } else { m_bSelected = FALSE; }
//根據(jù)按鈕的狀態(tài)填充按鈕的底色 if (m_bOver) { pDC->FillRect(&rect, &m_overBrush);? } else if (m_bSelected) { pDC->FillRect(&rect, &m_selectedBrush);? } else { pDC->FillRect(&rect, &m_normalBrush);? }
// 畫(huà)按鈕標(biāo)題,水平和垂直都居中。把DC當(dāng)作一個(gè)畫(huà)布 // 如果要使DT_VCENTER(垂直居中)有效,必須同時(shí)設(shè)置DT_SINGLELINE(單行)風(fēng)格 if (_tcslen(strText) > 0) { pDC->SelectObject(m_font); pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(RGB(255, 255, 255)); pDC->DrawText(strText, &lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_CENTER | DT_VCENTER);? }}但是寫(xiě)了以上代碼,發(fā)現(xiàn)還是不管用,原來(lái),還需要設(shè)置按鈕的屬性為自繪類型,即需要設(shè)置BS_OWNERDRAW屬性。這個(gè)可以在對(duì)話框的OnInitDialog函數(shù)或Button類的PreSubClassWindow函數(shù)中,調(diào)用Button的SetButtonStyle方法設(shè)置,如下:UINT uStyle = GetButtonStyle(); ? ? ? // 得到按鈕的愿風(fēng)格? ? SetButtonStyle(uStyle | BS_OWNERDRAW); ? ? // 加入自畫(huà)風(fēng)格為了判斷按鈕當(dāng)前的狀態(tài),如是否有鼠標(biāo)停留、焦點(diǎn)等,就需要跟蹤鼠標(biāo)的移動(dòng),這個(gè)可以通過(guò)WM_MOUSEMOVE消息實(shí)現(xiàn)。但是鼠標(biāo)移入、移出的操作無(wú)法通過(guò)Vc的Wizard實(shí)現(xiàn),在Vs2010里可以通過(guò)Wizard添加,但是函數(shù)不會(huì)被調(diào)用。為了能夠知道鼠標(biāo)移入、移出操作,需要在WM_MOUSEMOVE消息中增加如下代碼,已跟蹤鼠標(biāo)事件:if (!m_bTrack) ? ?//記錄是否已經(jīng)設(shè)置跟蹤事件 { TRACKMOUSEEVENT ent = {0}; ent.dwFlags = TME_HOVER | TME_LEAVE; ent.cbSize = sizeof(TRACKMOUSEEVENT); ent.dwHoverTime = 10; ent.hwndTrack = this->m_hWnd; m_bTrack = TrackMouseEvent(&ent); }通過(guò)以上代碼,我們就可以收到WM_MOUSEHOVER、WM_MOUSELEAVE事件,通過(guò)將鼠標(biāo)停留時(shí)間ent.dwHoverTime(觸發(fā)WM_MOUSEHOVER的時(shí)間)設(shè)置為足夠短,我們就可以模擬出鼠標(biāo)移入事件。為了能夠處理這兩個(gè)消息,需要通過(guò)MFCWizard增加WM_MOUSEHOVER、WM_MOUSELEAVE的事件處理函數(shù)。? ??void CMyButton::OnMouseHover(UINT nFlags, CPoint point){ m_bOver = TRUE; InvalidateRect(NULL, TRUE);
CButton::OnMouseHover(nFlags, point);}? ? void CMyButton::OnMouseLeave(){ m_bTrack = FALSE; m_bOver = FALSE; InvalidateRect(NULL, FALSE);
CButton::OnMouseLeave();}
3、Static控件雖然可以通過(guò)對(duì)話框的WM_CTLCOLOR消息設(shè)置Static控件的字體顏色和背景模式,但是如果要更好地控制其字體、背景等屬性,以及添加鼠標(biāo)事件,就要自己寫(xiě)代碼了。方法很簡(jiǎn)單,和Button類似,需要自己寫(xiě)一個(gè)繼承自CStatic的類,然后改寫(xiě)DrawItem方法,實(shí)現(xiàn)自己的繪畫(huà)方法。? ? 只是有一點(diǎn)需要注意,默認(rèn)情況下Static是不發(fā)送消息的,即鼠標(biāo)點(diǎn)擊、移動(dòng)等事件是沒(méi)有效果的,需要設(shè)置SS_NOTIFY屬性,這可以在繼承Static類的PreSubClassWindow中進(jìn)行入操作設(shè)置:ModifyStyle(SS_TYPEMASK, SS_OWNERDRAW|SS_NOTIFY);以上代碼同時(shí)設(shè)置自繪和消息通知屬性。
4、無(wú)標(biāo)題對(duì)話框的移動(dòng)在Windows的應(yīng)用程序中,都可以通過(guò)點(diǎn)擊標(biāo)題欄拖拽窗口進(jìn)行移動(dòng),但是如果窗口沒(méi)有標(biāo)題欄怎么辦呢,即將對(duì)話框的Border屬性設(shè)置為None,如何實(shí)現(xiàn)窗口移動(dòng)呢?在我的對(duì)話框應(yīng)用中,在窗口頂部放了一個(gè)Static控件,想把它作為窗口的標(biāo)題欄,通過(guò)拖拽Static空間拖動(dòng)窗口。有兩種方法可以實(shí)習(xí)這個(gè)目的。一個(gè)很容易想到的方法是寫(xiě)一個(gè)繼承自CStatic類的自繪控件,然后關(guān)聯(lián)該類的對(duì)象到充當(dāng)標(biāo)題欄的Static控件。通過(guò)處理Static控件的WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP消息,就可以得知鼠標(biāo)是否按下、移動(dòng)、松開(kāi),然后在WM_MOUSEMOVE的處理函數(shù)計(jì)算鼠標(biāo)移動(dòng)的距離,然后獲得父窗口的指針,通過(guò)MoveWindow函數(shù)移動(dòng)父窗口。顯然,這種方法比較復(fù)雜,需要進(jìn)行的操作也比較繁瑣。? ? 既然點(diǎn)擊標(biāo)題欄能夠移動(dòng)窗口,而點(diǎn)擊其他地方卻效果不同,那么windows肯定通過(guò)什么標(biāo)志來(lái)判斷要采取什么操作,我們應(yīng)該能夠不需要自己去實(shí)現(xiàn)移動(dòng)窗口,而是告訴Windows,點(diǎn)擊Static這個(gè)“標(biāo)題”時(shí)就是點(diǎn)擊真正的標(biāo)題欄。控件在響應(yīng)點(diǎn)擊操作之前,都會(huì)發(fā)送WM_NCHITTEST消息,用于判斷鼠標(biāo)點(diǎn)擊的位置,而消息處理函數(shù)的返回值就指出了位置。WM_NCHITTEST的處理函數(shù)如下:? ??LRESULT CWinAppDlg::OnNcHitTest(CPoint point){ CRect rect; GetDlgItem(IDC_TITLE)->GetClientRect(rect); ClientToScreen(rect);
return rect.PtInRect(point) ? HTCAPTION : CDialogEx::OnNcHitTest(point);}在上面的代碼中,首先獲得Static控件IDC_TITLE的位置,然后判斷鼠標(biāo)點(diǎn)擊位置是否位于控件內(nèi),如果是,則返回HTCAPTION,告訴Windows點(diǎn)擊的是標(biāo)題欄,否則返回默認(rèn)值。顯然這種移動(dòng)窗口的做法要簡(jiǎn)單的多,而且它利用MFC的原理進(jìn)行操作,更穩(wěn)定也更具兼容性。這幾句代碼也有一個(gè)限制,即標(biāo)題欄只能是Static控件,因?yàn)镾tatic默認(rèn)是不接收事件、消息,所以WM_NCHITTEST由對(duì)話框進(jìn)行處理,如果是Button等其他控件,WM_NCHITTEST就會(huì)發(fā)送到控件而不是對(duì)話框上。
轉(zhuǎn)載于:https://blog.51cto.com/jslmes/1161756
總結(jié)
以上是生活随笔為你收集整理的MFC的Button和Static控件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。