對WM_NCHITTEST消息的瞭解+代碼實例進行演示

轉自:http://blog.csdn.net/yiruirui0507/article/details/6081069

這個消息比較實用也很關鍵,它代表非顯示區域命中測試。這個消息優先於所有其他的顯示區域和非顯示區域鼠標消息。其中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來處理。



#include <windows.h>
#define DIVISIONS 5
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName [] = TEXT ("RECT") ;
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW|CS_DBLCLKS ;//客戶區想要響應雙擊,則CS_DBLCLKS得註冊進去
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL;
     wndclass.lpszClassName = szAppName ;
     
     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"),
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName, TEXT ("RECT TEST"), 
                          WS_OVERLAPPEDWINDOW, 
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;

     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}
     static int     cxClient, cyClient;
         static BOOL state[DIVISIONS][DIVISIONS];
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

     HDC            hdc;
         HPEN hpen;
     PAINTSTRUCT    ps ;
         RECT     rect;
         int i,j;
         int x,y;
		 //INT a;
		//char buf[10];
         char szAppName[]="RECTANGLE";
		 LRESULT b;
     
     switch (message)
     {
     case WM_CREATE:
			//a=GetDoubleClickTime();
			//itoa(a,buf,10);
			//MessageBoxA(hwnd,buf,"hi",0);// 得到鼠標雙擊的間隔
          return 0 ;


     case WM_SIZE:
          cxClient = LOWORD (lParam)/DIVISIONS ;
          cyClient = HIWORD (lParam)/DIVISIONS ;
		  //if(IsIconic(hwnd))
			  //MessageBox(hwnd,TEXT("窗口已然最小化"),TEXT("情況"),0);
		  //if(IsZoomed(hwnd))
			    //MessageBox(hwnd,TEXT("窗口已然最大化"),TEXT("情況"),0);
          return 0 ;
	 case WM_LBUTTONDBLCLK:
		 POINT point;
		 point.x=LOWORD(lParam);
		 point.y=HIWORD(lParam);
		 PostMessage(hwnd,WM_NCLBUTTONDBLCLK,HTCAPTION,MAKELPARAM(point.x,point.y));//實行欺騙
		 //SetWindowPos(hwnd,
		 return 0;
         case WM_LBUTTONDOWN:
			 {
                        x=LOWORD(lParam);
                        y=HIWORD(lParam);
                        i=j=0;
                        while(!(x<i*cxClient))
                        {
                                ++i;
                        }
                        while(!(y<j*cyClient))
                        {
                                ++j;
                        }
                        if(state[i-1][j-1]==TRUE)
                        {
                                state[i-1][j-1]=FALSE;
                        }
                        else
                        {
                                state[i-1][j-1]=TRUE;
                        }
                        InvalidateRect(hwnd,NULL,TRUE);
						POINT point;
						point.x=LOWORD(lParam);
						point.y=HIWORD(lParam);
						PostMessage(hwnd,WM_NCLBUTTONDOWN,HTCAPTION,MAKELPARAM(point.x,point.y));//實行欺騙
	 }
                        return 0;

     case WM_PAINT:
                 hdc=BeginPaint(hwnd,&ps);
                 GetClientRect(hwnd,&rect);
                 hpen=CreatePen(PS_SOLID,2,RGB(255,0,0));
                
                for(j=0;j<DIVISIONS;j++)
                        for(i=0;i<DIVISIONS;i++)
                        {
                                Rectangle(hdc,i*cxClient,j*cyClient,(i+1)*cxClient,(j+1)*cyClient);
                                if(state[i][j])
                                {
                                         SelectObject(hdc,hpen);
                                        MoveToEx(hdc,i*cxClient,j*cyClient,NULL);
                                        LineTo(hdc,(i+1)*cxClient,(j+1)*cyClient);
                                                MoveToEx(hdc,i*cxClient,(j+1)*cyClient,NULL);
                                        LineTo(hdc,(i+1)*cxClient,j*cyClient);
                                        SelectObject(hdc,GetStockObject(BLACK_PEN));
									
                                                 
                                }
                        }
							DeleteObject(hpen);
                 EndPaint(hwnd,&ps);

          return 0 ;
	 //case WM_NCLBUTTONDOWN:
		 //return (LRESULT)HTCAPTION;
	 case WM_NCHITTEST:
		 
	b=DefWindowProc(hwnd,message,wParam,lParam);
	switch(b)
	{
	case HTCLIENT:
		SetWindowText(hwnd,TEXT("點擊的是客戶區"));
		return b;
	case HTCAPTION:
		SetWindowText(hwnd,TEXT("點擊的是標題欄"));
		return b;
	case HTBOTTOM:
		SetWindowText(hwnd,TEXT("點擊的是下邊框"));
		return b;
	case HTBOTTOMLEFT:
		SetWindowText(hwnd,TEXT("點擊的是左下邊框"));
		return b;
	case HTCLOSE:
		SetWindowText(hwnd,TEXT("點擊的是關閉按鈕"));
		return b;
	case HTLEFT:
		SetWindowText(hwnd,TEXT("點擊的是左邊框"));
		return b;
	case HTMAXBUTTON:
		SetWindowText(hwnd,TEXT("點擊的是最大化按鈕"));
		return b;
	case HTMINBUTTON:
		SetWindowText(hwnd,TEXT("點擊的是最小化按鈕"));
		return b;
	case HTRIGHT:
		SetWindowText(hwnd,TEXT("點擊的是右邊框"));
		return b;
	case HTSYSMENU:
		SetWindowText(hwnd,TEXT("點擊的是系統菜單"));
		return b;
	case HTTOP:
		SetWindowText(hwnd,TEXT("點擊的是上邊框"));
		return b;
	case HTBOTTOMRIGHT:
		SetWindowText(hwnd,TEXT("點擊的是右下邊框"));
		return b;
	default:
		return b;
	}



     case WM_DESTROY:
        
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}


轉自:http://blog.csdn.net/debehe/article/details/4412959


問題: 
我出來的WM_NCHITTEST,但同時又想處理WM_RBUTTONUP,可以結果卻是兩者無法共存。而我的需求就是“兩者共存”。


問題原因: 
對 WM_NCHITTEST與WM_LBUTTONDOWN、WM_NCLBUTTONDOWN、WM_LBUTTONUP、 WM_NCLBUTTONUP、WM_RBUTTONUP、WM_NCRBUTTONUP、WM_MOUSEMOVE、WM_NCMOUSEMOVE的關係理解錯誤

具體分析: 
無標題窗口拖動,我們一般是採用:
響應WM_NCHITTEST消息,返回HTCAPTION來實現。
但是,如果在這同時還要處理鼠標的消息,如WM_LBUTTONDOWN、WM_LBUTTONUP、WM_RBUTTONUP、WM_MOUSEMOVE,你會發現,這些消息都收不到了。

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

解決煩方案:
 
呵呵,自然是同時處理WM_NCHITTEST和WM_NCRBUTTONUP,而不處理WM_RBUTTONUP

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章