1. 程式人生 > >7.3 非客戶區滑鼠訊息

7.3 非客戶區滑鼠訊息

摘錄於《Windows程式(第5版,珍藏版).CHarles.Petzold 著》P230

        到目前為止,所有討論的 10 種滑鼠訊息都是發生在視窗客戶區內的移動或單擊。如果滑鼠位於視窗內部除客戶區外的其他區域,Windows 就會向視窗過程傳送一個“非客戶區”滑鼠訊息。視窗的非客戶區包括標題欄、選單和視窗滾動條

        系統一般不需要使用者處理非客戶區滑鼠訊息。取而代之的是,使用者只需將這些訊息傳送給 DefWindowProc,從而使 Windows 執行系統函式。從這個角度來看,非客戶區滑鼠訊息與 WM_SYSKEYDOWN、WM_SYSKEYUP 和 WM_SYSCHAR 這樣的系統鍵盤訊息很相似。

        非客戶區滑鼠訊息幾乎與客戶區滑鼠訊息完全對應。訊息的識別符號包含了字母“NC”,表示“非客戶”(nonclient)。如果滑鼠在視窗的非客戶區內移動,視窗過程就會接收 WM_NCMOUSEMOVE 訊息。滑鼠按鈕產生的訊息如下表所示。

按  鈕 按  下 釋  放 第二次按下按鈕
 左鍵  WM_NCLBUTTONDOWN  WM_NCLBUTTONUP  WM_NCLBUTTONDBLCLK
 中鍵  WM_NCMBUTTONDOWN  WM_NCMBUTTONUP  WM_NCMBUTTONDBLCLK
 右鍵  WM_NCRBUTTONDOWN  WM_NCRBUTTONUP  WM_NCRBUTTONDBLCLK

        非客戶區滑鼠訊息的引數 wParam 和 lParam 與客戶區滑鼠訊息的引數有些不同。引數 wParam 表示非客戶區滑鼠移動或單擊的位置。它的值被設定成一個以 HT 為首的識別符號。其中 HT 表示“擊中測試”(hit-test)。這些識別符號都定義在 WINUSER.H 標頭檔案中。

        引數 lParam 的低位字包含 x 座標,高位字包含 y 座標。但是,這些座標都是螢幕座標,而不是前面的客戶座標。對螢幕座標來說,顯示區域左上角的 x 和 y 都是 0。向右表示 x 值增加的方向,而沿螢幕向下表示 y 值增加的方向

。(如圖 7-3 所示。)

        利用下面兩個 Windows 函式,可以將螢幕座標與客戶區座標相互轉換:

  1. ScreenToClient (hwnd, &pt);  
  2. ClientToScreen (hwnd, &pt);  
其中 pt 是一個 POINT 結構。這兩個函式轉換 POINT 結構儲存的座標值,且不保留過去的值。

        注意,如果一個螢幕座標點位於視窗客戶區的上方或者左方,那麼轉換成客戶區座標後,x 值或 y 值會是負數。

7.3.1  擊中測試訊息

        數一數,對於所有 21 個滑鼠訊息,到現在我們已經學習了其中 20 個。最後一個訊息是 WM_NCHITTEST,表示“非客戶區擊中測試”(nonclient hit test)。這個訊息的優先順序高於其他所有的客戶區和非客戶區滑鼠訊息。引數 lParam 包含滑鼠位置的螢幕座標 x 和 y。引數 wParam 沒有用到。

        Windows 應用程式通常會把這個訊息傳送給 DefWindowProc。然後 Windows 會利用 WM_NCHITTEST 訊息來產生所有其他和滑鼠位置相關的滑鼠訊息。對非客戶區訊息來說,DefWindowProc 處理 WM_NCHITTEST 訊息後返回一個可用於滑鼠訊息引數 wParam 的值。這個返回值可以是任何一個非客戶區滑鼠訊息的 wParam 引數的值,也可以是如下所示的一些值:

  1. HTCLIENT                 客戶區  
  2. HTNOWHERE                不在任何視窗  
  3. HTTRANSPARENT            被另一個視窗覆蓋的視窗  
  4. HTERROR                  使函式 DefWindowProc 產生一個警示聲  
如果 DefWindowProc 在處理 WM_NCHITEST 訊息之後返回 HTCLIENT,則Windows 會將螢幕座標轉換成客戶區座標,併產生一個客戶區滑鼠訊息

        你可能記得如何捕捉 WM_SYSKEYDOWN 訊息,使所有的系統鍵盤函式失效。也許你會想,可不可以利用滑鼠訊息來實現類似的功能呢?當然可以!如果在視窗過程中包含下面幾行語句:

  1. case WM_NCHITTEST:  
  2.      return (LRESULT) HTNOWHERE;  
那麼程式就能夠有效地阻止系統向視窗傳送所有客戶區和非客戶區滑鼠訊息。此時,無論滑鼠位於視窗的任何位置,包括系統選單圖示、調整大小按鈕和關閉按鈕,滑鼠按鈕操作都將失效。

7.3.2  訊息引發訊息

        Windows 利用 WM_NCHITTEST 訊息來產生其他所有的滑鼠訊息。這種訊息引發訊息的思想在 Windows 中很常見。舉一個例子來說,你可能知道,雙擊 Windows 程式的系統選單圖示可以關閉這個這個視窗。雙擊產生了一系列 WM_NCHITTEST 訊息。滑鼠位於系統選單圖示之上,所以 DefWindowProc 返回 HTSYSMENU,這時 Windows 在訊息佇列中添加了一個 WM_NCLBUTTONDBLCLK 訊息,其中引數 wParam 等於 HTSYSMENU。

        視窗過程一般將這個訊息傳送給 DefWindowProc。當 DefWindowProc 接收到引數 wParam 為 HTSYSMENU 的 WM_NCLBUTTONDBLCLK 訊息時,系統會在訊息佇列中新增一個 WM_SYSCOMMAND 訊息,其中引數 wParam 等於 SC_CLOSE。(當用戶選擇系統選單中的關閉按鈕時,也產生 WM_SYSCOMMAND 訊息。)然後,通常視窗過程再將這個訊息傳送給 DefWindowProc。DefWindowProc 處理這個訊息,並向視窗傳送 WM_CLOSE 訊息。

        如果想在結束程式之前等待使用者的確認,視窗過程可以捕捉 WM_CLOSE 訊息。否則,DefWindowProc 會呼叫 DestroyWindow 函式來處理 WM_CLOSE 訊息。除了其他處理,DestroyWindow 還會向視窗過程傳送一個 WM_DESTROY 訊息。在正常情況下,視窗過程處理 WM_DESTROY 訊息的程式碼如下。

  1. case WM_DESTROY:  
  2.     PostQuitMessage (0);  
  3.     reutrn 0;  
PostQuitMessage 函式使 Windows 在訊息佇列中新增一個 WM_QUIT 訊息。視窗過程不會接收到這個訊息,因為它會導致 GetMessage 函式返回 0,從而結束了訊息迴圈,整個程式也就退出了。