SDK平臺三態按鈕的實現

  Windows平臺提供了豐富的控件,可是在使用中咱們不會使用它提供的默認風格,有時候須要對控件進行改寫,讓它展示出更友好的一面,此次主要是說明三態按鈕的實現。函數

  三態按鈕指的是按鈕在鼠標移到按鈕上時顯示一種狀態,鼠標在按下時展示一種狀態,在鼠標移開時又展示出另一種狀態,總共三種。固然鼠標按下和移出按鈕展現的狀態系統本身提供的有,這個時候在處理這兩種狀態只須要貼相應的圖片就好了,三態按鈕的實現關鍵在於如何判斷鼠標已經移動到按鈕上以及鼠標移出按鈕,而後根據鼠標的位置將按鈕作相應的調整。字體

  判斷鼠標在按鈕的相應位置,系統提供了一個函數_TrackMouseEvent用戶處理鼠標移出、移入按鈕。函數原型以下: spa

BOOL _TrackMouseEvent(      
    LPTRACKMOUSEEVENT lpEventTrack
);

 函數須要傳入一個TRACKMOUSEEVENT類型的指針,該結構的原型以下:指針

1 typedef struct tagTRACKMOUSEEVENT {
2     DWORD cbSize;//該結構體所佔空間大小
3     DWORD dwFlags;//指定服務的請求(指定它須要偵聽的事件),此次主要用到的是TME_HOVER和TME_LEAVE(偵聽鼠標移開和移入事件)
4     HWND hwndTrack;//指定咱們須要偵聽的控件的句柄
5     DWORD dwHoverTime;//HOVER消耗的時間,能夠用系統提供的一個常量HOVER_DEFAULT由系統默認給出,也能夠本身填寫,單位是毫秒
6 } TRACKMOUSEEVENT, *LPTRACKMOUSEEVENT;

   在使用該函數時須要包含頭文件commctrl.h和lib文件comctl32.libcode

  解決了鼠標行爲的檢測以後,就是針對不一樣的鼠標行爲重繪相應的按鈕。重繪按鈕須要在消息WM_DRAWITEM中,這個消息的處理是在相應控件的父窗口中實現的,而在通常狀況下父窗口不會收到該消息,須要咱們手工指定控件資源的屬性爲的OWNERDRAW爲真,或者在建立相應的按鈕窗口時將樣式設置爲BS_OWNERDRAW 。blog

設置完成後就能夠在對應的父窗口處理函數中接收並處理WM_DRAWITEM,在該消息中重繪按鈕事件

該消息中主要使用的參數是lpParam它裏面包含的是一個指向DRAWITEMSTRUCT的結構體:圖片

typedef struct tagDRAWITEMSTRUCT { 
  UINT CtlType; //控件類型
  UINT CtlID; //控件ID
  UINT itemID; //子菜單項的ID主要用於菜單
  UINT itemAction; //控件發出的動做,如ODA_SELECT表示控件被選中
  UINT itemState; //控件狀態,此次須要用到的狀態爲ODS_SELECTED表示按鈕被按下
  HWND hwndItem; //控件句柄
  HDC hDC; 
  RECT rcItem;//控件的矩形區域 
  ULONG_PTR itemData; 
} DRAWITEMSTRUCT; 
//該結構體中的一些成員須要根據控件類型賦值,同時結構體中的itemAction、itemState是能夠由多個值經過位或組成在判斷是否具備某種狀態時須要使用位與運算


而繪製控件時咱們可使用函數DrawFrameControl,該函數能夠根據指定的控件類型、控件所處的狀態來繪製控件的樣式,繪製出來的任然是系統的以前的標準樣式,處理WM_DRAWITEN消息的具體代碼以下:資源

LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;

