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() //释放鼠标输入控制权