明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

WIN98特殊窗口的動(dòng)態(tài)拖動(dòng)

[摘要]WIN98中常居頂層的無標(biāo)題條窗口是一種特殊的窗口,典型實(shí)例有IME輸入法應(yīng)用程序、UCWIN平臺(tái)、各種浮動(dòng)工具箱、OFFICE桌面工具欄等。   一、命令檢測(cè)與光標(biāo)動(dòng)態(tài)提示   這種窗口拖動(dòng)一般分...
WIN98中常居頂層的無標(biāo)題條窗口是一種特殊的窗口,典型實(shí)例有IME輸入法應(yīng)
用程序、UCWIN平臺(tái)、各種浮動(dòng)工具箱、OFFICE桌面工具欄等。

  一、命令檢測(cè)與光標(biāo)動(dòng)態(tài)提示

  這種窗口拖動(dòng)一般分為兩種:特定客戶命令區(qū)域和非特定客戶命令區(qū)域。特定
客戶命令區(qū)域是指利用"RECT"定義的特定子矩形區(qū)域;非特定客戶命令區(qū)域是指沒
有明確定義的窗口客戶區(qū)域部分,即所有特定客戶命令區(qū)域之外的部分。實(shí)現(xiàn)該功
能的首要問題是如何檢測(cè)和處理特定客戶命令區(qū)域和非特定客戶命令區(qū)域內(nèi)的鼠標(biāo)
命令,以及如何利用鼠標(biāo)光標(biāo)來動(dòng)態(tài)提示用戶何時(shí)可以進(jìn)行窗口的拖動(dòng)操作。

  1、特定客戶命令區(qū)域檢測(cè)鼠標(biāo)命令

  要在窗口中設(shè)置實(shí)現(xiàn)拖動(dòng)功能的圖標(biāo)命令按鈕,就必須在資源文件中定義命令
按鈕的特定客戶區(qū)域,該區(qū)域一般也就是命令按鈕圖標(biāo)的矩形區(qū)域,這個(gè)區(qū)域的定
義方法為"RECT DragRT",其中DragRT為定義的檢測(cè)鼠標(biāo)命令的矩形區(qū)域,它用
DragRT.LEFT、DragRT.TOP、DragRT.RIGHT和DragRT.BOTTOM四個(gè)參數(shù)來描述矩形區(qū)
域相對(duì)于窗口客戶區(qū)域左上角的相對(duì)坐標(biāo)值。也可用"SETRECT"函數(shù)填充。

  窗口函數(shù)在處理鼠標(biāo)消息WM_LBUTTONDOWN時(shí),當(dāng)收到系統(tǒng)傳遞的鼠標(biāo)位置參數(shù)
lParam后,通過MAKEPOINT()函數(shù)將其轉(zhuǎn)換為窗口坐標(biāo)值,然后利用判斷某坐標(biāo)點(diǎn)
是否位于特定矩形區(qū)域內(nèi)的函數(shù)PtInRect(),判斷出鼠標(biāo)指針是否點(diǎn)擊在拖動(dòng)命令
按鈕之內(nèi)。

  2、非特定客戶命令區(qū)域檢測(cè)鼠標(biāo)命令

  當(dāng)窗口應(yīng)用程序中采取了非特定客戶命令區(qū)域拖動(dòng)方法時(shí),必須在資源文件中
事先確定各個(gè)特定客戶命令區(qū)域的矩形坐標(biāo),這時(shí)非特定客戶命令區(qū)域是不規(guī)則的
區(qū)域,它需要根據(jù)實(shí)際的應(yīng)用程序窗口及各個(gè)命令按鈕矩形區(qū)域來確定,也就是各
個(gè)命令按鈕矩形區(qū)域相對(duì)于窗口矩形區(qū)域的“非”子集。窗口函數(shù)在處理鼠標(biāo)消息
WM_LBUTTONDOWN時(shí),首先利用函數(shù)PtInRect()判斷當(dāng)前鼠標(biāo)指針是否點(diǎn)擊在各個(gè)命
令按鈕矩形區(qū)域內(nèi),如果未點(diǎn)擊在任何命令按鈕區(qū)域內(nèi),則可確定鼠標(biāo)點(diǎn)擊在非特
定客戶命令區(qū)域內(nèi),從而啟動(dòng)窗口拖動(dòng)功能。

  3、窗口拖動(dòng)功能的光標(biāo)動(dòng)態(tài)提示

  實(shí)現(xiàn)鼠標(biāo)光標(biāo)動(dòng)態(tài)提示功能前需要定制鼠標(biāo)光標(biāo)形狀,窗口拖動(dòng)功能的動(dòng)態(tài)提
