MFC的Button和Static控件
生活随笔
收集整理的這篇文章主要介紹了
MFC的Button和Static控件
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
最近要寫一個MFC的對話框程序,發現要把MFC的對話框寫的有色彩點并不容易,不像在C#里設置屬性指就好,而是要自己去寫一些代碼完成對話框的繪畫操作。比如一個簡單的鼠標移入、移出操作,都要自己去寫代碼。由于我只用到了Button和Static兩種控件,一切看上去還是比較順利,所以談談自己的經驗。
1、對話框的背景MFC中沒有屬性能夠設定對話框的背景顏色或是圖片,需要我們在程序中進行操作。首先,需要實現WM_CTLCOLOR的消息操作,通過這個消息我們能夠控制對話框以及Static控件(包括Group)的背景色、前景色。該消息的處理函數原型如下:HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);其中,通過pDC能夠文件的顏色、背景模式,通過pWnd可以獲取正在繪制的控件ID,通過nCtlColor可以判斷當前正在繪制的控件類型。這里我需要控制對話框的背景,所以要進行如下操作:HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);? ? if (nCtlColor == CTLCOLOR_DLG){return m_backgroundBrush; ? ?//返回對話框背景的畫刷}else if (nCtlColor == CTLCOLOR_STATIC){pDC->SetBkMode(TRANSPARENT); ? ?//所有Static控件的背景色為透明? ? ? ? if (pWnd->GetDlgCtrlID() == IDC_NOTE)? ? ? ? { ? ??pDC->SetTextColor(RGB(255, 255, 255)); ? ?//針對特殊的static控件,設置單獨的文字顏色? ? ? ? }}return?hbr; //不是要自繪的控件,返回默認值
2、按鈕(Button)控件一開始很奇怪,在WM_CTLCOLOR的消息處理函數進行如下操作竟然沒有用:? ? ? ? ? ??if (pWnd->GetDlgCtrlID() == IDB_TEST) ?//按鈕文字顏色? ? ? ? ? ??{? ? ? ? ? ? ? ? pDC->SetTextColor(RGB(0, 0, 255));
? ? ? ? ? ??}? ? 原來按鈕控件的顏色、背景等屬性無法通過WM_CTLCOLOR消息實現,要改變這些屬性,就必須要自己從CButton類繼承一個類,然后改寫其繪畫函數——DrawItem。通過DrawItem函數的參數可以獲取控件的大小、狀態、類型、繪畫DC等信息,有了它們,重繪Button就簡單了。下面給出簡單實例: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);
//獲取按鈕的狀態 if (state & ODS_FOCUS) { m_bSelected = TRUE; } else { m_bSelected = FALSE; }
//根據按鈕的狀態填充按鈕的底色 if (m_bOver) { pDC->FillRect(&rect, &m_overBrush);? } else if (m_bSelected) { pDC->FillRect(&rect, &m_selectedBrush);? } else { pDC->FillRect(&rect, &m_normalBrush);? }
// 畫按鈕標題,水平和垂直都居中。把DC當作一個畫布 // 如果要使DT_VCENTER(垂直居中)有效,必須同時設置DT_SINGLELINE(單行)風格 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);? }}但是寫了以上代碼,發現還是不管用,原來,還需要設置按鈕的屬性為自繪類型,即需要設置BS_OWNERDRAW屬性。這個可以在對話框的OnInitDialog函數或Button類的PreSubClassWindow函數中,調用Button的SetButtonStyle方法設置,如下:UINT uStyle = GetButtonStyle(); ? ? ? // 得到按鈕的愿風格? ? SetButtonStyle(uStyle | BS_OWNERDRAW); ? ? // 加入自畫風格為了判斷按鈕當前的狀態,如是否有鼠標停留、焦點等,就需要跟蹤鼠標的移動,這個可以通過WM_MOUSEMOVE消息實現。但是鼠標移入、移出的操作無法通過Vc的Wizard實現,在Vs2010里可以通過Wizard添加,但是函數不會被調用。為了能夠知道鼠標移入、移出操作,需要在WM_MOUSEMOVE消息中增加如下代碼,已跟蹤鼠標事件:if (!m_bTrack) ? ?//記錄是否已經設置跟蹤事件 { 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); }通過以上代碼,我們就可以收到WM_MOUSEHOVER、WM_MOUSELEAVE事件,通過將鼠標停留時間ent.dwHoverTime(觸發WM_MOUSEHOVER的時間)設置為足夠短,我們就可以模擬出鼠標移入事件。為了能夠處理這兩個消息,需要通過MFCWizard增加WM_MOUSEHOVER、WM_MOUSELEAVE的事件處理函數。? ??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控件雖然可以通過對話框的WM_CTLCOLOR消息設置Static控件的字體顏色和背景模式,但是如果要更好地控制其字體、背景等屬性,以及添加鼠標事件,就要自己寫代碼了。方法很簡單,和Button類似,需要自己寫一個繼承自CStatic的類,然后改寫DrawItem方法,實現自己的繪畫方法。? ? 只是有一點需要注意,默認情況下Static是不發送消息的,即鼠標點擊、移動等事件是沒有效果的,需要設置SS_NOTIFY屬性,這可以在繼承Static類的PreSubClassWindow中進行入操作設置:ModifyStyle(SS_TYPEMASK, SS_OWNERDRAW|SS_NOTIFY);以上代碼同時設置自繪和消息通知屬性。
4、無標題對話框的移動在Windows的應用程序中,都可以通過點擊標題欄拖拽窗口進行移動,但是如果窗口沒有標題欄怎么辦呢,即將對話框的Border屬性設置為None,如何實現窗口移動呢?在我的對話框應用中,在窗口頂部放了一個Static控件,想把它作為窗口的標題欄,通過拖拽Static空間拖動窗口。有兩種方法可以實習這個目的。一個很容易想到的方法是寫一個繼承自CStatic類的自繪控件,然后關聯該類的對象到充當標題欄的Static控件。通過處理Static控件的WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP消息,就可以得知鼠標是否按下、移動、松開,然后在WM_MOUSEMOVE的處理函數計算鼠標移動的距離,然后獲得父窗口的指針,通過MoveWindow函數移動父窗口。顯然,這種方法比較復雜,需要進行的操作也比較繁瑣。? ? 既然點擊標題欄能夠移動窗口,而點擊其他地方卻效果不同,那么windows肯定通過什么標志來判斷要采取什么操作,我們應該能夠不需要自己去實現移動窗口,而是告訴Windows,點擊Static這個“標題”時就是點擊真正的標題欄。控件在響應點擊操作之前,都會發送WM_NCHITTEST消息,用于判斷鼠標點擊的位置,而消息處理函數的返回值就指出了位置。WM_NCHITTEST的處理函數如下:? ??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的位置,然后判斷鼠標點擊位置是否位于控件內,如果是,則返回HTCAPTION,告訴Windows點擊的是標題欄,否則返回默認值。顯然這種移動窗口的做法要簡單的多,而且它利用MFC的原理進行操作,更穩定也更具兼容性。這幾句代碼也有一個限制,即標題欄只能是Static控件,因為Static默認是不接收事件、消息,所以WM_NCHITTEST由對話框進行處理,如果是Button等其他控件,WM_NCHITTEST就會發送到控件而不是對話框上。
1、對話框的背景MFC中沒有屬性能夠設定對話框的背景顏色或是圖片,需要我們在程序中進行操作。首先,需要實現WM_CTLCOLOR的消息操作,通過這個消息我們能夠控制對話框以及Static控件(包括Group)的背景色、前景色。該消息的處理函數原型如下:HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);其中,通過pDC能夠文件的顏色、背景模式,通過pWnd可以獲取正在繪制的控件ID,通過nCtlColor可以判斷當前正在繪制的控件類型。這里我需要控制對話框的背景,所以要進行如下操作:HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);? ? if (nCtlColor == CTLCOLOR_DLG){return m_backgroundBrush; ? ?//返回對話框背景的畫刷}else if (nCtlColor == CTLCOLOR_STATIC){pDC->SetBkMode(TRANSPARENT); ? ?//所有Static控件的背景色為透明? ? ? ? if (pWnd->GetDlgCtrlID() == IDC_NOTE)? ? ? ? { ? ??pDC->SetTextColor(RGB(255, 255, 255)); ? ?//針對特殊的static控件,設置單獨的文字顏色? ? ? ? }}return?hbr; //不是要自繪的控件,返回默認值
2、按鈕(Button)控件一開始很奇怪,在WM_CTLCOLOR的消息處理函數進行如下操作竟然沒有用:? ? ? ? ? ??if (pWnd->GetDlgCtrlID() == IDB_TEST) ?//按鈕文字顏色? ? ? ? ? ??{? ? ? ? ? ? ? ? pDC->SetTextColor(RGB(0, 0, 255));
? ? ? ? ? ??}? ? 原來按鈕控件的顏色、背景等屬性無法通過WM_CTLCOLOR消息實現,要改變這些屬性,就必須要自己從CButton類繼承一個類,然后改寫其繪畫函數——DrawItem。通過DrawItem函數的參數可以獲取控件的大小、狀態、類型、繪畫DC等信息,有了它們,重繪Button就簡單了。下面給出簡單實例: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);
//獲取按鈕的狀態 if (state & ODS_FOCUS) { m_bSelected = TRUE; } else { m_bSelected = FALSE; }
//根據按鈕的狀態填充按鈕的底色 if (m_bOver) { pDC->FillRect(&rect, &m_overBrush);? } else if (m_bSelected) { pDC->FillRect(&rect, &m_selectedBrush);? } else { pDC->FillRect(&rect, &m_normalBrush);? }
// 畫按鈕標題,水平和垂直都居中。把DC當作一個畫布 // 如果要使DT_VCENTER(垂直居中)有效,必須同時設置DT_SINGLELINE(單行)風格 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);? }}但是寫了以上代碼,發現還是不管用,原來,還需要設置按鈕的屬性為自繪類型,即需要設置BS_OWNERDRAW屬性。這個可以在對話框的OnInitDialog函數或Button類的PreSubClassWindow函數中,調用Button的SetButtonStyle方法設置,如下:UINT uStyle = GetButtonStyle(); ? ? ? // 得到按鈕的愿風格? ? SetButtonStyle(uStyle | BS_OWNERDRAW); ? ? // 加入自畫風格為了判斷按鈕當前的狀態,如是否有鼠標停留、焦點等,就需要跟蹤鼠標的移動,這個可以通過WM_MOUSEMOVE消息實現。但是鼠標移入、移出的操作無法通過Vc的Wizard實現,在Vs2010里可以通過Wizard添加,但是函數不會被調用。為了能夠知道鼠標移入、移出操作,需要在WM_MOUSEMOVE消息中增加如下代碼,已跟蹤鼠標事件:if (!m_bTrack) ? ?//記錄是否已經設置跟蹤事件 { 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); }通過以上代碼,我們就可以收到WM_MOUSEHOVER、WM_MOUSELEAVE事件,通過將鼠標停留時間ent.dwHoverTime(觸發WM_MOUSEHOVER的時間)設置為足夠短,我們就可以模擬出鼠標移入事件。為了能夠處理這兩個消息,需要通過MFCWizard增加WM_MOUSEHOVER、WM_MOUSELEAVE的事件處理函數。? ??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控件雖然可以通過對話框的WM_CTLCOLOR消息設置Static控件的字體顏色和背景模式,但是如果要更好地控制其字體、背景等屬性,以及添加鼠標事件,就要自己寫代碼了。方法很簡單,和Button類似,需要自己寫一個繼承自CStatic的類,然后改寫DrawItem方法,實現自己的繪畫方法。? ? 只是有一點需要注意,默認情況下Static是不發送消息的,即鼠標點擊、移動等事件是沒有效果的,需要設置SS_NOTIFY屬性,這可以在繼承Static類的PreSubClassWindow中進行入操作設置:ModifyStyle(SS_TYPEMASK, SS_OWNERDRAW|SS_NOTIFY);以上代碼同時設置自繪和消息通知屬性。
4、無標題對話框的移動在Windows的應用程序中,都可以通過點擊標題欄拖拽窗口進行移動,但是如果窗口沒有標題欄怎么辦呢,即將對話框的Border屬性設置為None,如何實現窗口移動呢?在我的對話框應用中,在窗口頂部放了一個Static控件,想把它作為窗口的標題欄,通過拖拽Static空間拖動窗口。有兩種方法可以實習這個目的。一個很容易想到的方法是寫一個繼承自CStatic類的自繪控件,然后關聯該類的對象到充當標題欄的Static控件。通過處理Static控件的WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP消息,就可以得知鼠標是否按下、移動、松開,然后在WM_MOUSEMOVE的處理函數計算鼠標移動的距離,然后獲得父窗口的指針,通過MoveWindow函數移動父窗口。顯然,這種方法比較復雜,需要進行的操作也比較繁瑣。? ? 既然點擊標題欄能夠移動窗口,而點擊其他地方卻效果不同,那么windows肯定通過什么標志來判斷要采取什么操作,我們應該能夠不需要自己去實現移動窗口,而是告訴Windows,點擊Static這個“標題”時就是點擊真正的標題欄。控件在響應點擊操作之前,都會發送WM_NCHITTEST消息,用于判斷鼠標點擊的位置,而消息處理函數的返回值就指出了位置。WM_NCHITTEST的處理函數如下:? ??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的位置,然后判斷鼠標點擊位置是否位于控件內,如果是,則返回HTCAPTION,告訴Windows點擊的是標題欄,否則返回默認值。顯然這種移動窗口的做法要簡單的多,而且它利用MFC的原理進行操作,更穩定也更具兼容性。這幾句代碼也有一個限制,即標題欄只能是Static控件,因為Static默認是不接收事件、消息,所以WM_NCHITTEST由對話框進行處理,如果是Button等其他控件,WM_NCHITTEST就會發送到控件而不是對話框上。
轉載于:https://blog.51cto.com/jslmes/1161756
總結
以上是生活随笔為你收集整理的MFC的Button和Static控件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 感想四
- 下一篇: JavaScript中使Promise模