WIN98特殊窗口的動態拖動
發表時間:2023-08-20 來源:明輝站整理相關軟件相關文章人氣:
[摘要]WIN98中常居頂層的無標題條窗口是一種特殊的窗口,典型實例有IME輸入法應用程序、UCWIN平臺、各種浮動工具箱、OFFICE桌面工具欄等。 一、命令檢測與光標動態提示 這種窗口拖動一般分...
WIN98中常居頂層的無標題條窗口是一種特殊的窗口,典型實例有IME輸入法應
用程序、UCWIN平臺、各種浮動工具箱、OFFICE桌面工具欄等。
一、命令檢測與光標動態提示
這種窗口拖動一般分為兩種:特定客戶命令區域和非特定客戶命令區域。特定
客戶命令區域是指利用"RECT"定義的特定子矩形區域;非特定客戶命令區域是指沒
有明確定義的窗口客戶區域部分,即所有特定客戶命令區域之外的部分。實現該功
能的首要問題是如何檢測和處理特定客戶命令區域和非特定客戶命令區域內的鼠標
命令,以及如何利用鼠標光標來動態提示用戶何時可以進行窗口的拖動操作。
1、特定客戶命令區域檢測鼠標命令
要在窗口中設置實現拖動功能的圖標命令按鈕,就必須在資源文件中定義命令
按鈕的特定客戶區域,該區域一般也就是命令按鈕圖標的矩形區域,這個區域的定
義方法為"RECT DragRT",其中DragRT為定義的檢測鼠標命令的矩形區域,它用
DragRT.LEFT、DragRT.TOP、DragRT.RIGHT和DragRT.BOTTOM四個參數來描述矩形區
域相對于窗口客戶區域左上角的相對坐標值。也可用"SETRECT"函數填充。
窗口函數在處理鼠標消息WM_LBUTTONDOWN時,當收到系統傳遞的鼠標位置參數
lParam后,通過MAKEPOINT()函數將其轉換為窗口坐標值,然后利用判斷某坐標點
是否位于特定矩形區域內的函數PtInRect(),判斷出鼠標指針是否點擊在拖動命令
按鈕之內。
2、非特定客戶命令區域檢測鼠標命令
當窗口應用程序中采取了非特定客戶命令區域拖動方法時,必須在資源文件中
事先確定各個特定客戶命令區域的矩形坐標,這時非特定客戶命令區域是不規則的
區域,它需要根據實際的應用程序窗口及各個命令按鈕矩形區域來確定,也就是各
個命令按鈕矩形區域相對于窗口矩形區域的“非”子集。窗口函數在處理鼠標消息
WM_LBUTTONDOWN時,首先利用函數PtInRect()判斷當前鼠標指針是否點擊在各個命
令按鈕矩形區域內,如果未點擊在任何命令按鈕區域內,則可確定鼠標點擊在非特
定客戶命令區域內,從而啟動窗口拖動功能。
3、窗口拖動功能的光標動態提示
實現鼠標光標動態提示功能前需要定制鼠標光標形狀,窗口拖動功能的動態提
示光標形狀一般為四箭頭圖案,這可以利用微軟公司的SDK、FPT3.0和VC++4.1等高
級開發軟件中的資源編輯器"IMAGE EDIT"等來實現。光標資源文件一般為32X32的2
色或16色.CUR圖形文件。建立起自己的鼠標光標資源文件后,首先需要在應用程序
的資源文件中定義鼠標光標,然后在應用程序中利用LoadCursor()調入,并在類中
進行設置:wc.hCursor=hCurm。
當鼠標光標需要在窗口的特定客戶命令按鈕區域內或非特定客戶命令區域內進
行動態提示時,不能使用上述定義方法,而需要在窗口函數處理WM_MOUSEMOVE消息
時進行特殊處理:首先判斷鼠標光標指針的當前位置是否在拖動命令按鈕或非特定
客戶命令區域內,如果鼠標指針位置滿足拖動窗口功能區域的要求,則利用API函
數SETCURSOR()改變鼠標光標圖案,提示用戶此時可以進行窗口拖動操作,并將鼠
標輸入控制權交給當前窗口,同時設置改變后的鼠標光標標志;當鼠標指針移出拖
動窗口命令啟動區域時,恢復原來鼠標光標圖案同時釋放鼠標輸入焦點控制權,并
清除鼠標光標動態提示標志單元。
二、動態拖動框的定制
窗口拖動前的關鍵問題是鼠標拖動窗口過程中的拖動框顯示與擦除功能實現。
窗口拖動虛框就是在WINDOWS 整個屏幕區域內顯示被拖動窗口大小的線框,它的大
小需要根據被拖動窗口的矩形區域大小和實際需要來具體確定,一般為被拖動窗口
的矩形區域大小。
WINDOWS系統中的繪圖方法是通過顯示設備描述表實現的,繪圖操作需要占用
一定的GDI資源。WINDOWS 95中的GDI資源要比WINDOWS3.X中的GDI資源大得多,所
以系統可以為窗口、菜單、對話框、字體和各種繪圖函數分配足夠的GDI資源。
WINDOWS中有兩種使用顯示設備描述符表的方法:更新窗口顯示客戶區域和直接操
作窗口顯示客戶區域。更新窗口顯示客戶區域是直接針對應用程序窗口矩形區域而
言的,在窗口函數響應WM_PAINT消息時利用圖形操作命令進行窗口更新處理:
InvalidateRect(hWnd,&WinRECT,TRUE);//WinRECT為要更新區域
UpdateWindow(hWnd);
窗口初始建立時默認為更新窗口的全部區域,當要更新的矩形區域為NULL時表
示更新窗口所有矩形區域。函數UpdateWindow()通知系統向要更新矩形區域的窗口
發送WM_PAINT消息,窗口函數接收到WM_PAINT消息后首先利用BeginPaint()函數取
得設備描述符表,然后利用圖形命令直接對顯示設備進行更新操作,最后利用
EndPaint()函數通知系統更新操作結束。
更新窗口矩形區域直接使用窗口類中定義的屏幕畫刷,即使利用
SelectObject()函數選擇相應屏幕畫刷也無效,而且更新矩形區域范圍是通過
InvalidateRect()函數累加的,由UpdateWindow()函數通知系統開始進行窗口更新
操作。整個過程是由系統來調度的,因此使用這種方法無法實現窗口的拖動虛框繪
制和實時操作。
直接操作窗口客戶區域的方法是利用GetDC()函數直接取得顯示設備句柄,利
用各種圖形操作命令直接對顯示設備進行繪圖。它使用屏幕當前設置的畫筆和畫刷
來實現各種圖形繪制操作,無須向系統傳遞任何消息應用程序就可以實時地對屏幕
窗口進行更新和繪圖操作。其操作過程是首先取得顯示設備描述符句柄
hDC=GetDC(hWnd),當hWnd 參數為NULL時取得的是整個屏幕的設備描述符表句柄,
然后利用SelectObject()函數設置當前屏幕的畫筆和畫刷,利用各種畫圖函數完成
屏幕的繪圖操作,最后利用ReleaseDC()函數釋放獲取的設備描述表。如果利用畫
矩形函數Rectangle()實現虛框,那么在設置當前屏幕畫筆的同時必須使用
SelectObject(hDC,GetStockObject(NULL_BRUSH))屏蔽掉任何屏幕畫刷,否則
WINDOWS程序會很快吞筮掉所有GDI資源,相當于在屏幕設備資源中增加了無數矩形
區域。
對于窗口拖動框的擦除操作,只需在拖動框繪制函數中將屏幕的圖形畫筆操作
方式設置為R2_XORPEN異或方式,即SetROP2(hDC2,R2_XORPEN)。在拖動框繪制結束
時注意恢復,在窗口拖動框移動到下一個位置前,在原屏幕位置重新調用繪制函數
一次將原來拖動框擦除(見程序1)。
//利用畫矩形函數實現拖動實框(由于篇幅畫線、畫點函數省略)
void DrawMoveRect(int xx1,int yy1,int xx2,int yy2,int xy)
{ HDC hDC;
int oldrop2,m,k;
hDC = GetDC(NULL); //取得全屏幕設備描述句柄
oldrop2= GetROP2(hDC); //取得原來屏幕畫圖方式
SetROP2(hDC,R2_XORPEN); //設置異或屏幕畫圖方式
SelectObject(hDC,GetStockObject(NULL_BRUSH));//屏蔽畫刷
SelectObject(hDC2,GetStockObject(WHITE_PEN));//選擇畫筆
for (k=0;k xx1-=1;xx2+=1;
yy1-=1;yy2+=1;
Rectangle(hDC2,xx1,yy1,xx2,yy2);
}
SetROP2(hDC2,oldrop2); //恢復原來作圖方式
ReleaseDC(NULL,hDC2); //釋放設備描述符表
}
。ǔ绦1)
三、動態拖動窗口“三步曲”
完成了對WINDOWS 高級窗口的客戶區域拖動命令判斷、拖動功能的鼠標光標動
態提示和定制窗口拖動框函數之后,就需要實現整個拖動方案中的拖動過程啟動、
窗口拖動框移動和拖動結束處理的三步曲過程。于是必須在窗口函數中直接處理
WM_LBUTTONDOWN、WM_MOUSEMOVE和WM_LBUTTONUP消息。下面來具體處理上述三個步
驟中的細節問題。
第一步,在窗口函數中對鼠標點擊消息WM_LBUTTONDOWN進行判斷,以處理用戶
通過鼠標光標動態提示功能獲取滿足窗口拖動條件時,按下鼠標左鍵產生的啟動拖
動過程消息。其功能性代碼如下:
case WM_LBUTTONDOWN:
pt = MAKEPOINT(lParam);
if(PtInRect(&DragRT,pt)){
DragBegin((LPRECT)&WinRT,lParam,hWnd,2);//啟動過程
} else {//進行其它處理;}
上述DragBegin()函數為筆者開發的窗口拖動啟動函數。由于一個高級窗口應
用程序中往往存在很多窗口,所以將其作為一個單獨函數處理。其中WinRT為高級
窗口矩形區域,這里作為拖動框矩形區域參數來傳遞,lParam為鼠標光標指針長整
數,hWnd為當前被拖動窗口的句柄,2 為拖動框寬度。這時,需要同時將鼠標控制
權交給當前被拖動窗口、設置拖動窗口標志單元、保存當前鼠標在屏幕上的位置并
顯示被拖動窗口的拖動框。拖動功能啟動函數的源代碼如下:
void DragBegin(LPRECT WinRect, //拖動框的矩形區域
LPARAM lParam, //鼠標光標當前指針
HWND hwnd, //當前窗口句柄
unsigned int kk) //拖動框顯示的寬度
{SetCapture(hwnd); //拖動時窗口必須具有鼠標輸入權
MoveFlag=TRUE; //設置拖動標志
oldmx=LOWORD(lParam);//記錄當前鼠標屏幕坐標X
oldmy=HIWORD(lParam);//記錄當前鼠標屏幕坐標Y
DrawMoveRect(WinRect->left,WinRect->top,//顯示拖動框
WinRect->right,WinRect->bottom,kk);
}
第二步,需要處理鼠標拖動窗口時的拖動框移動過程,這需要在窗口函數中進
行WM_MOUSEMOVE消息處理。拖動框的移動包括上次顯示拖動框的清除和本次拖動框
的顯示兩步。由于拖動框繪制函數中對當前的繪制方式進行了重新設置,異或方式
使得只要重新在原屏幕坐標位置處調用一次該函數即可清除拖動框,因此,在鼠標
拖動窗口移動過程中顯示和清除拖動框只需要調用兩次拖動框繪制函數即可。另外
,拖動框在屏幕上位置的計算方法也非常簡單,就是將當前取得的屏幕位置坐標值
減去保存的前次屏幕位置坐標值所得鼠標移動偏移量,再用原來窗口屏幕左上角坐
標值加上這個偏移量,就可以確定被拖動窗口和拖動框新的屏幕位置坐標值。
case WM_MOUSEMOVE:
if(MoveFlag==TRUE){
DragMove((LPRECT)&WinRT,WinWT,WinHi,lParam,2);
} else {//進行其它處理;}
鑒于高級窗口應用程序一般為多個子窗口,所以將拖動框移動處理過程單獨編
制成函數,并且對鼠標拖動窗口過程中窗口不能完全位于屏幕可見區域內進行了特
殊處理。開發者可根據需要自行調整其位置,以便被拖動的窗口能夠完全被顯示于
屏幕可視區域內。其拖動過程函數源代碼部分如下:
void DragMove(LPRECT rcwin, //拖動框矩形區域
unsigned int wi, //被拖動窗口寬度
unsigned int hi, //被拖動窗口高度
LPARAM lParam, //鼠標位置指針
unsigned int kk) //拖動框邊框寬度
{ DrawMoveRect(rcwin->left,rcwin->top,
rcwin->right,rcwin->bottom,kk);//清除上次畫拖動框
rcwin->left+=LOWORD(lParam)-sImeG.oldmx;//計算窗口
rcwin->top+=HIWORD(lParam)-sImeG.oldmy; //新位置
sImeG.oldmx=LOWORD(lParam); //保存當前坐標值
sImeG.oldmy=HIWORD(lParam);
if (rcwin->left<0) rcwin->left=0;//對窗口超越屏幕
if (rcwin->left>sImeG.xScrWi-wi) //可視區域處理
rcwin->left=sImeG.xScrWi-wi;
ii=sImeG.yScrHi-hi-(sImeG.WinVer<0x35f ? 0:BOTOFF);
if (rcwin->top<0) rcwin->top=0; //對WIN95進行底部
if (rcwin->top>ii) rcwin->top=ii;//特殊保留處理
rcwin->right =rcwin->left+wi-1;
rcwin->bottom=rcwin->top+hi-1;
DrawMoveRect(rcwin->left,rcwin->top,
rcwin->right,rcwin->bottom,kk);//畫新位置拖動框
}
第三步,在鼠標拖動窗口結束時需要進行窗口的實際移動處理,這就需要在處
理WM_LBUTTONUP消息時利用MOVEWINDOW()命令進行實際移動處理。同樣鑒于多窗口
原因仍然需要將這個處理過程單獨形成一個函數,而且在移動窗口前還需要利用繪
制函數清除屏幕上所畫的拖動框。如果窗口未完全位于屏幕的可見位置,還必須進
行適當調整使被拖動的窗口能夠完全位于屏幕可視區內,同時釋放鼠標控制權并清
除拖動窗口標志單元。結束過程的描述性代碼部分如下:
case WM_LBUTTONUP:
if (sImeG.MoveFlag==TRUE){//拖動標志有效
DragEnd((LPRECT)&WinRT,WinWT,WinHI,hWnd);}
拖動結束處理函數的源代碼部分如下:
void DragEnd(LPRECT rcwin,//拖動框矩形區域
unsigned int wi, //被拖動窗口寬度
unsigned int hi, //被拖動窗口高度
unsigned int kk) //拖動框邊框寬度
{ DrawMoveRect(rcwin->left,rcwin->top,
rcwin->right,rcwin->bottom,1); //清除拖動框
if (rcwin->left<0) rcwin->left=0;//對窗口超越屏幕
if (rcwin->left>sImeG.xScrWi-wi) //可視區域處理
rcwin->left=sImeG.xScrWi-wi;
ii=sImeG.yScrHi-hi-(sImeG.WinVer<0x35f ? 0:BOTOFF);
if (rcwin->top<0) rcwin->top=0; //對WIN95進行底部
if (rcwin->top>ii) rcwin->top=ii;//特殊保留處理
rcwin->right =rcwin->left+wi-1;
rcwin->bottom=rcwin->top+hi-1;
MoveWindow(hwnd,rcwin->left,rcwin->top,
wi,hi,TRUE); //將窗口實際移到新位置
sImeG.MoveFlag=FALSE; //清除拖動標志單元
ReleaseCapture(); //釋放鼠標控制權
}
至此,整個窗口的動態拖動過程就完成了。有興趣的讀者可以去
"bobosong.yeah.net"主頁下載推薦的軟件來看看這一功能的實現。