[轉] 對WM_NCHITTEST消息的了解+代碼實例進行演示


這個消息比較實用也很關鍵,它代表非顯示區域命中測試。這個消息優先於所有其他的顯示區域和非顯示區域鼠標消息。其中lParam參數含有鼠標位置的x和y屏幕坐標,wParam 這里沒有用。

Windows應用程序通常把這個消息傳送給DefWindowProc,然后Windows用WM_NCHITTEST消息產生與鼠標位置相關的所有其他鼠標消息。通俗的講從消息產生消息。

case WM_NCHITTEST:

return (LRESULT)HTNOWHERE;

以上代碼能禁用窗口的所有顯示區域和非顯示區域鼠標消息,也就是當鼠標在窗口,這里包括系統菜單圖標,縮放按鈕,關閉按鈕等時,鼠標按鍵將會失效。

先看一下這個消息的返回值吧:

One of the mouse hit-test enumerated values listed below.

 下面列出的鼠標擊中測試枚舉值之一。  

· HTBORDER 在不具有可變大小邊框的窗口的邊框上。 

· HTBOTTOM 在窗口的水平邊框的底部。 

· HTBOTTOMLEFT 在窗口邊框的左下角。  

· HTBOTTOMRIGHT 在窗口邊框的右下角。  

· HTCAPTION 在標題條中。  

· HTCLIENT 在客戶區中。  

· HTERROR 在屏幕背景或窗口之間的分隔線上(與HTNOWHERE相同,除了Windows的DefWndProc函數產生一個系統響聲以指明錯誤)。  

· HTGROWBOX 在尺寸框中。  

· HTHSCROLL 在水平滾動條上。  

· HTLEFT 在窗口的左邊框上。  

· HTMAXBUTTON 在最大化按鈕上。  

· HTMENU 在菜單區域。  

· HTMINBUTTON 在最小化按鈕上。  

· HTNOWHERE 在屏幕背景或窗口之間的分隔線上。  

· HTREDUCE 在最小化按鈕上。  

· HTRIGHT 在窗口的右邊框上。  

· HTSIZE 在尺寸框中。(與HTGROWBOX相同)  

· HTSYSMENU 在控制菜單或子窗口的關閉按鈕上。  

· HTTOP 在窗口水平邊框的上方。  

· HTTOPLEFT 在窗口邊框的左上角。  

· HTTOPRIGHT 在窗口邊框的右上角。  

· HTTRANSPARENT 在一個被其它窗口覆蓋的窗口中。  

· HTVSCROLL 在垂直滾動條中。  

· HTZOOM 在最大化按鈕上。

關於這個消息的一個經典應用莫過於如何拖動一個無標題欄的窗體或者說我如何實現在客戶區也能拖動此窗體,簡單來說就是對WINDOWS進行了欺騙。

一個思考3秒就容易想到的方法是:處理鼠標消息WM_LBUTTONDOWN和WM_LBUTTONUP。在OnLButtonUp函數中計算鼠標位置的變化,調用MoveWindow實現窗口的移動。

PS:拖動標題欄移動窗口的時候,會出現一個矩形框,它提示了窗口移動的當前位置。當鼠標左鍵放開的時候,窗口就移動到矩形框所在位置。而我們剛才的那個的實現方案中沒有這個功能。要實現此功能,我們必須自己來畫這些矩形。事實上,我們沒有必要自己來做這件事情,因為Windows已經給我們做好了。

試想,如果我能夠欺騙Windows,告訴它現在鼠標正在拖動的是標題欄而不是客戶區,那么窗口移動操作就由Windows來代勞了。

到這里這個消息就要閃亮登場了,前面說過了WM_NCHITTEST的消息響應函數會根據鼠標當前的坐標來判斷鼠標命中了窗口的哪個部位,消息響應函數的返回值指出了部位,例如它可能會返回HTCAPTION,或者HTCLIENT等。

為了便於理解,先描述一下Windows對鼠標鍵按下的響應流程:

1. 確定鼠標鍵點擊的是哪個窗口。Windows會用表記錄當前屏幕上各個窗口的區域坐標,當鼠標驅動程序通知Windows鼠標鍵按下了,Windows根據鼠標的坐標確定它點擊的是哪個窗口。

2. 確定鼠標鍵點擊的是窗口的哪個部位。Windows會向鼠標鍵點擊的窗口發送WM_NCHITTEST消息,來詢問鼠標鍵點擊的是窗口的哪個部位。(WM_NCHITTEST的消息響應函數的返回值會通知Windows)。通常來說,WM_NCHITTEST消息是系統來處理的,用戶一般不會主動去處理它(也就是說,WM_NCHITTEST的消息響應函數通常采用的是Windows默認的處理函數)。

3. 根據鼠標鍵點擊的部位給窗口發送相應的消息。例如:如果WM_NCHITTEST的消息響應函數的返回值是HTCLIENT,表示鼠標點擊的是客戶區,則Windows會向窗口發送WM_LBUTTONDOWN消息;如果WM_NCHITTEST的消息響應函數的返回值不是HTCLIENT(可能是HTCAPTION、HTCLOSE、HTMAXBUTTON等),即鼠標點擊的是非客戶區,Windows就會向窗口發送WM_NCLBUTTONDOWN消息。