示光標(biāo)形狀一般為四箭頭圖案,這可以利用微軟公司的SDK、FPT3.0和VC++4.1等高
級(jí)開發(fā)軟件中的資源編輯器"IMAGE EDIT"等來實(shí)現(xiàn)。光標(biāo)資源文件一般為32X32的2
色或16色.CUR圖形文件。建立起自己的鼠標(biāo)光標(biāo)資源文件后,首先需要在應(yīng)用程序
的資源文件中定義鼠標(biāo)光標(biāo),然后在應(yīng)用程序中利用LoadCursor()調(diào)入,并在類中
進(jìn)行設(shè)置:wc.hCursor=hCurm。


  當(dāng)鼠標(biāo)光標(biāo)需要在窗口的特定客戶命令按鈕區(qū)域內(nèi)或非特定客戶命令區(qū)域內(nèi)進(jìn)
行動(dòng)態(tài)提示時(shí),不能使用上述定義方法,而需要在窗口函數(shù)處理WM_MOUSEMOVE消息
時(shí)進(jìn)行特殊處理:首先判斷鼠標(biāo)光標(biāo)指針的當(dāng)前位置是否在拖動(dòng)命令按鈕或非特定
客戶命令區(qū)域內(nèi),如果鼠標(biāo)指針位置滿足拖動(dòng)窗口功能區(qū)域的要求,則利用API函
數(shù)SETCURSOR()改變鼠標(biāo)光標(biāo)圖案,提示用戶此時(shí)可以進(jìn)行窗口拖動(dòng)操作,并將鼠
標(biāo)輸入控制權(quán)交給當(dāng)前窗口,同時(shí)設(shè)置改變后的鼠標(biāo)光標(biāo)標(biāo)志;當(dāng)鼠標(biāo)指針移出拖
動(dòng)窗口命令啟動(dòng)區(qū)域時(shí),恢復(fù)原來鼠標(biāo)光標(biāo)圖案同時(shí)釋放鼠標(biāo)輸入焦點(diǎn)控制權(quán),并
清除鼠標(biāo)光標(biāo)動(dòng)態(tài)提示標(biāo)志單元。

  二、動(dòng)態(tài)拖動(dòng)框的定制

  窗口拖動(dòng)前的關(guān)鍵問題是鼠標(biāo)拖動(dòng)窗口過程中的拖動(dòng)框顯示與擦除功能實(shí)現(xiàn)。
窗口拖動(dòng)虛框就是在WINDOWS 整個(gè)屏幕區(qū)域內(nèi)顯示被拖動(dòng)窗口大小的線框,它的大
小需要根據(jù)被拖動(dòng)窗口的矩形區(qū)域大小和實(shí)際需要來具體確定,一般為被拖動(dòng)窗口
的矩形區(qū)域大小。

  WINDOWS系統(tǒng)中的繪圖方法是通過顯示設(shè)備描述表實(shí)現(xiàn)的,繪圖操作需要占用
一定的GDI資源。WINDOWS 95中的GDI資源要比WINDOWS3.X中的GDI資源大得多,所
以系統(tǒng)可以為窗口、菜單、對(duì)話框、字體和各種繪圖函數(shù)分配足夠的GDI資源。
WINDOWS中有兩種使用顯示設(shè)備描述符表的方法:更新窗口顯示客戶區(qū)域和直接操
作窗口顯示客戶區(qū)域。更新窗口顯示客戶區(qū)域是直接針對(duì)應(yīng)用程序窗口矩形區(qū)域而
言的,在窗口函數(shù)響應(yīng)WM_PAINT消息時(shí)利用圖形操作命令進(jìn)行窗口更新處理:
  InvalidateRect(hWnd,&WinRECT,TRUE);//WinRECT為要更新區(qū)域

  UpdateWindow(hWnd);
  窗口初始建立時(shí)默認(rèn)為更新窗口的全部區(qū)域,當(dāng)要更新的矩形區(qū)域?yàn)镹ULL時(shí)表
示更新窗口所有矩形區(qū)域。函數(shù)UpdateWindow()通知系統(tǒng)向要更新矩形區(qū)域的窗口
發(fā)送WM_PAINT消息,窗口函數(shù)接收到WM_PAINT消息后首先利用BeginPaint()函數(shù)取
得設(shè)備描述符表,然后利用圖形命令直接對(duì)顯示設(shè)備進(jìn)行更新操作,最后利用
EndPaint()函數(shù)通知系統(tǒng)更新操作結(jié)束。

  更新窗口矩形區(qū)域直接使用窗口類中定義的屏幕畫刷,即使利用
SelectObject()函數(shù)選擇相應(yīng)屏幕畫刷也無效,而且更新矩形區(qū)域范圍是通過
InvalidateRect()函數(shù)累加的,由UpdateWindow()函數(shù)通知系統(tǒng)開始進(jìn)行窗口更新
操作。整個(gè)過程是由系統(tǒng)來調(diào)度的,因此使用這種方法無法實(shí)現(xiàn)窗口的拖動(dòng)虛框繪
制和實(shí)時(shí)操作。

  直接操作窗口客戶區(qū)域的方法是利用GetDC()函數(shù)直接取得顯示設(shè)備句柄,利
用各種圖形操作命令直接對(duì)顯示設(shè)備進(jìn)行繪圖。它使用屏幕當(dāng)前設(shè)置的畫筆和畫刷
來實(shí)現(xiàn)各種圖形繪制操作,無須向系統(tǒng)傳遞任何消息應(yīng)用程序就可以實(shí)時(shí)地對(duì)屏幕
窗口進(jìn)行更新和繪圖操作。其操作過程是首先取得顯示設(shè)備描述符句柄
hDC=GetDC(hWnd),當(dāng)hWnd 參數(shù)為NULL時(shí)取得的是整個(gè)屏幕的設(shè)備描述符表句柄,
然后利用SelectObject()函數(shù)設(shè)置當(dāng)前屏幕的畫筆和畫刷,利用各種畫圖函數(shù)完成
屏幕的繪圖操作,最后利用ReleaseDC()函數(shù)釋放獲取的設(shè)備描述表。如果利用畫
矩形函數(shù)Rectangle()實(shí)現(xiàn)虛框,那么在設(shè)置當(dāng)前屏幕畫筆的同時(shí)必須使用
SelectObject(hDC,GetStockObject(NULL_BRUSH))屏蔽掉任何屏幕畫刷,否則
WINDOWS程序會(huì)很快吞筮掉所有GDI資源,相當(dāng)于在屏幕設(shè)備資源中增加了無數(shù)矩形
區(qū)域。

  對(duì)于窗口拖動(dòng)框的擦除操作,只需在拖動(dòng)框繪制函數(shù)中將屏幕的圖形畫筆操作
方式設(shè)置為R2_XORPEN異或方式,即SetROP2(hDC2,R2_XORPEN)。在拖動(dòng)框繪制結(jié)束
時(shí)注意恢復(fù),在窗口拖動(dòng)框移動(dòng)到下一個(gè)位置前,在原屏幕位置重新調(diào)用繪制函數(shù)
一次將原來拖動(dòng)框擦除(見程序1)。

  //利用畫矩形函數(shù)實(shí)現(xiàn)拖動(dòng)實(shí)框(由于篇幅畫線、畫點(diǎn)函數(shù)省略)

  void DrawMoveRect(int xx1,int yy1,int xx2,int yy2,int xy)

  { HDC hDC;

   int oldrop2,m,k;

   hDC = GetDC(NULL); //取得全屏幕設(shè)備描述句柄

   oldrop2= GetROP2(hDC); //取得原來屏幕畫圖方式

   SetROP2(hDC,R2_XORPEN); //設(shè)置異或屏幕畫圖方式

   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); //恢復(fù)原來作圖方式

   ReleaseDC(NULL,hDC2); //釋放設(shè)備描述符表

  }

 。ǔ绦1)

  三、動(dòng)態(tài)拖動(dòng)窗口“三步曲”

  完成了對(duì)WINDOWS 高級(jí)窗口的客戶區(qū)域拖動(dòng)命令判斷、拖動(dòng)功能的鼠標(biāo)光標(biāo)動(dòng)
態(tài)提示和定制窗口拖動(dòng)框函數(shù)之后,就需要實(shí)現(xiàn)整個(gè)拖動(dòng)方案中的拖動(dòng)過程啟動(dòng)、
窗口拖動(dòng)框移動(dòng)和拖動(dòng)結(jié)束處理的三步曲過程。于是必須在窗口函數(shù)中直接處理
WM_LBUTTONDOWN、WM_MOUSEMOVE和WM_LBUTTONUP消息。下面來具體處理上述三個(gè)步
驟中的細(xì)節(jié)問題。

  第一步,在窗口函數(shù)中對(duì)鼠標(biāo)點(diǎn)擊消息WM_LBUTTONDOWN進(jìn)行判斷,以處理用戶
