1. 程式人生 > >windows基礎應用程式程式設計(七)滑鼠訊息

windows基礎應用程式程式設計(七)滑鼠訊息

上一篇中,我們介紹了鍵盤訊息,接下來,我們來了解一下滑鼠訊息。 在上一篇中,我們知道Windows只把鍵盤訊息傳送給擁有輸入焦點的視窗。滑鼠訊息與此不同,只要滑鼠跨越視窗或者在某視窗中按下滑鼠鍵,那麼視窗過程就會受到滑鼠訊息,而不管該視窗是否活動或者是否擁有輸入焦點。同鍵盤訊息類似,滑鼠訊息也簡單的可以分為客戶區滑鼠訊息和非客戶區滑鼠訊息。

客戶區滑鼠訊息

當滑鼠通過視窗的客戶區時,視窗過程受到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訊息。如果我們的程式需要的話,我們可以捕獲這個訊息。 (其實,一般在我們的程式中,很少去處理非客戶區滑鼠訊息,這裡僅作了解!)

示例

現在,我們來簡單的使用以下上面的滑鼠訊息,我們這裡演示以下如何使用滑鼠像畫圖程式那樣畫出一個矩形。

其中視窗處理函式如下所示:

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);
}
這裡需要注意的是對WM_MOUSEMOVE訊息的處理,在看這個訊息之前,我們先來了解一下SetROP2函式。SetROP2是用來設定畫圖模式的函式,設定前景與背景的關係。這個函式有兩個引數,第一個引數為DC控制代碼,第二個引數為繪圖模式。常用的繪圖模式如下所示:
說明
R2_BLACK 畫素顏色始終為0(即黑色)
R2_WHITE 畫素顏色始終為1(即白色)
R2_COPYPEN 畫素為畫筆的顏色
R2_NOTCOPYPEN 畫素為畫筆相反的顏色
R2_NOP 畫素不發生任何變化
R2_NOT 畫素反轉
R2_NOTXORPEN 畫素為當前畫素與畫筆“異或”的值再取反
R2_XORPEN 畫素為當前畫素與畫筆“異或”的值
我們在對WM_MOUSEMOVE訊息進行處理的時候,我們呼叫了兩次繪圖函式,並且使用了畫素反轉模式。這樣我們第二次繪製的矩形,會首先被下一次的WM_MOUSEMOVE訊息處理中的第一次繪製所覆蓋,這樣就消除了滑鼠繪圖的軌跡。可以把第二次繪圖給註釋掉,然後再觀察一下繪圖效果發生了什麼變化。 另外,我們的這個程式中還存在著一個問題,那就是,當我們在客戶區按下滑鼠左鍵,並移動滑鼠到客戶區外。這時,我們的程式就不能再接受WM_MOUSEMOVE訊息了,程式出現了不理想的效果。我們可以觀察一下,windows系統自帶的繪圖程式,發現這樣操作時,windows的繪圖程式仍然能夠響應滑鼠移動的變化。那麼我們怎麼在我們的程式中新增這個功能呢?非常簡單,我們只需要使用一個函式SetCapture。這個函式就一個引數表示視窗控制代碼。呼叫這個函式之後,Windows將所有滑鼠訊息發給視窗控制代碼為所傳入引數的視窗過程。滑鼠訊息總是客戶區訊息。即使滑鼠位於非客戶區。當我們釋放滑鼠時,我們還需要釋放滑鼠捕獲。即呼叫ReleaseCapture來釋放。 所以現在我們在上面的視窗回撥函式中,在WM_LBUTTONDOWN訊息處理中,加上SetCapture(hWnd);在WM_LBUTTONUP訊息處理中,加上ReleaseCapture();這個時候再執行程式,就可以看到我們想要的效果了。 鍵盤和滑鼠訊息就介紹這麼多,其中涉及到很多之前沒見到的函式,不能一一介紹,可以多查MSDN,積累很重要。