俗話說佛要金裝、人要衣裝,作軟件的當然得要個好界面啦^_^。網上提供的控件自繪基本上是MFC或WTL封裝好的類,對於不想用MFC的人來說是一無是處的,我可是WIN32API的堅決擁護者。因為MFC等也是用WIN32API封裝起來的,學好了WIN32API,可以深入的了解WINDOWS內部的機制,編寫出來的程序才能得到更好的優化。
下面分析一下自繪按鈕的原理,用過MFC自繪按鈕的人都知道,是通過重載了父窗口WM_DRAWITEM的響應消息實現的。同時也要子類化按鈕來得到按鈕的其他有用的消息,比如WM_MOUSEMOVE、WM_KEYDOWN等消息。因為MFC的消息循環都是封裝好的,所以只要派生一下基本控件類就可以了。當是用WIN32API做的話就需要自己來子類化按鈕窗口的消息循環了,相信經常編程的朋友都知道,子類化控件要用到SetWindowLong來改變窗口的回調過程,然後在回調窗口內添上自己需要處理的消息即可。因為我們要實現自繪按鈕所以最好把子類化的過程做成一個類,然後傳給它要自繪的按鈕句柄就行了。因為要在類裡面實現消息回調函數,但是類裡面的消息回調函數只能是靜態的,所以不能對應每個實例的消息回調。在我實現的按鈕子類化類裡,我用到Thunk技術或SetProp函數來實現的,具體請網上查找。
下面我來談談自繪按鈕裡最重要的部分,就是響應按鈕消息函數裡的WM_PAINT消息,我們所有的自繪動作都在這裡進行的。WM_PAINT裡的繪圖操作與普通窗口的操作一樣,但是為了跟蹤按鈕的當前狀態,我們還要響應按鈕窗口的WM_MOUSEMOVE、WM_SETFOCUS、WM_KILLFOCUS、WM_LBUTTONDOWN、WM_ENABLE等消息來得到當前按鈕的狀態。從而在WM_PAINT裡面繪出不同的狀態,能實現的東西很多可以說你想多少基本就能實現多少^_^,看個人喜好了,我提供源代碼大家可以自行修改。我也是參看了ButtonST裡面自繪的代碼,我自己添加了右鍵拖動功能,鼠標掠過發生功能大家有興趣可以自己添加,鍛煉一下自己的編程能力^_^。本文發表於http://bianceng.cn(編程入門網)
下面我說一下我做的這個類的一個問題,我把按鈕類做成了一個動態庫,調用時只要加上我的頭文件和連接的lib庫就可以了。我的動態庫在WIN32的程序加載是沒有問題的,但是在MFC裡面,必需要響應父窗口的WM_DRAWITEM消息,在裡面直接返回,而不要調用MFC默認的處理就OK了。這是因為我沒有截獲父窗口的WM_DRAWITEM消息,否則在關閉程序時會出現非法操作!主要代碼分析如下:
自繪按鈕類聲明:class DLLPORT CWINButton
{
public:
//初始化按鈕(這是第一步!)
BOOL GetItemhWnd(HWND hWnd);
//還原按鈕區域設置
BOOL Restore();
//設置按鈕是否可以拖動
BOOL SetDrag(BOOL Enable);
//設置按鈕圖標
BOOL SetIcon(HICON icon);
//設置按鈕文字
BOOL SetText(char *text, HFONT font);
BOOL SetText(char *text);
BOOL SetText(char *text, COLORREF color);
//設置按鈕有效區域
BOOL SetupRegion(COLORREF TransColor);
LRESULT OnPaint(HDC hdc);
//設置按鈕無效時的圖片
BOOL SetDisablePic(HBITMAP bmp);
//設置按鈕按下時的圖片
BOOL SetPressPic(HBITMAP bmp);
//設置懸停按鈕時的圖片
BOOL SetHoverPic(HBITMAP bmp);
//設置按鈕背景圖片,第二個參數是是否根據圖片調整按鈕大小
BOOL SetBackPic(HBITMAP bmp, BOOL bReSize);
//設置按鈕的提示消息
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; //鼠標懸停時按鈕背景圖片
HBITMAP m_Press; //鼠標按下時按鈕背景圖片
HBITMAP m_Disable; //按鈕無效時背景圖片
BITMAP bm;
COLORREF m_textcolor; //按鈕文字的顏色
BOOL m_bMouseTracking; //判斷鼠標是否在窗口內
BOOL m_bPress; //判斷鼠標是否按下
BOOL m_Enable; //控件是否有效
BOOL m_bFocus; //按鈕是否處於輸入焦點
BOOL m_bOwnerDraw; //判斷是否用戶自己貼圖
BOOL m_bDrag; //是否處於拖動狀態
BOOL m_bDragEnable; //是否允許拖動
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;
//鼠標指針置於按鈕之上時按鈕的內邊框
HPEN m_InsideBoundryPenLeft;
HPEN m_InsideBoundryPenRight;
HPEN m_InsideBoundryPenTop;
HPEN m_InsideBoundryPenBottom;
//按鈕獲得焦點時按鈕的內邊框
HPEN m_InsideBoundryPenLeftSel;
HPEN m_InsideBoundryPenRightSel;
HPEN m_InsideBoundryPenTopSel;
HPEN m_InsideBoundryPenBottomSel;
//按鈕的底色,包括有效和無效兩種狀態
HBRUSH m_FillActive;
HBRUSH m_FillInactive;
};消息回調類裡的實現代碼: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 代碼可看我的代碼或者去網上查詢。
以上是我提供給大家的一點淺見,歡迎大家跟我討論有關的技術。