char szBuf[50];
GetWindowText(lpdis->hwndItem,szBuf,50);
if (ODT_BUTTON ==lpdis->CtlType)
{   UINT uState = DFCS_BUTTONPUSH;   if (lpdis->itemState & ODS_SELECTED)   {     uState |= DFCS_PUSHED;   }   DrawFrameControl(lpdis->hDC,&(lpdis->rcItem),DFC_BUTTON,uState);   SetTextColor(lpdis->hDC,RGB(255,0,0));   DrawText(lpdis->hDC,szBuf,strlen(szBuf) + 1,&(lpdis->rcItem),DT_CENTER |   DT_VCENTER | DT_SINGLELINE); }

 函數_TrackMouseEvent根據其檢測的鼠標狀態不一樣能夠返回不一樣的消息,此次主要用的是WM_MOUSEHOVER(表示鼠標移動到按鈕上)、WM_MOUSELEAVE(鼠標移出按鈕),還須要注意的是這個函數每次檢測完成返回後不會再次檢測,須要咱們本身主動調用函數檢測鼠標狀態,因爲要屢次調用,而每次調用都須要初始化所須要的結構體指針,因此咱們封裝一個函數專門用於調用_TrackMouseEvent:原型

void Track(HWND hWnd)
{
	TRACKMOUSEEVENT tme;
	tme.cbSize = sizeof(TRACKMOUSEEVENT);
	tme.dwFlags = TME_HOVER | TME_LEAVE;
	tme.dwHoverTime = 10;
	tme.hwndTrack = hWnd;
	_TrackMouseEvent(&tme);
}

 消息WM_MOUSEHOVER和消息WM_MOUSELEAVE的處理是在對應的窗口過程當中處理的,而按鈕的窗口過程由系統提供咱們並不知道,因此只有使用子類化的方法在咱們的窗口過程當中處理這兩個消息。在按鈕建立後立馬要檢測鼠標因此能夠按鈕對應的父窗口完成建立後子類化,對於窗口能夠在它的WM_CREATE消息中處理,對於對話框能夠在WM_INITDIALOG消息中處理,子類化調用函數SetWindowLong:

g_OldProc = (LRESULT*)SetWindowLong(GetDlgItem(hDlg,IDC_BUTTON1),GWL_WNDPROC,(LONG)BtnProc);
return 0;

 在新的窗口過程當中處理消息,完成三態按鈕:

switch (uMsg)
{
  case WM_MOUSEMOVE:
    Track(hBtn);//當鼠標移動時檢測
    break;
  case WM_MOUSEHOVER:
  {
    char szBuf[50];
    RECT rtBtn;
    GetClientRect(hBtn,&rtBtn);     HDC hDc = GetDC(hBtn);     DrawFrameControl(hDc,&(rtBtn),DFC_BUTTON,DFCS_BUTTONPUSH);     HBRUSH hBr = CreateSolidBrush(RGB(255,255,255));     FillRect(hDc,&rtBtn,hBr);     GetWindowText(hBtn,szBuf,50);     SetBkMode(hDc,TRANSPARENT);     DrawText(hDc,szBuf,strlen(szBuf),&rtBtn,DT_CENTER | DT_VCENTER | DT_SINGLELINE);     ReleaseDC(hBtn,hDc);   }    break;   case WM_MOUSELEAVE:   {     char szBuf[50];     RECT rtBtn;     GetClientRect(hBtn,&rtBtn);     HDC hDc = GetDC(hBtn);     DrawFrameControl(hDc,&(rtBtn),DFC_BUTTON,DFCS_BUTTONPUSH);     GetWindowText(hBtn,szBuf,50);     SetBkMode(hDc,TRANSPARENT);//設置字體背景爲透明     DrawText(hDc,szBuf,strlen(szBuf),&rtBtn,DT_CENTER | DT_VCENTER | DT_SINGLELINE);     ReleaseDC(hBtn,hDc);   }     break;   default:     return CallWindowProc((WNDPROC)g_OldProc,hBtn,uMsg,wParam, lParam);//在處理完咱們感興趣的消息後必定要記得將按鈕的窗口過程還原   } return 0;

 到這個地方爲止,已經實現了三態按鈕的基本樣式,經過檢測鼠標的位置設置按鈕樣式,上述代碼只是改變了按鈕的背景顏色和文字顏色,可能效果很差看。

相關文章
相關標籤/搜索