這里有必要詳細討論一下:如果WM_NCHITTEST的消息響應函數的返回值是HTCAPTION,即指示了鼠標點擊了標題欄,接下去Windows的處理是怎樣的?

上面已經提到,接下來,Windows會向窗口發送WM_NCLBUTTONDOWN消息。

MSDN對WM_NCLBUTTONDOWN消息描述如下:

WM_NCLBUTTONDOWN

nHittest = (INT) wParam; // hit-test value

pts = MAKEPOINTS(lParam); // position of cursor

WM_NCLBUTTONDOWN的wParam指示了鼠標點擊的窗口部位,lParam指示了當前鼠標的坐標。

如果應用程序沒有對該消息響應,則由系統默認處理。

系統默認處理又是怎樣的呢?系統發現wParam指示了鼠標點擊的是標題欄,就會標識當前窗口處於“拖拽狀態”(Windows內部記錄了每個窗口的狀態信息)。由於標識了“拖拽狀態”,則從此刻起到鼠標鍵放開之前,你的鼠標移動狀況完全由Windows跟蹤。它根據鼠標的移動,使得窗口作“同步”移動。

注意,這個過程中,窗口不會收到WM_NCMOUSEMOVE消息,因為窗口和鼠標是“同步”移動的,你的鼠標相對於窗口是靜止的。

最后再順路提一下,如果想在右鍵這個窗體的時候彈出一個菜單, 當完成 MSG_WM_RBUTTONDOWN 這個消息的時候,發現窗體收不到這個消息, 將WM_NCHITTEST消息的實現去掉就可以了,原因是:

因為你在WM_NCHITTEST中處理了鼠標消息,把他定位成HTCAPTION,也就是鼠標在標題欄上,而標題欄屬於非客戶區(NC); 
非客戶區的事件消息都是以WM_NC開頭的。也就是說,當你的WM_NCHITTEST返回HTCAPTION時,原來可以用WM_LBUTTONUP處理的消息,你只能用WM_NCLBUTTONUP來處理。

