有一種按鍵,看起來是一幅完整的圖片,當(dāng)鼠標(biāo)移到按鍵區(qū)域時(shí),圖片的一部分凸現(xiàn),形成一個(gè)按鍵,當(dāng)鼠標(biāo)移走時(shí)又恢復(fù)原來狀態(tài)。 最近,看了一些關(guān)于浮動(dòng)按鍵的代碼,其原理大致上跟CBitmapButton差不多,用數(shù)幅位圖代表按鍵的各個(gè)狀態(tài),響應(yīng)鼠標(biāo)的各種消息來設(shè)置按鍵的狀態(tài),實(shí)現(xiàn)按鍵的浮動(dòng)顯示,但是這樣的按鍵卻不能和周圍的背景混和成一幅圖片。 為了實(shí)現(xiàn)“透明”按鍵,可以簡(jiǎn)單地做個(gè)試驗(yàn):先在對(duì)話框中加入一個(gè)BUTTON,通過屬性框選“Owner Draw”風(fēng)格,再加入一個(gè)PICTURE,并加入圖片,將BUTTON移到PICTURE上。運(yùn)行結(jié)果發(fā)現(xiàn),按鍵沒有顯示出來,但在按鍵區(qū)域按下鼠標(biāo)時(shí),該按鍵仍然能發(fā)出WM_COMMAND消息,這樣一個(gè)純透明的按鍵建立了。顯然,這個(gè)按鍵是毫無使用意義的,必須讓用戶容易覺察到按鍵的位置,可以把這個(gè)按鍵改造一下:
(首先從CButton派生出一個(gè)新類CDrawButton) 把按鍵的標(biāo)題顯示出來這個(gè)實(shí)現(xiàn)起來比較簡(jiǎn)單,我們可以重載CButton類的成員函數(shù)DrawItem(), void CDrawButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CDC dc; CRect rect=lpDrawItemStruct->rcItem;//得到按鍵區(qū)域 CString sCaption; dc.Attach(lpDrawItemStruct->hDC); //得到設(shè)備環(huán)境CDC VERIFY(lpDrawItemStruct->CtlType==ODT_BUTTON); GetWindowText(sCaption);//得到按鍵的標(biāo)題 dc.SetBkMode(TRANSPARENT);//透明顯示 CFont* m_pOldFont=dc.SelectObject(m_pFont); dc.DrawText(sCaption,&rect,DT_CENTER|DT_VCENTER|DT_SINGLELINE); dc.SelectObject(m_pOldFont); }
其中的m_pFont是成員變量,它保存了對(duì)話框的字體指針,為了按鍵的標(biāo)題風(fēng)格與對(duì)話框的字體風(fēng)格一致,在初始化時(shí)調(diào)用對(duì)話框的成員函數(shù)GetFont()即可得到指向?qū)υ捒蜃煮w的CFont類指針。
使按鍵浮動(dòng)顯示 要通過自繪來表示按鍵的各種狀態(tài),可填寫DRAWITEMSTRUCT來通知DrawItem()函數(shù)需要做什么,我們先了解一下DRAWITEMSTRUCT: typedef struct tagDRAWITEMSTRUCT{ UINT CtlType; // 控件類型 UINT CtlID;// 控件的ID號(hào) UNIT itemID;//菜單項(xiàng)的索引 UINT itemAction;// 繪圖操作 UINT itemState; // 狀態(tài) HWND hwndItem; // 控件的窗口句柄 HDC hDC; // 相關(guān)的設(shè)備環(huán)境 RECT rcItem;//控件的范圍 DWORD itemData;// 指定與菜單項(xiàng)相聯(lián)系的應(yīng)用程序定義的32位值 }DRAWITEMSTRUCT;
利用這個(gè)結(jié)構(gòu)先做一個(gè)按鍵狀態(tài)設(shè)置函數(shù):
void CDrawButton::SetButtonMode(UINT action, UINT mode) { // TOD Add your message handler code here and/or call default DRAWITEMSTRUCT DIS; DIS.CtlType = ODT_BUTTON; DIS.CtlID = GetDlgCtrlID(); DIS.itemAction = action; DIS.itemState = mode; DIS.hwndItem = GetSafeHwnd(); DIS.hDC = GetDC()->GetSafeHdc(); GetClientRect(&(DIS.rcItem)); SendMessage(WM_DRAWITEM,(WPARAM)GetSafeHwnd(),(LPARAM)&DIS); ReleaseDC(CDC::FromHandle(DIS.hDC)); }
這樣,我們可以響應(yīng)鼠標(biāo)的各種消息來設(shè)置按鍵的各種狀態(tài):
void CDrawButton::OnMouseMove(UINT nFlags, CPoint point) { // TOD Add your message handler code here and/or call default CRect rect; GetClientRect(&rect); if(rect.PtInRect(point)){ if (mBtnStats==BTN_NORMAL){ SetButtonMode(ODA_SELECT, ODS_FOCUS); SetCapture(); } } else{ //AutoLoad(GetDlgCtrlID(),GetParent()); SetButtonMode(ODA_DRAWENTIRE,ODS_DEFAULT); ReleaseCapture(); }
CButton::OnMouseMove(nFlags, point); }
這里,mBtnStats是個(gè)UINT類型的成員變量,它可以有三種自定義狀態(tài): BTN_NORMAL 正常狀態(tài) BTN_UP 鼠標(biāo)移入按鍵區(qū)域或釋放鼠標(biāo) BTN_DOWN 按下鼠標(biāo) (可以再加一種DISABLE狀態(tài))
當(dāng)在按鍵區(qū)域釋放鼠標(biāo)時(shí),必須發(fā)送WM_COMMAND消息: void CDrawButton::OnLButtonUp(UINT nFlags, CPoint point) { // TOD Add your message handler code here and/or call default CRect rect; GetClientRect(&rect); if(rect.PtInRect(point)){ if (mBtnStats==BTN_DOWN) GetParent()->SendMessage(WM_COMMAND, MAKELPARAM(GetDlgCtrlID(),BN_CLICKED), (LPARAM)GetSafeHwnd()); SetCapture(); } else{ SetButtonMode(ODA_DRAWENTIRE,ODS_DEFAULT); ReleaseCapture(); }
CButton::OnLButtonUp(nFlags, point); }
接著就是繪制按鍵的各種狀態(tài):由于按鍵必須“透明”,所以在按下和釋放時(shí)只在按鍵區(qū)域的四周加上一個(gè)3D邊框就行了。而在正常狀態(tài)下,則必須去掉邊框恢復(fù)背景。但如何恢復(fù)背景圖象呢?我是這樣做的:在按鍵初始化時(shí),先把被按鍵覆蓋了的區(qū)域保存在一個(gè)CBitmap類中,以后需要重繪按鍵時(shí)就把這個(gè)CBitmap畫在按鍵上就行了。
void CDrawButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { // TOD Add your code to draw the specified item CDC dc; CRect rect=lpDrawItemStruct->rcItem; CString sCaption; dc.Attach(lpDrawItemStruct->hDC); //得到繪制的設(shè)備環(huán)境CDC VERIFY(lpDrawItemStruct->CtlType==ODT_BUTTON);
if (lpDrawItemStruct->itemAction & ODA_DRAWENTIRE){ //重繪控件(正常狀態(tài)) mBtnStats=BTN_NORMAL; if (m_pBitmap!=0){ CDC memDC; memDC.CreateCompatibleDC(&dc); memDC.SelectObject(m_pBitmap); dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); memDC.DeleteDC(); } //顯示按鍵標(biāo)題 GetWindowText(sCaption); dc.SetBkMode(TRANSPARENT); if (m_pFont!=0){ CFont* m_pOldFont=dc.SelectObject(m_pFont); dc.DrawText(sCaption,&rect, DT_CENTER|DT_VCENTER|DT_SINGLELINE); dc.SelectObject(m_pOldFont); } }
if ((lpDrawItemStruct->itemState & ODS_SELECTED) && (lpDrawItemStruct->itemAction & ODA_SELECT)){ //按下鼠標(biāo) mBtnStats=BTN_DOWN; dc.Draw3dRect(&rect,RGB(128,128,128),RGB(192,192,192)); rect.top=rect.top+1;rect.bottom=rect.bottom-1; rect.left=rect.left+1;rect.right=rect.right-1; dc.Draw3dRect(&rect,RGB(0,0,0),RGB(255,255,255)); }
if(!(lpDrawItemStruct->itemState & ODS_SELECTED) && (lpDrawItemStruct->itemAction & ODA_SELECT)){ //釋放鼠標(biāo)或鼠標(biāo)進(jìn)入按鍵區(qū)域 mBtnStats=BTN_UP; dc.Draw3dRect(&rect,RGB(255,255,255),RGB(0,0,0)); rect.top=rect.top+1;rect.bottom=rect.bottom-1; rect.left=rect.left+1;rect.right=rect.right-1; dc.Draw3dRect(&rect,RGB(192,192,192),RGB(128,128,128)); } dc.Detach(); }
接著就必須一些初始化工作,其中最關(guān)鍵就是把被按鍵覆蓋了的區(qū)域保存進(jìn)CBitmap類中,我們知道CDC::StretchBlt()函數(shù)可以把位圖的指定區(qū)域從一個(gè)設(shè)備拷貝到另一個(gè)設(shè)備中,這樣可以很方便地把窗口或?qū)υ捒虻哪硞€(gè)區(qū)域保存,條件是獲得其DC:
void CDrawButton::LoadBack(CWnd *pParent) { ASSERT(GetStyle() & BS_OWNERDRAW); if (m_pBitmap!=0) return;
CRect rect; GetWindowRect(&rect); pParent->ScreenToClient(&rect);//獲得按鍵區(qū)域 CPaintDC dc(pParent); if (m_pBitmap==0) m_pBitmap=new CBitmap;//初始化位圖 m_pBitmap->CreateCompatibleBitmap(&dc,rect.Width(),rect.Height()); CDC memDC; memDC.CreateCompatibleDC(&dc); memDC.SelectObject(m_pBitmap); memDC.StretchBlt(0, 0, rect.Width(),rect.Height(), &dc, rect.left, rect.top, rect.Width(),rect.Height(), SRCCOPY);//保存 memDC.DeleteDC(); m_pFont=pParent->GetFont();//獲得窗口或?qū)υ捒虻淖煮w
ModifyStyle(0,WS_VISIBLE);//顯示按鍵
SetBitmapMode(ODA_DRAWENTIRE,0);//繪制按鍵 }
而使這個(gè)類和對(duì)話框上的按鍵產(chǎn)生聯(lián)系還必須調(diào)用一下SubclassDlgItem():
BOOL CDrawButton::AutoLoad(UINT nID, CWnd *pParent) { // first attach the CDrawButton to the dialog control
if (m_pBitmap!=0) return FALSE;
if (!SubclassDlgItem(nID, pParent)) return FALSE;
LoadBack(pParent);
return TRUE; }
這個(gè)類還必須具有三個(gè)成員變量: CFont* m_pFont; CBitmap* m_pBitmap; UINT mBtnStats;
在構(gòu)造函數(shù)中初始化這些變量 m_pBitmap=0; m_pFont=0; //賦予0是可以的 mBtnStats=BTN_NORMAL;
在折構(gòu)函數(shù)中拆除位圖 if(m_pBitmap!=0) delete m_pBitmap;
這樣,一個(gè)透明的浮動(dòng)式按鍵類就做好了,具體實(shí)現(xiàn)方法以下: 1.接管對(duì)話框的BUTTON,首先在對(duì)話框上畫一個(gè)BUTTON,再加一個(gè)PICTURE圖片,BUTTON的風(fēng)格必須加入OWNER DRAW及去掉VISIBLE,把BUTTON移到PICTURE上適當(dāng)?shù)奈恢?,在?duì)話框類加入CDrawButton類成員m_myButton,由于按鍵初始化時(shí)必須保存對(duì)話框的圖象,而對(duì)話框在運(yùn)行InitDialog()或第一次運(yùn)行OnPaint()時(shí)對(duì)話框的控件還沒有真正顯示出來,我們只好在OnMouseMove()中進(jìn)行初始化:m_myButton.AutoLoad(IDC_BUTTON1,this); AutoLoad()只運(yùn)行一次。
2.動(dòng)態(tài)建立CDrawButton,在對(duì)話框類或CxxxView類加入CDrawButton類成員m_myButton,可以在對(duì)話框的InitDialog()或CxxxView類的InitialUpdate()中加入:m_myButton.Create()函數(shù),必須包含BS_OWNERDRAW而不能有WS_VISIBLE風(fēng)格,然后在OnMouseMove()或OnDraw()中進(jìn)行初始化:m_myButton.LoadBack(this);注意應(yīng)加在OnDraw()的最后。 同樣地,LoadBack()只運(yùn)行一次。 (如果按鍵比背景的圖片遲建立而具有可見(Visible)屬性,則會(huì)把圖片抹掉,所以必須去掉VISIBLE屬性或不能加入WS_VISIBLE風(fēng)格) 當(dāng)鼠標(biāo)移到按鍵區(qū)域時(shí),改變鼠標(biāo) 這個(gè)很容易實(shí)現(xiàn),不在這里多說了。
聯(lián)系客服