俗話說(shuō)佛要金裝、人要衣裝,作軟件的當(dāng)然得要個(gè)好界面啦。網(wǎng)上提供的控件自繪基本上是MFC或WTL封裝好的類(lèi),對(duì)于不想用MFC的人來(lái)說(shuō)是一無(wú)是處的,我可是WIN32API的堅(jiān)決擁護(hù)者。因?yàn)镸FC等也是用WIN32API封裝起來(lái)的,學(xué)好了WIN32API,可以深入的了解Windows內(nèi)部的機(jī)制,編寫(xiě)出來(lái)的程序才能得到更好的優(yōu)化。
下面分析一下自繪按鈕的原理,用過(guò)MFC自繪按鈕的人都知道,是通過(guò)重載了父窗口WM_DRAWITEM的響應(yīng)消息實(shí)現(xiàn)的。同時(shí)也要子類(lèi)化按鈕來(lái)得到按鈕的其他有用的消息,比如WM_MOUSEMOVE、WM_KEYDOWN等消息。因?yàn)镸FC的消息循環(huán)都是封裝好的,所以只要派生一下基本控件類(lèi)就可以了。當(dāng)是用WIN32API做的話就需要自己來(lái)子類(lèi)化按鈕窗口的消息循環(huán)了,相信經(jīng)常編程的朋友都知道,子類(lèi)化控件要用到SetWindowLong來(lái)改變窗口的回調(diào)過(guò)程,然后在回調(diào)窗口內(nèi)添上自己需要處理的消息即可。因?yàn)槲覀円獙?shí)現(xiàn)自繪按鈕所以把子類(lèi)化的過(guò)程做成一個(gè)類(lèi),然后傳給它要自繪的按鈕句柄就行了。因?yàn)橐陬?lèi)里面實(shí)現(xiàn)消息回調(diào)函數(shù),但是類(lèi)里面的消息回調(diào)函數(shù)只能是靜態(tài)的,所以不能對(duì)應(yīng)每個(gè)實(shí)例的消息回調(diào)。在我實(shí)現(xiàn)的按鈕子類(lèi)化類(lèi)里,我用到Thunk技術(shù)或SetProp函數(shù)來(lái)實(shí)現(xiàn)的,具體請(qǐng)網(wǎng)上查找。
下面我來(lái)談?wù)勛岳L按鈕里最重要的部分,就是響應(yīng)按鈕消息函數(shù)里的WM_PAINT消息,我們所有的自繪動(dòng)作都在這里進(jìn)行的。WM_PAINT里的繪圖操作與普通窗口的操作一樣,但是為了跟蹤按鈕的當(dāng)前狀態(tài),我們還要響應(yīng)按鈕窗口的WM_MOUSEMOVE、WM_SETFOCUS、WM_KILLFOCUS、WM_LBUTTONDOWN、WM_ENABLE等消息來(lái)得到當(dāng)前按鈕的狀態(tài)。從而在WM_PAINT里面繪出不同的狀態(tài),能實(shí)現(xiàn)的東西很多可以說(shuō)你想多少基本就能實(shí)現(xiàn)多少,看個(gè)人喜好了,我提供源代碼大家可以自行修改。我也是參看了ButtonST里面自繪的代碼,我自己添加了右鍵拖動(dòng)功能,鼠標(biāo)掠過(guò)發(fā)生功能大家有興趣可以自己添加,鍛煉一下自己的編程能力。
下面我說(shuō)一下我做的這個(gè)類(lèi)的一個(gè)問(wèn)題,我把按鈕類(lèi)做成了一個(gè)動(dòng)態(tài)庫(kù),調(diào)用時(shí)只要加上我的頭文件和連接的lib庫(kù)就可以了。我的動(dòng)態(tài)庫(kù)在WIN32的程序加載是沒(méi)有問(wèn)題的,但是在MFC里面,必需要響應(yīng)父窗口的WM_DRAWITEM消息,在里面直接返回,而不要調(diào)用MFC默認(rèn)的處理就OK了。這是因?yàn)槲覜](méi)有截獲父窗口的WM_DRAWITEM消息,否則在關(guān)閉程序時(shí)會(huì)出現(xiàn)非法操作!主要代碼分析如下:
自繪按鈕類(lèi)聲明:
class DLLPORT CWINButton
{
public:
//初始化按鈕(這是第一步!)
BOOL GetItemhWnd(HWND hWnd);
//還原按鈕區(qū)域設(shè)置
BOOL Restore();
//設(shè)置按鈕是否可以拖動(dòng)
BOOL SetDrag(BOOL Enable);
//設(shè)置按鈕圖標(biāo)
BOOL SetIcon(HICON icon);
//設(shè)置按鈕文字
BOOL SetText(char *text, HFONT font);
BOOL SetText(char *text);
BOOL SetText(char *text, COLORREF color);
//設(shè)置按鈕有效區(qū)域
BOOL SetupRegion(COLORREF TransColor);
LRESULT OnPaint(HDC hdc);
//設(shè)置按鈕無(wú)效時(shí)的圖片
BOOL SetDisablePic(HBITMAP bmp);
//設(shè)置按鈕按下時(shí)的圖片
BOOL SetPressPic(HBITMAP bmp);
//設(shè)置懸停按鈕時(shí)的圖片
BOOL SetHovERPic(HBITMAP bmp);
//設(shè)置按鈕背景圖片,第二個(gè)參數(shù)是是否根據(jù)圖片調(diào)整按鈕大小
BOOL SetBackPic(HBITMAP bmp, BOOL bReSize);
//設(shè)置按鈕的提示消息
BOOL SetToolTip(char *text);
CWINButton();
virtual ~CWINButton();
private:
static LRESULT WINAPI stdProc(HWND hWnd,UINT uMsg,UINT wParam,LONG lParam);
WNDPROC GetThunk();
WNDPROC CreateThunk();
LRESULT CALLBACK WINProc(UINT message, WPARAM wParam, LPARAM lParam);
BOOL DrawInsideBorder(HDC dc, RECT *rect);
BOOL DrawFlat(HDC dc, RECT *rect);
BOOL DrawDefault(HDC dc);
HWND m_ToolTip;
HWND m_hWnd;
HWND m_hWndParent;
LONG m_OldProc;
WNDPROC m_thunk;
TOOLINFO ti;
HICON m_icon;
HBITMAP m_Back; //按鈕背景圖片
HBITMAP m_Hove; //鼠標(biāo)懸停時(shí)按鈕背景圖片
HBITMAP m_Press; //鼠標(biāo)按下時(shí)按鈕背景圖片
HBITMAP m_Disable; //按鈕無(wú)效時(shí)背景圖片
BITMAP bm;
COLORREF m_textcolor; //按鈕文字的顏色
BOOL m_bMouseTracking; //判斷鼠標(biāo)是否在窗口內(nèi)
BOOL m_bPress; //判斷鼠標(biāo)是否按下
BOOL m_Enable; //控件是否有效
BOOL m_bFocus; //按鈕是否處于輸入焦點(diǎn)
BOOL m_bOwnerDraw; //判斷是否用戶(hù)自己貼圖
BOOL m_bDrag; //是否處于拖動(dòng)狀態(tài)
BOOL m_bDragEnable; //是否允許拖動(dòng)
char m_text[MAX_TEXTLEN]; //按鈕文字
char m_tiptext[MAX_TEXTLEN]; //按鈕提示文字
HFONT m_font; //按鈕文字字體
HCURSOR m_OldCursor;
RECT m_ParentRt;
RECT m_BeginRt;
RECT m_CurrentRt;
POINT m_BeginPt;
POINT m_CurrentPt;
int m_CaptionHeight;
int m_BorderWidth;
int m_EdgeWidth;
protected:
//按鈕的外邊框
HPEN m_BoundryPen;
//鼠標(biāo)指針置于按鈕之上時(shí)按鈕的內(nèi)邊框
HPEN m_InsideBoundryPenLeft;
HPEN m_InsideBoundryPenRight;
HPEN m_InsideBoundryPenTop;
HPEN m_InsideBoundryPenBottom;
//按鈕獲得焦點(diǎn)時(shí)按鈕的內(nèi)邊框
HPEN m_InsideBoundryPenLeftSel;
HPEN m_InsideBoundryPenRightSel;
HPEN m_InsideBoundryPenTopSel;
HPEN m_InsideBoundryPenBottomSel;
//按鈕的底色,包括有效和無(wú)效兩種狀態(tài)
HBRUSH m_FillActive;
HBRUSH m_FillInactive;
};
消息回調(diào)類(lèi)里的實(shí)現(xiàn)代碼:
CWINButton::GetItemhWnd()里面
if(SetProp(m_hWnd, "CWINBUTTON", (HANDLE)this) == 0)
{
OutputDebugString("SetProp ERROR");
return FALSE;
}
m_OldProc = SetWindowLong(m_hWnd,GWL_WNDPROC,(LONG)stdProc);
CWINButton::stdProc()里面
{
CWINButton* w = (CWINButton*)GetProp(hWnd, "CWINBUTTON");
return w->WINProc(uMsg,wParam,lParam);
}
Thunk 代碼可看我的代碼或者去網(wǎng)上查詢(xún)。
下面分析一下自繪按鈕的原理,用過(guò)MFC自繪按鈕的人都知道,是通過(guò)重載了父窗口WM_DRAWITEM的響應(yīng)消息實(shí)現(xiàn)的。同時(shí)也要子類(lèi)化按鈕來(lái)得到按鈕的其他有用的消息,比如WM_MOUSEMOVE、WM_KEYDOWN等消息。因?yàn)镸FC的消息循環(huán)都是封裝好的,所以只要派生一下基本控件類(lèi)就可以了。當(dāng)是用WIN32API做的話就需要自己來(lái)子類(lèi)化按鈕窗口的消息循環(huán)了,相信經(jīng)常編程的朋友都知道,子類(lèi)化控件要用到SetWindowLong來(lái)改變窗口的回調(diào)過(guò)程,然后在回調(diào)窗口內(nèi)添上自己需要處理的消息即可。因?yàn)槲覀円獙?shí)現(xiàn)自繪按鈕所以把子類(lèi)化的過(guò)程做成一個(gè)類(lèi),然后傳給它要自繪的按鈕句柄就行了。因?yàn)橐陬?lèi)里面實(shí)現(xiàn)消息回調(diào)函數(shù),但是類(lèi)里面的消息回調(diào)函數(shù)只能是靜態(tài)的,所以不能對(duì)應(yīng)每個(gè)實(shí)例的消息回調(diào)。在我實(shí)現(xiàn)的按鈕子類(lèi)化類(lèi)里,我用到Thunk技術(shù)或SetProp函數(shù)來(lái)實(shí)現(xiàn)的,具體請(qǐng)網(wǎng)上查找。
下面我來(lái)談?wù)勛岳L按鈕里最重要的部分,就是響應(yīng)按鈕消息函數(shù)里的WM_PAINT消息,我們所有的自繪動(dòng)作都在這里進(jìn)行的。WM_PAINT里的繪圖操作與普通窗口的操作一樣,但是為了跟蹤按鈕的當(dāng)前狀態(tài),我們還要響應(yīng)按鈕窗口的WM_MOUSEMOVE、WM_SETFOCUS、WM_KILLFOCUS、WM_LBUTTONDOWN、WM_ENABLE等消息來(lái)得到當(dāng)前按鈕的狀態(tài)。從而在WM_PAINT里面繪出不同的狀態(tài),能實(shí)現(xiàn)的東西很多可以說(shuō)你想多少基本就能實(shí)現(xiàn)多少,看個(gè)人喜好了,我提供源代碼大家可以自行修改。我也是參看了ButtonST里面自繪的代碼,我自己添加了右鍵拖動(dòng)功能,鼠標(biāo)掠過(guò)發(fā)生功能大家有興趣可以自己添加,鍛煉一下自己的編程能力。
下面我說(shuō)一下我做的這個(gè)類(lèi)的一個(gè)問(wèn)題,我把按鈕類(lèi)做成了一個(gè)動(dòng)態(tài)庫(kù),調(diào)用時(shí)只要加上我的頭文件和連接的lib庫(kù)就可以了。我的動(dòng)態(tài)庫(kù)在WIN32的程序加載是沒(méi)有問(wèn)題的,但是在MFC里面,必需要響應(yīng)父窗口的WM_DRAWITEM消息,在里面直接返回,而不要調(diào)用MFC默認(rèn)的處理就OK了。這是因?yàn)槲覜](méi)有截獲父窗口的WM_DRAWITEM消息,否則在關(guān)閉程序時(shí)會(huì)出現(xiàn)非法操作!主要代碼分析如下:
自繪按鈕類(lèi)聲明:
class DLLPORT CWINButton
{
public:
//初始化按鈕(這是第一步!)
BOOL GetItemhWnd(HWND hWnd);
//還原按鈕區(qū)域設(shè)置
BOOL Restore();
//設(shè)置按鈕是否可以拖動(dòng)
BOOL SetDrag(BOOL Enable);
//設(shè)置按鈕圖標(biāo)
BOOL SetIcon(HICON icon);
//設(shè)置按鈕文字
BOOL SetText(char *text, HFONT font);
BOOL SetText(char *text);
BOOL SetText(char *text, COLORREF color);
//設(shè)置按鈕有效區(qū)域
BOOL SetupRegion(COLORREF TransColor);
LRESULT OnPaint(HDC hdc);
//設(shè)置按鈕無(wú)效時(shí)的圖片
BOOL SetDisablePic(HBITMAP bmp);
//設(shè)置按鈕按下時(shí)的圖片
BOOL SetPressPic(HBITMAP bmp);
//設(shè)置懸停按鈕時(shí)的圖片
BOOL SetHovERPic(HBITMAP bmp);
//設(shè)置按鈕背景圖片,第二個(gè)參數(shù)是是否根據(jù)圖片調(diào)整按鈕大小
BOOL SetBackPic(HBITMAP bmp, BOOL bReSize);
//設(shè)置按鈕的提示消息
BOOL SetToolTip(char *text);
CWINButton();
virtual ~CWINButton();
private:
static LRESULT WINAPI stdProc(HWND hWnd,UINT uMsg,UINT wParam,LONG lParam);
WNDPROC GetThunk();
WNDPROC CreateThunk();
LRESULT CALLBACK WINProc(UINT message, WPARAM wParam, LPARAM lParam);
BOOL DrawInsideBorder(HDC dc, RECT *rect);
BOOL DrawFlat(HDC dc, RECT *rect);
BOOL DrawDefault(HDC dc);
HWND m_ToolTip;
HWND m_hWnd;
HWND m_hWndParent;
LONG m_OldProc;
WNDPROC m_thunk;
TOOLINFO ti;
HICON m_icon;
HBITMAP m_Back; //按鈕背景圖片
HBITMAP m_Hove; //鼠標(biāo)懸停時(shí)按鈕背景圖片
HBITMAP m_Press; //鼠標(biāo)按下時(shí)按鈕背景圖片
HBITMAP m_Disable; //按鈕無(wú)效時(shí)背景圖片
BITMAP bm;
COLORREF m_textcolor; //按鈕文字的顏色
BOOL m_bMouseTracking; //判斷鼠標(biāo)是否在窗口內(nèi)
BOOL m_bPress; //判斷鼠標(biāo)是否按下
BOOL m_Enable; //控件是否有效
BOOL m_bFocus; //按鈕是否處于輸入焦點(diǎn)
BOOL m_bOwnerDraw; //判斷是否用戶(hù)自己貼圖
BOOL m_bDrag; //是否處于拖動(dòng)狀態(tài)
BOOL m_bDragEnable; //是否允許拖動(dòng)
char m_text[MAX_TEXTLEN]; //按鈕文字
char m_tiptext[MAX_TEXTLEN]; //按鈕提示文字
HFONT m_font; //按鈕文字字體
HCURSOR m_OldCursor;
RECT m_ParentRt;
RECT m_BeginRt;
RECT m_CurrentRt;
POINT m_BeginPt;
POINT m_CurrentPt;
int m_CaptionHeight;
int m_BorderWidth;
int m_EdgeWidth;
protected:
//按鈕的外邊框
HPEN m_BoundryPen;
//鼠標(biāo)指針置于按鈕之上時(shí)按鈕的內(nèi)邊框
HPEN m_InsideBoundryPenLeft;
HPEN m_InsideBoundryPenRight;
HPEN m_InsideBoundryPenTop;
HPEN m_InsideBoundryPenBottom;
//按鈕獲得焦點(diǎn)時(shí)按鈕的內(nèi)邊框
HPEN m_InsideBoundryPenLeftSel;
HPEN m_InsideBoundryPenRightSel;
HPEN m_InsideBoundryPenTopSel;
HPEN m_InsideBoundryPenBottomSel;
//按鈕的底色,包括有效和無(wú)效兩種狀態(tài)
HBRUSH m_FillActive;
HBRUSH m_FillInactive;
};
消息回調(diào)類(lèi)里的實(shí)現(xiàn)代碼:
CWINButton::GetItemhWnd()里面
if(SetProp(m_hWnd, "CWINBUTTON", (HANDLE)this) == 0)
{
OutputDebugString("SetProp ERROR");
return FALSE;
}
m_OldProc = SetWindowLong(m_hWnd,GWL_WNDPROC,(LONG)stdProc);
CWINButton::stdProc()里面
{
CWINButton* w = (CWINButton*)GetProp(hWnd, "CWINBUTTON");
return w->WINProc(uMsg,wParam,lParam);
}
Thunk 代碼可看我的代碼或者去網(wǎng)上查詢(xún)。