View Code
  1 #include <windows.h>
  2  #define DIVISIONS 5
  3  LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
  4  int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
  5                      PSTR szCmdLine, int iCmdShow)
  6  {
  7       static TCHAR szAppName [] = TEXT ("RECT") ;
  8       HWND         hwnd ;
  9       MSG          msg ;
 10       WNDCLASS     wndclass ;
 11  
 12       wndclass.style         = CS_HREDRAW | CS_VREDRAW|CS_DBLCLKS ;//客戶區想要響應雙擊,則CS_DBLCLKS得注冊進去
 13       wndclass.lpfnWndProc   = WndProc ;
 14       wndclass.cbClsExtra    = 0 ;
 15       wndclass.cbWndExtra    = 0 ;
 16       wndclass.hInstance     = hInstance ;
 17       wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
 18       wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
 19       wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
 20       wndclass.lpszMenuName  = NULL;
 21       wndclass.lpszClassName = szAppName ;
 22       
 23       if (!RegisterClass (&wndclass))
 24       {
 25            MessageBox (NULL, TEXT ("This program requires Windows NT!"),
 26                        szAppName, MB_ICONERROR) ;
 27            return 0 ;
 28       }
 29       
 30       hwnd = CreateWindow (szAppName, TEXT ("RECT TEST"), 
 31                            WS_OVERLAPPEDWINDOW, 
 32                            CW_USEDEFAULT, CW_USEDEFAULT,
 33                            CW_USEDEFAULT, CW_USEDEFAULT,
 34                            NULL, NULL, hInstance, NULL) ;
 35  
 36       ShowWindow (hwnd, iCmdShow) ;
 37       UpdateWindow (hwnd) ;
 38  
 39       while (GetMessage (&msg, NULL, 0, 0))
 40       {
 41            TranslateMessage (&msg) ;
 42            DispatchMessage (&msg) ;
 43       }
 44       return msg.wParam ;
 45  }
 46       static int     cxClient, cyClient;
 47           static BOOL state[DIVISIONS][DIVISIONS];
 48  LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 49  {
 50  
 51       HDC            hdc;
 52           HPEN hpen;
 53       PAINTSTRUCT    ps ;
 54           RECT     rect;
 55           int i,j;
 56           int x,y;
 57           //INT a;
 58          //char buf[10];
 59           char szAppName[]="RECTANGLE";
 60           LRESULT b;
 61       
 62       switch (message)
 63       {
 64       case WM_CREATE:
 65              //a=GetDoubleClickTime();
 66              //itoa(a,buf,10);
 67              //MessageBoxA(hwnd,buf,"hi",0);// 得到鼠標雙擊的間隔
 68            return 0 ;
 69  
 70  
 71       case WM_SIZE:
 72            cxClient = LOWORD (lParam)/DIVISIONS ;
 73            cyClient = HIWORD (lParam)/DIVISIONS ;
 74            //if(IsIconic(hwnd))
 75                //MessageBox(hwnd,TEXT("窗口已然最小化"),TEXT("情況"),0);
 76            //if(IsZoomed(hwnd))
 77                  //MessageBox(hwnd,TEXT("窗口已然最大化"),TEXT("情況"),0);
 78            return 0 ;
 79       case WM_LBUTTONDBLCLK:
 80           POINT point;
 81           point.x=LOWORD(lParam);
 82           point.y=HIWORD(lParam);
 83           PostMessage(hwnd,WM_NCLBUTTONDBLCLK,HTCAPTION,MAKELPARAM(point.x,point.y));//實行欺騙
 84           //SetWindowPos(hwnd,
 85           return 0;
 86           case WM_LBUTTONDOWN:
 87               {
 88                          x=LOWORD(lParam);
 89                          y=HIWORD(lParam);
 90                          i=j=0;
 91                          while(!(x<i*cxClient))
 92                          {
 93                                  ++i;
 94                          }
 95                          while(!(y<j*cyClient))
 96                          {
 97                                  ++j;
 98                          }
 99                          if(state[i-1][j-1]==TRUE)
100                          {
101                                  state[i-1][j-1]=FALSE;
102                          }
103                          else
104                          {
105                                  state[i-1][j-1]=TRUE;
106                          }
107                          InvalidateRect(hwnd,NULL,TRUE);
108                          POINT point;
109                          point.x=LOWORD(lParam);
110                          point.y=HIWORD(lParam);
111                          PostMessage(hwnd,WM_NCLBUTTONDOWN,HTCAPTION,MAKELPARAM(point.x,point.y));//實行欺騙
112       }
113                          return 0;
114  
115       case WM_PAINT:
116                   hdc=BeginPaint(hwnd,&ps);
117                   GetClientRect(hwnd,&rect);
118                   hpen=CreatePen(PS_SOLID,2,RGB(255,0,0));
119                  
120                  for(j=0;j<DIVISIONS;j++)
121                          for(i=0;i<DIVISIONS;i++)
122                          {
123                                  Rectangle(hdc,i*cxClient,j*cyClient,(i+1)*cxClient,(j+1)*cyClient);
124                                  if(state[i][j])
125                                  {
126                                           SelectObject(hdc,hpen);
127                                          MoveToEx(hdc,i*cxClient,j*cyClient,NULL);
128                                          LineTo(hdc,(i+1)*cxClient,(j+1)*cyClient);
129                                                  MoveToEx(hdc,i*cxClient,(j+1)*cyClient,NULL);
130                                          LineTo(hdc,(i+1)*cxClient,j*cyClient);
131                                          SelectObject(hdc,GetStockObject(BLACK_PEN));
132                                      
133                                                   
134                                  }
135                          }
136                              DeleteObject(hpen);
137                   EndPaint(hwnd,&ps);
138  
139            return 0 ;
140       //case WM_NCLBUTTONDOWN:
141           //return (LRESULT)HTCAPTION;
142       case WM_NCHITTEST:
143           
144      b=DefWindowProc(hwnd,message,wParam,lParam);
145      switch(b)
146      {
147      case HTCLIENT:
148          SetWindowText(hwnd,TEXT("點擊的是客戶區"));
149          return b;
150      case HTCAPTION:
151          SetWindowText(hwnd,TEXT("點擊的是標題欄"));
152          return b;
153      case HTBOTTOM:
154          SetWindowText(hwnd,TEXT("點擊的是下邊框"));
155          return b;
156      case HTBOTTOMLEFT:
157          SetWindowText(hwnd,TEXT("點擊的是左下邊框"));
158          return b;
159      case HTCLOSE:
160          SetWindowText(hwnd,TEXT("點擊的是關閉按鈕"));
161          return b;
162      case HTLEFT:
163          SetWindowText(hwnd,TEXT("點擊的是左邊框"));
164          return b;
165      case HTMAXBUTTON:
166          SetWindowText(hwnd,TEXT("點擊的是最大化按鈕"));
167          return b;
168      case HTMINBUTTON:
169          SetWindowText(hwnd,TEXT("點擊的是最小化按鈕"));
170          return b;
171      case HTRIGHT:
172          SetWindowText(hwnd,TEXT("點擊的是右邊框"));
173          return b;
174      case HTSYSMENU:
175          SetWindowText(hwnd,TEXT("點擊的是系統菜單"));
176          return b;
177      case HTTOP:
178          SetWindowText(hwnd,TEXT("點擊的是上邊框"));
179          return b;
180      case HTBOTTOMRIGHT:
181          SetWindowText(hwnd,TEXT("點擊的是右下邊框"));
182          return b;
183      default:
184          return b;
185      }
186  
187  
188  
189       case WM_DESTROY:
190          
191            PostQuitMessage (0) ;
192            return 0 ;
193       }
194       return DefWindowProc (hwnd, message, wParam, lParam) ;
195  }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM