在VC中透明浮動按鍵的完成
發(fā)表時間:2024-02-21 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]有一種按鍵,看起來是一幅完整的圖片,當鼠標移到按鍵區(qū)域時,圖片的一部分凸現(xiàn),形成一個按鍵,當鼠標移走時又恢復(fù)原來狀態(tài)。 最近,看了一些關(guān)于浮動按鍵的代碼,其原理大致上跟CBitmapButton差不多,用數(shù)幅位圖代表按鍵的各個狀態(tài),響應(yīng)鼠標的各種消息來設(shè)置按鍵的狀態(tài),實現(xiàn)按鍵的浮動顯示,...
有一種按鍵,看起來是一幅完整的圖片,當鼠標移到按鍵區(qū)域時,圖片的一部分凸現(xiàn),形成一個按鍵,當鼠標移走時又恢復(fù)原來狀態(tài)。
最近,看了一些關(guān)于浮動按鍵的代碼,其原理大致上跟CBitmapButton差不多,用數(shù)幅位圖代表按鍵的各個狀態(tài),響應(yīng)鼠標的各種消息來設(shè)置按鍵的狀態(tài),實現(xiàn)按鍵的浮動顯示,但是這樣的按鍵卻不能和周圍的背景混和成一幅圖片。
為了實現(xiàn)“透明”按鍵,可以簡單地做個試驗:先在對話框中加入一個BUTTON,通過屬性框選“Owner Draw”風(fēng)格,再加入一個PICTURE,并加入圖片,將BUTTON移到PICTURE上。運行結(jié)果發(fā)現(xiàn),按鍵沒有顯示出來,但在按鍵區(qū)域按下鼠標時,該按鍵仍然能發(fā)出WM_COMMAND消息,這樣一個純透明的按鍵建立了。顯然,這個按鍵是毫無使用意義的,因為用戶不知道按鍵的位置,必須讓用戶容易覺察到按鍵的位置,可以把這個按鍵改造一下:
(首先從CButton派生出一個新類CDrawButton)
·把按鍵的標題顯示出來
這個實現(xià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);//得到按鍵的標題
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是成員變量,它保存了對話框的字體指針,為了按鍵的標題風(fēng)格與對話框的字體風(fēng)格一致,在初始化時調(diào)用對話框的成員函數(shù)GetFont()即可得到指向?qū)υ捒蜃煮w的CFont類指針。
·使按鍵浮動顯示
要通過自繪來表示按鍵的各種狀態(tài),可填寫DRAWITEMSTRUCT來通知DrawItem()函數(shù)需要做什么,我們先了解一下DRAWITEMSTRUCT:
typedef struct tagDRAWITEMSTRUCT{
UINT CtlType; // 控件類型
UINT CtlID;// 控件的ID號
UNIT itemID;//菜單項的索引
UINT itemAction;// 繪圖操作
UINT itemState; // 狀態(tài)
HWND hwndItem; // 控件的窗口句柄
HDC hDC; // 相關(guān)的設(shè)備環(huán)境
RECT rcItem;//控件的范圍
DWORD itemData;//指定與菜單項相聯(lián)系的應(yīng)用程序定義的32位值
}DRAWITEMSTRUCT;
利用這個結(jié)構(gòu)先做一個按鍵狀態(tài)設(shè)置函數(shù):
void CDrawButton::SetButtonMode(UINT action, UINT mode)
{
// TODO: 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)鼠標的各種消息來設(shè)置按鍵的各種狀態(tài):
void CDrawButton::OnMouseMove
(UINT nFlags, CPoint point)
{
// TODO: 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是個UINT類型的成員變量,它可以有三種自定義狀態(tài):
BTN_NORMAL 正常狀態(tài)
BTN_UP 鼠標移入按鍵區(qū)域或釋放鼠標
BTN_DOWN 按下鼠標
(可以再加一種DISABLE狀態(tài))
當在按鍵區(qū)域釋放鼠標時,必須發(fā)送WM_COMMAND消息:
void CDrawButton::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 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):由于按鍵必須“透明”,所以在按下和釋放時只在按鍵區(qū)域的四周加上一個3D邊框就行了。而在正常狀態(tài)下,則必須去掉邊框恢復(fù)背景。但如何恢復(fù)背景圖象呢?我是這樣做的:在按鍵初始化時,先把被按鍵覆蓋了的區(qū)域保存在一個CBitmap類中,以后需要重繪按鍵時就把這個CBitmap畫在按鍵上就行了。
void CDrawButton::DrawItem
(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: 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();
}
//顯示按鍵標題
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)){
//按下鼠標
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)){
//釋放鼠標或鼠標進入按鍵區(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ū)域保存進CBitmap類中,我們知道CDC::StretchBlt()函數(shù)可以把位圖的指定區(qū)域從一個設(shè)備拷貝到另一個設(shè)備中,這樣可以很方便地把窗口或?qū)υ捒虻哪硞區(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);//繪制按鍵
}
而使這個類和對話框上的按鍵產(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;
}
這個類還必須具有三個成員變量:
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;
這樣,一個透明的浮動式按鍵類就做好了,具體實現(xiàn)方法以下:
1.接管對話框的BUTTON,首先在對話框上畫一個BUTTON,再加一個PICTURE圖片,BUTTON的風(fēng)格必須加入OWNER DRAW及去掉VISIBLE,把BUTTON移到PICTURE上適當?shù)奈恢,在對話框類加入CDrawButton類成員m_myButton,由于按鍵初始化時必須保存對話框的圖象,而對話框在運行InitDialog()或第一次運行OnPaint()時對話框的控件還沒有真正顯示出來,我們只好在OnMouseMove()中進行初始化:
m_myButton.AutoLoad(IDC_BUTTON1,this);
AutoLoad()只運行一次。
2.動態(tài)建立CDrawButton,在對話框類或CxxxView類加入CDrawButton類成員m_myButton,可以在對話框的InitDialog()或CxxxView類的InitialUpdate()中加入:m_myButton.Create()函數(shù),必須包含BS_OWNERDRAW而不能有WS_VISIBLE風(fēng)格,然后在OnMouseMove()或OnDraw()中進行初始化:m_myButton.LoadBack(this);注意應(yīng)加在OnDraw()的最后。
同樣地,LoadBack()只運行一次。
(如果按鍵比背景的圖片遲建立而具有可見(Visible)屬性,則會把圖片抹掉,所以必須去掉VISIBLE屬性或不能加入WS_VISIBLE風(fēng)格)
·當鼠標移到按鍵區(qū)域時,改變鼠標
這個很容易實現(xiàn),不在這里多說了。