通過鼠標(biāo)光標(biāo)動(dòng)態(tài)提示功能獲取滿足窗口拖動(dòng)條件時(shí),按下鼠標(biāo)左鍵產(chǎn)生的啟動(dòng)拖
動(dòng)過程消息。其功能性代碼如下:

  case WM_LBUTTONDOWN:

   pt = MAKEPOINT(lParam);

   if(PtInRect(&DragRT,pt)){

   DragBegin((LPRECT)&WinRT,lParam,hWnd,2);//啟動(dòng)過程

  } else {//進(jìn)行其它處理;}

  上述DragBegin()函數(shù)為筆者開發(fā)的窗口拖動(dòng)啟動(dòng)函數(shù)。由于一個(gè)高級(jí)窗口應(yīng)
用程序中往往存在很多窗口,所以將其作為一個(gè)單獨(dú)函數(shù)處理。其中WinRT為高級(jí)
窗口矩形區(qū)域,這里作為拖動(dòng)框矩形區(qū)域參數(shù)來傳遞,lParam為鼠標(biāo)光標(biāo)指針長整
數(shù),hWnd為當(dāng)前被拖動(dòng)窗口的句柄,2 為拖動(dòng)框?qū)挾取_@時(shí),需要同時(shí)將鼠標(biāo)控制
權(quán)交給當(dāng)前被拖動(dòng)窗口、設(shè)置拖動(dòng)窗口標(biāo)志單元、保存當(dāng)前鼠標(biāo)在屏幕上的位置并
顯示被拖動(dòng)窗口的拖動(dòng)框。拖動(dòng)功能啟動(dòng)函數(shù)的源代碼如下:

  void DragBegin(LPRECT WinRect, //拖動(dòng)框的矩形區(qū)域

   LPARAM lParam, //鼠標(biāo)光標(biāo)當(dāng)前指針

   HWND hwnd, //當(dāng)前窗口句柄

   unsigned int kk) //拖動(dòng)框顯示的寬度

  {SetCapture(hwnd); //拖動(dòng)時(shí)窗口必須具有鼠標(biāo)輸入權(quán)

   MoveFlag=TRUE; //設(shè)置拖動(dòng)標(biāo)志

   oldmx=LOWORD(lParam);//記錄當(dāng)前鼠標(biāo)屏幕坐標(biāo)X

   oldmy=HIWORD(lParam);//記錄當(dāng)前鼠標(biāo)屏幕坐標(biāo)Y

   DrawMoveRect(WinRect->left,WinRect->top,//顯示拖動(dòng)框

   WinRect->right,WinRect->bottom,kk);

  }

  第二步,需要處理鼠標(biāo)拖動(dòng)窗口時(shí)的拖動(dòng)框移動(dòng)過程,這需要在窗口函數(shù)中進(jìn)
行WM_MOUSEMOVE消息處理。拖動(dòng)框的移動(dòng)包括上次顯示拖動(dòng)框的清除和本次拖動(dòng)框
的顯示兩步。由于拖動(dòng)框繪制函數(shù)中對(duì)當(dāng)前的繪制方式進(jìn)行了重新設(shè)置,異或方式
使得只要重新在原屏幕坐標(biāo)位置處調(diào)用一次該函數(shù)即可清除拖動(dòng)框,因此,在鼠標(biāo)
拖動(dòng)窗口移動(dòng)過程中顯示和清除拖動(dòng)框只需要調(diào)用兩次拖動(dòng)框繪制函數(shù)即可。另外
,拖動(dòng)框在屏幕上位置的計(jì)算方法也非常簡單,就是將當(dāng)前取得的屏幕位置坐標(biāo)值
減去保存的前次屏幕位置坐標(biāo)值所得鼠標(biāo)移動(dòng)偏移量,再用原來窗口屏幕左上角坐
標(biāo)值加上這個(gè)偏移量,就可以確定被拖動(dòng)窗口和拖動(dòng)框新的屏幕位置坐標(biāo)值。

  case WM_MOUSEMOVE:

   if(MoveFlag==TRUE){

   DragMove((LPRECT)&WinRT,WinWT,WinHi,lParam,2);

   } else {//進(jìn)行其它處理;}

  鑒于高級(jí)窗口應(yīng)用程序一般為多個(gè)子窗口,所以將拖動(dòng)框移動(dòng)處理過程單獨(dú)編
制成函數(shù),并且對(duì)鼠標(biāo)拖動(dòng)窗口過程中窗口不能完全位于屏幕可見區(qū)域內(nèi)進(jìn)行了特
殊處理。開發(fā)者可根據(jù)需要自行調(diào)整其位置,以便被拖動(dòng)的窗口能夠完全被顯示于
屏幕可視區(qū)域內(nèi)。其拖動(dòng)過程函數(shù)源代碼部分如下:

   void DragMove(LPRECT rcwin, //拖動(dòng)框矩形區(qū)域

   unsigned int wi, //被拖動(dòng)窗口寬度

   unsigned int hi, //被拖動(dòng)窗口高度

   LPARAM lParam, //鼠標(biāo)位置指針

   unsigned int kk) //拖動(dòng)框邊框?qū)挾?

   { DrawMoveRect(rcwin->left,rcwin->top,

   rcwin->right,rcwin->bottom,kk);//清除上次畫拖動(dòng)框

   rcwin->left+=LOWORD(lParam)-sImeG.oldmx;//計(jì)算窗口

   rcwin->top+=HIWORD(lParam)-sImeG.oldmy; //新位置

   sImeG.oldmx=LOWORD(lParam); //保存當(dāng)前坐標(biāo)值

   sImeG.oldmy=HIWORD(lParam);

   if (rcwin->left<0) rcwin->left=0;//對(duì)窗口超越屏幕

   if (rcwin->left>sImeG.xScrWi-wi) //可視區(qū)域處理

   rcwin->left=sImeG.xScrWi-wi;

   ii=sImeG.yScrHi-hi-(sImeG.WinVer<0x35f ? 0:BOTOFF);

   if (rcwin->top<0) rcwin->top=0; //對(duì)WIN95進(jìn)行底部

   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);//畫新位置拖動(dòng)框

   }

  第三步,在鼠標(biāo)拖動(dòng)窗口結(jié)束時(shí)需要進(jìn)行窗口的實(shí)際移動(dòng)處理,這就需要在處
理WM_LBUTTONUP消息時(shí)利用MOVEWINDOW()命令進(jìn)行實(shí)際移動(dòng)處理。同樣鑒于多窗口
原因仍然需要將這個(gè)處理過程單獨(dú)形成一個(gè)函數(shù),而且在移動(dòng)窗口前還需要利用繪
制函數(shù)清除屏幕上所畫的拖動(dòng)框。如果窗口未完全位于屏幕的可見位置,還必須進(jìn)
行適當(dāng)調(diào)整使被拖動(dòng)的窗口能夠完全位于屏幕可視區(qū)內(nèi),同時(shí)釋放鼠標(biāo)控制權(quán)并清
除拖動(dòng)窗口標(biāo)志單元。結(jié)束過程的描述性代碼部分如下:

  case WM_LBUTTONUP:

   if (sImeG.MoveFlag==TRUE){//拖動(dòng)標(biāo)志有效

   DragEnd((LPRECT)&WinRT,WinWT,WinHI,hWnd);}

  拖動(dòng)結(jié)束處理函數(shù)的源代碼部分如下:

   void DragEnd(LPRECT rcwin,//拖動(dòng)框矩形區(qū)域

   unsigned int wi, //被拖動(dòng)窗口寬度

   unsigned int hi, //被拖動(dòng)窗口高度

   unsigned int kk) //拖動(dòng)框邊框?qū)挾?

   { DrawMoveRect(rcwin->left,rcwin->top,

   rcwin->right,rcwin->bottom,1); //清除拖動(dòng)框

   if (rcwin->left<0) rcwin->left=0;//對(duì)窗口超越屏幕

   if (rcwin->left>sImeG.xScrWi-wi) //可視區(qū)域處理

   rcwin->left=sImeG.xScrWi-wi;

   ii=sImeG.yScrHi-hi-(sImeG.WinVer<0x35f ? 0:BOTOFF);

   if (rcwin->top<0) rcwin->top=0; //對(duì)WIN95進(jìn)行底部

   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); //將窗口實(shí)際移到新位置

   sImeG.MoveFlag=FALSE; //清除拖動(dòng)標(biāo)志單元

   ReleaseCapture(); //釋放鼠標(biāo)控制權(quán)

   }

  至此,整個(gè)窗口的動(dòng)態(tài)拖動(dòng)過程就完成了。有興趣的讀者可以去
"bobosong.yeah.net"主頁下載推薦的軟件來看看這一功能的實(shí)現(xiàn)。