WINDOWS應用程序窗口一般包括兩種:普通窗口和常居頂層的無標題條高級窗口。前者是由WINDOWS內部功能定制的,它具有WINDOWS應用程序窗口的所有普通特性:具有標題條、窗口邊框、最大化按鈕、最小化按鈕和系統默認的快捷鍵及鼠標支持功能等,利用鼠標左鍵拖動該種窗口的標題條可以在屏幕上任意移動窗口,當鼠標光標停在窗口邊框上時可以改變窗口大小;後者是一種定制的高級窗口,它不具有普通窗口的任何屬性,整個窗口的控制必須由編程者來一一確定,使用這種窗口的典型實例有WINDOWS中的IME輸入法應用程序、UCWIN4.0平台、各種浮動工具箱、Office中的桌面工具欄和第三方開發的漢字輸入平台等。
WINDOWS 這種無標題條常居頂層高級窗口的一個顯著特點是,不需改變窗口大小但必須具有窗口的客戶區域拖動功能。由於普通窗口的拖動功能是由系統來完成的,編制普通的應用程序根據無須考慮客戶區域拖動問題,因此一般編程人員很難碰到這個問題,更談不上如何實現這一功能了。開發者往往希望自己開發出來的軟件具有經典軟件中的窗口客戶區域拖動功能,筆者曾經利用模擬系統鼠標點擊標題條拖動窗口和WINDOWS系統內部提供的API發送函數發送內部拖動命令來實現無標題常居頂層高級窗口的客戶拖動功能,結果都不理想。後來只好在窗口函數中通過直接處理WM_LBUTTONDOWN、WM_MOUSEMOVE和WM_LBUTTONUP消息,自行控制窗口拖動的客戶命令區、拖動開始、窗口移動、拖動虛框繪制、虛框移動和拖動結束等過程,來實現高級頂層窗口的客戶區域拖動方案。下面就自己實踐經驗具體介紹實現該方案的具體方法和主要技巧。
一、WINDOWS檢測客戶拖動命令及鼠標光標動態提示的實現方法
WINDOWS 無標題條常居頂層高級窗口的客戶區域一般分為兩種:特定客戶命令區域和非特定客戶命令區域。特定客戶命令區域是指利用"RECT"定義的特定子矩形區域,窗口函數對發生在該區域內的鼠標命令進行檢測並處理;非特定客戶命令區域是指沒有明確定義的窗口客戶區域部分,即所有特定客戶命令區域之外的部分,窗口函數根據實際需要來確定是否對該區域內發生的鼠標命令進行處理。實現常居頂層高級窗口拖動功能的首要問題,是如何檢測和處理特定客戶命令區域和非特定客戶命令區域內的鼠標命令,以及如何利用鼠標光標來動態提示用戶此時可以進行窗口的拖動操作。
1、在特定客戶區域檢測鼠標命令的方法
當窗口中設置了實現拖動功能的圖標命令按鈕時,就必須在資源文件中定義命令按鈕的特定客戶區域,該區域一般也就是顯示命令按鈕中圖標的矩形區域,這個區域的定義方法為"RECT DragRT",其中DragRT為定義的檢測鼠標命令矩形區域,它用DragRT.LEFT、DragRT.TOP、DragRT.RIGHT和DragRT.BOTTOM四個參數來描述矩形區域相對於窗口客戶區域左上角的相對坐標值,這四個參數必須事先定義具體的數值,也可以利用"SETRECT"函數直接填充。
窗口函數在處理鼠標消息WM_LBUTTONDOWN時,在接收系統傳遞的鼠標位置參數lParam後,通過MAKEPOINT( )函數將其轉換為窗口坐標值,利用判定某坐標點是否位於特定矩形區域內的函數PtInRect(),就可以判定鼠標指針是否點擊在拖動命令按鈕之內,從而完成窗口拖動功能的啟動任務。其描述性功能代碼示例如下:
case WM_LBUTTONDOWN://鼠標光標點擊處理
POINT pt;//鼠標在屏幕上位置指針,包括pt.X和pt.Y兩個參數,
//該指針值利用MAKEPOINT通過lParam參數轉換而來
pt=MAKEPOINT(lParam); //獲取鼠標當前屏幕位置指針
if(PtInRect(&DragRT,pt)){//判定鼠標是否點擊在拖動按鈕內
//實現鼠標拖動窗口方案的啟動功能
} else {
//進行其它特定或非特定命令客戶區域判定處理
}
break;
2、在非特定客戶區域檢測鼠標命令的方法
當窗口應用程序中采取了非特定客戶區域拖動方法時,必須在資源文件中事先確定各個特定客戶區域的矩形坐標,這時非特定客戶區域是不規則的區域,它需要根據實際的應用程序窗口及各個命令按鈕矩形區域來確定,也就是各個命令按鈕相對於窗口矩形區域的“非”子集。窗口函數在處理鼠標消息WM_LBUTTONDOWN時,首先利用函數PtInRect()判定當前鼠標指針是否點擊在各個命令按鈕矩形區域內,假如未點擊在任何命令按鈕區域內,則可確定鼠標點擊在非特定客戶區域內,從而實現窗口拖動功能的啟動。其描述性功能代碼示例如下:
case WM_LBUTTONDOWN: //鼠標光標點擊處理
POINT pt; //定義鼠標在屏幕上的位置指針
pt=MAKEPOINT(lParam); //取得鼠標光標當前位置指針
for(I=0;I
if(PtInRect(&DragRT[I],pt)){//DragRT[I]為按鈕矩形數組
break; //鼠標點擊在其它按鈕上中斷
}
}
if(I
//鼠標點擊在其它特定客戶區域內則處理其它按鈕功能
}else{
//鼠標點擊在非客戶區域內則完成窗口拖動方案的啟動
}
break;
3、窗口拖動功能的鼠標光標動態提示方法
在無標題條常居頂層高級窗口應用程序中,既可以采用將特定客戶區域作為拖動命令按鈕的方法,也可以采取在非特定客戶區域檢測窗口拖動命令的方法,或者兩種方法兼顧使用。在使用第一種方法時,可以在命令按鈕中用特定的圖標或文字來提示用戶該命令按鈕的功能,而後一種方法由於矩形區域無法確定不可能用圖標或文字來提示,或根本無法顯示圖標和文字(如非特定客戶區域為窗口邊界區域等),用戶根本無法知道非特定客戶區域具有拖動窗口功能,這時唯有充分利用鼠標光標的動態提示功能,就象WINDOWS 普通窗口中鼠標光標停在窗口邊框上時鼠標光標變成雙箭頭外形來提示用戶此時可以改變窗口大小那樣,這個功能在高級窗口界面設計中非常重要。
實現鼠標光標動態提示功能前需要定制鼠標光標外形,窗口拖動功能的動態提示光標外形一般為四箭頭圖案,這可以利用微軟公司的SDK、FPT3.0和VC++4.1等高級開發軟件中的資源編輯器"IMAGE ED99v"等來實現。光標資源文件一般為32X32的2色或16色.CUR圖形文件,可根據實現的功能來具體確定光標圖案或直接使用WINDOWS 系統中提供的光標資源文件,當自己利用資源編輯器繪制光標圖案後,還需要利用DEBUG. EXE程序修改光標資源文件中的鼠標光標顯示偏移坐標,以便光標圖案能象WINDOWS 系統中的動態提示光標一樣,動態提示時光標圖案中心點正好處於屏幕的當前位置。這個偏移坐標值位於光示資源文件中的10和12處的雙字節位置,如動態提示光標資源文件名為MOUSEM.CUR,要使32X32(2色)的光標圖形顯示時圖案的中心點正好處於當前屏幕位置,其修改方法如下:
C>DEBUG MOUSEM.CUR
-E 10A
XXXX:10A 00.10 00.00 00.10 00.00
-W
建立起自己的鼠標光標資源文件後,首先需要在應用程序的資源文件中定義鼠標光標,資源文件中的定義方法為:
imecurm CURSOR mousem.cur
鼠標光標資源文件只有在定義之後,才能在應用程序中利用LoadCursor()函數調入內存使用,其調用方法為:
HCURSOR hCurm;//將鼠標光標資源文件數據調入內存
hCurm=LoadCursor(hInstance,"imecurm");
當需要動態改變鼠標光標外形的客戶區域為整個窗口或某個子窗口的全部客戶區域時,在注冊客戶應用程序窗口類時定義相應的鼠標光標資源句柄,當鼠標光標移到相應窗口內時馬上變成定制的光標外形,移出相應窗口時自動恢復原來光標外形。實現鼠標光標這一動態提示功能的定義方法如下:
wc.hCursor=hCurm;
當鼠標光標需要在窗口的特定客戶命令按鈕區域內或非特定客戶命令區域內進行動態提示時,就不能使用上述定義方法,必須在窗口函數處理WM_MOUSEMOVE消息時進行非凡處理:首先判定鼠標光標指針當前位置是否在拖動命令按鈕或非特定客戶區域內移動,假如鼠標指針位置滿足拖動窗口功能區域的要求,則利用API函數SETCURSOR()改變鼠標光標圖案,提示用戶此時可以進行窗口拖動操作,並將鼠標輸入控制權交給當前窗口,同時設置改變鼠標光標標志;當鼠標指針移出拖動窗口啟動命令區域時,恢復原來鼠標光標圖案同時釋放鼠標輸入焦點控制權,並清除鼠標光標動態提示標志單元。其功能性代碼描述如下:
BOOL DragFlag; //動態提示光標標志
case WM_MOUSEMOVE: //鼠標光標移動處理
pt=MAKEPOINT(lParam); //鼠標光標當前位置指針
if(PtInRect(&DragRT,pt)){//鼠標指針在拖動命令區域內則
SetCursor(hCurm); //動態改變鼠標光標外形
SetCapture(hWnd); //將鼠標輸入控制權交當前窗口
iFlag=TRUE; //設置鼠標光標外形改變標志
} else if(iFlag==TRUE){ //鼠標指針未在拖動命令區域內
SetCursor(LoadCursor(NULL,IDC_ARROW));//恢復原外形
ReleaseCapture() //釋放鼠標輸入控制權