windows基礎應用程式程式設計(七)滑鼠訊息
阿新 • • 發佈:2019-02-07
上一篇中,我們介紹了鍵盤訊息,接下來,我們來了解一下滑鼠訊息。
在上一篇中,我們知道Windows只把鍵盤訊息傳送給擁有輸入焦點的視窗。滑鼠訊息與此不同,只要滑鼠跨越視窗或者在某視窗中按下滑鼠鍵,那麼視窗過程就會受到滑鼠訊息,而不管該視窗是否活動或者是否擁有輸入焦點。同鍵盤訊息類似,滑鼠訊息也簡單的可以分為客戶區滑鼠訊息和非客戶區滑鼠訊息。
非客戶區滑鼠訊息
如果滑鼠在視窗的客戶區之外但還在視窗之中(比如,標題欄,選單等),Windows就會給視窗過程傳送一個“非客戶區”滑鼠訊息。同客戶區滑鼠訊息類似,當我們的滑鼠在非客戶區移動,那麼視窗過程將接受到一個WM_NCMOUSEMOVE訊息。同樣,擋在非客戶區按下或釋放一個滑鼠鍵時,視窗過程會收到下列訊息。
滑鼠左鍵: WM_NCLBUTTONDOWN(按下) WM_NCLBUTTONUP(擡起) WM_NCLBUTTONDBLCLK(雙擊)
滑鼠中鍵: WM_NCMBUTTONDOWN WM_NCMBUTTONUP WM_NCMBUTTONDBLCLK
滑鼠右鍵: WM_NCRBUTTONDOWN WM_NCRBUTTONUP WM_NCRBUTTONDBLCLK
其中,非客戶區滑鼠訊息的wParam引數表示移動或者單擊滑鼠鍵的非客戶區位置。(注意不是座標)而是一組以HT開頭定義的識別符號。
lParam引數的同客戶區滑鼠訊息類似,低位字為x座標,高位字為y座標,但之得注意的事,這裡的座標不同於客戶區滑鼠訊息中的客戶區座標,這裡的座標而是指螢幕座標。我們可以使用Windows提供的兩個函式來進行客戶區和螢幕之間的轉變。即ScreenToClient函式和ClientToScreen函式。
另外,我們還需要知道的一點是,我們現在通常的滑鼠,中間都有一個滑鼠輪,我們按下滑鼠輪時,產生的是滑鼠中鍵的訊息。我們也可以滾動它,這是將會產生一個WM_MOUSEWHEEL訊息。如果我們的程式需要的話,我們可以捕獲這個訊息。
(其實,一般在我們的程式中,很少去處理非客戶區滑鼠訊息,這裡僅作了解!)
示例
現在,我們來簡單的使用以下上面的滑鼠訊息,我們這裡演示以下如何使用滑鼠像畫圖程式那樣畫出一個矩形。
我們在對WM_MOUSEMOVE訊息進行處理的時候,我們呼叫了兩次繪圖函式,並且使用了畫素反轉模式。這樣我們第二次繪製的矩形,會首先被下一次的WM_MOUSEMOVE訊息處理中的第一次繪製所覆蓋,這樣就消除了滑鼠繪圖的軌跡。可以把第二次繪圖給註釋掉,然後再觀察一下繪圖效果發生了什麼變化。
另外,我們的這個程式中還存在著一個問題,那就是,當我們在客戶區按下滑鼠左鍵,並移動滑鼠到客戶區外。這時,我們的程式就不能再接受WM_MOUSEMOVE訊息了,程式出現了不理想的效果。我們可以觀察一下,windows系統自帶的繪圖程式,發現這樣操作時,windows的繪圖程式仍然能夠響應滑鼠移動的變化。那麼我們怎麼在我們的程式中新增這個功能呢?非常簡單,我們只需要使用一個函式SetCapture。這個函式就一個引數表示視窗控制代碼。呼叫這個函式之後,Windows將所有滑鼠訊息發給視窗控制代碼為所傳入引數的視窗過程。滑鼠訊息總是客戶區訊息。即使滑鼠位於非客戶區。當我們釋放滑鼠時,我們還需要釋放滑鼠捕獲。即呼叫ReleaseCapture來釋放。
所以現在我們在上面的視窗回撥函式中,在WM_LBUTTONDOWN訊息處理中,加上SetCapture(hWnd);在WM_LBUTTONUP訊息處理中,加上ReleaseCapture();這個時候再執行程式,就可以看到我們想要的效果了。
鍵盤和滑鼠訊息就介紹這麼多,其中涉及到很多之前沒見到的函式,不能一一介紹,可以多查MSDN,積累很重要。
客戶區滑鼠訊息
當滑鼠通過視窗的客戶區時,視窗過程受到WM_MOUSEMOVE訊息。需要注意的是,Windows並不為滑鼠的每個可能的畫素位置都產生一條WM_MOUSEMOVE訊息。我們的程式接收到WM_MOUSEMOVE訊息的次數依賴於滑鼠硬體以及視窗過程處理滑鼠移動的速度。當在視窗的客戶區中按下或者釋放一個滑鼠鍵時,視窗過程會接收到下列訊息。 滑鼠左鍵: WM_LBUTTONDOWN(按下) WM_LBUTTONUP(擡起) WM_LBUTTONDBLCLK(雙擊) 滑鼠中鍵: WM_MBUTTONDOWN WM_MBUTTONUP WM_MBUTTONDBLCLK 滑鼠右鍵: WM_RBTUUONDOWN WM_RBUTTONUP WM_RBUTTONDBLCLK 對這些訊息,它們的wParam的值表示滑鼠鍵及Shift和Ctrl鍵的狀態。我們可以通過位遮蔽來測試wParam。其中位標誌如下: MK_LBUTTON 按下左鍵 MK_MBUTTON 按下中鍵 MK_RBUTTON 按下右鍵 MK_SHIFT 按下Shift鍵 MK_CONTROL 按下Ctrl鍵 舉個簡單的例子,比如我們收到了WM_LBUTTONDOWN訊息,那麼如果wParam&MK_SHIFT的值為TRUE,那麼我們就可以判定在左鍵按下的時候也按下了Shift鍵。(我們也可以通過上一篇中介紹的GetKeyState函式來得到狀態。) 接下來lParam引數表示了滑鼠的位置,低位字為x座標,高位字為y座標,這兩個座標是相對於視窗客戶區左上角的位置。如下所示: x = LOWORD(lParam) y = HIWORD(lParam) 另外,還需要了解的是,如果我們的視窗過程要處理滑鼠雙擊的訊息,那麼我們的視窗類風格必須要包含CS_DBLCLKS識別符號。非客戶區滑鼠訊息
如果滑鼠在視窗的客戶區之外但還在視窗之中(比如,標題欄,選單等),Windows就會給視窗過程傳送一個“非客戶區”滑鼠訊息。同客戶區滑鼠訊息類似,當我們的滑鼠在非客戶區移動,那麼視窗過程將接受到一個WM_NCMOUSEMOVE訊息。同樣,擋在非客戶區按下或釋放一個滑鼠鍵時,視窗過程會收到下列訊息。
滑鼠左鍵: WM_NCLBUTTONDOWN(按下) WM_NCLBUTTONUP(擡起) WM_NCLBUTTONDBLCLK(雙擊)
滑鼠中鍵: WM_NCMBUTTONDOWN WM_NCMBUTTONUP WM_NCMBUTTONDBLCLK
滑鼠右鍵: WM_NCRBUTTONDOWN WM_NCRBUTTONUP WM_NCRBUTTONDBLCLK
其中,非客戶區滑鼠訊息的wParam引數表示移動或者單擊滑鼠鍵的非客戶區位置。(注意不是座標)而是一組以HT開頭定義的識別符號。
lParam引數的同客戶區滑鼠訊息類似,低位字為x座標,高位字為y座標,但之得注意的事,這裡的座標不同於客戶區滑鼠訊息中的客戶區座標,這裡的座標而是指螢幕座標。我們可以使用Windows提供的兩個函式來進行客戶區和螢幕之間的轉變。即ScreenToClient函式和ClientToScreen函式。
另外,我們還需要知道的一點是,我們現在通常的滑鼠,中間都有一個滑鼠輪,我們按下滑鼠輪時,產生的是滑鼠中鍵的訊息。我們也可以滾動它,這是將會產生一個WM_MOUSEWHEEL訊息。如果我們的程式需要的話,我們可以捕獲這個訊息。
(其實,一般在我們的程式中,很少去處理非客戶區滑鼠訊息,這裡僅作了解!)
示例
現在,我們來簡單的使用以下上面的滑鼠訊息,我們這裡演示以下如何使用滑鼠像畫圖程式那樣畫出一個矩形。
其中視窗處理函式如下所示:
這裡需要注意的是對WM_MOUSEMOVE訊息的處理,在看這個訊息之前,我們先來了解一下SetROP2函式。SetROP2是用來設定畫圖模式的函式,設定前景與背景的關係。這個函式有兩個引數,第一個引數為DC控制代碼,第二個引數為繪圖模式。常用的繪圖模式如下所示:LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { // 矩形的起點和終點 static POINT ptBegin, ptEnd; // fLButton標識滑鼠左鍵是否按下,fDone標識是否完成一個矩形的繪製 static BOOL fLButton, fDone; HDC hdc; PAINTSTRUCT ps; switch (message) { case WM_LBUTTONDOWN: fLButton = TRUE; ptBegin.x = ptEnd.x = LOWORD(lParam); ptBegin.y = ptEnd.y = HIWORD(lParam); SetCapture(hWnd); break; case WM_LBUTTONUP: ptEnd.x = LOWORD(lParam); ptEnd.y = HIWORD(lParam); fLButton = FALSE; fDone = TRUE; ReleaseCapture(); InvalidateRect( hWnd, NULL, TRUE ); break; case WM_MOUSEMOVE: if( fLButton ) { hdc = GetDC( hWnd ); SetROP2( hdc, R2_NOT ); SelectObject( hdc, GetStockObject( NULL_BRUSH ) ); Rectangle( hdc, ptBegin.x, ptBegin.y, ptEnd.x, ptEnd.y ); ReleaseDC( hWnd, hdc ); ptEnd.x = LOWORD( lParam ); ptEnd.y = HIWORD(lParam ); /* 註釋下面的程式碼,看看會出現什麼樣的效果 */ hdc = GetDC( hWnd ); SetROP2( hdc, R2_NOT ); SelectObject( hdc, GetStockObject( NULL_BRUSH ) ); Rectangle( hdc, ptBegin.x, ptBegin.y, ptEnd.x, ptEnd.y ); ReleaseDC( hWnd, hdc ); } break; case WM_PAINT: hdc = BeginPaint( hWnd, &ps ); if( fDone ) { SelectObject( hdc, GetStockObject(BLACK_BRUSH) ); Rectangle( hdc, ptBegin.x, ptBegin.y, ptEnd.x, ptEnd.y ); } EndPaint( hWnd, &ps ); break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, message, wParam, lParam); }
值 | 說明 |
R2_BLACK | 畫素顏色始終為0(即黑色) |
R2_WHITE | 畫素顏色始終為1(即白色) |
R2_COPYPEN | 畫素為畫筆的顏色 |
R2_NOTCOPYPEN | 畫素為畫筆相反的顏色 |
R2_NOP | 畫素不發生任何變化 |
R2_NOT | 畫素反轉 |
R2_NOTXORPEN | 畫素為當前畫素與畫筆“異或”的值再取反 |
R2_XORPEN | 畫素為當前畫素與畫筆“異或”的值 |