2024年1月18日发(作者:)
详述"无标题栏窗口"移动方法
正常情况下,系统怎样移动窗口:使用者在窗口标题栏(非客户区)内按下鼠标左键时,会发生下列事件:
◆ 系统向该窗口过程发送WM_NCLBUTTONDOWN消息,并最终传递给DefWindowProc函数;
◆ DefWindowProc根据"按下鼠标左键并移动,以及HTCAPTION标识所表示鼠标按下时的位置"等信息,来执行该消息的缺省处理(窗口随鼠标光标一起移动)操作。作为练习,测试一下在窗口过程中设置下列语句:
case WM_NCLBUTTONDOWN:
return 0;
然后,同样是在窗口标题栏内按鼠标左键并移动鼠标,但此时窗口却并不随同鼠标一起移动了,这是怎么回事?这是因为上述语句中有return 0语句的缘故。该语句使WM_NCLBUTTONDOWN消息未能传递给DefWindowProc函数,而在窗口过程中提前返回了,当然移动窗口的操作就无法实现。这也从反面印证了一个事实,那就是,最后完成移动窗口的操作是由DefWindowProc函数来完成的。通过上面分析可勾划出这样一个操作过程:用户在窗口标题栏内按下鼠标左键→系统发送WM_NCLBUTTONDOWN消息→DefWindowProc函数接收消息→用户移动鼠标→DefWindowProc函数执行窗口随同鼠标一起移动的操作。由此得出一个结论:要想实现移动窗口的操作,必须具备两个条件:一是要按下鼠标左键并移动鼠标(DefWindowProc将检测这个条件);二是按下鼠标左键时能发送WM_NCLBUTTONDOWN消息并返回HTCAPTION标识。下面根据以上分析,在没有窗口标题栏的情况下,采取绕过DefWindowProc函数来实现对无标题栏窗口的移动。
1.发送WM_NCLBUTTONDOWN消息
在窗口没有标题栏的情况下,在窗口上按下鼠标左键时,系统不会发送WM_NCLBUTTONDOWN消息,因为鼠标光标是在窗口客户区内被按下的,此时系统发送WM_LBUTTONDOWN消息。通过上面的分析可知道,DefWindowProc函数并不关心WM_NCLBUTTONDOWN消息是谁发出的,而只是关心是否有该消息发出。这样,只要我们在按下鼠标左键事件中,主动将WM_NCLBUTTONDOWN消息发出,不就可同时满足这两个条件吗!下面代码就是根据这个思路来设计的。
case WM_LBUTTONDOWN:
SendMessage(hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0);
break;
发送消息时,通过HTCAPTION参数给DefWindowProc函数带去一个信息,告诉它鼠标左键是在窗口非客户区的标题栏处按下的。当然这是一个假情报,但DefWindowProc函数会信以为真并根据这个信息来执行相应的操作。
2.发送WM_SYSCOMMAND消息
case WM_LBUTTONDOWN:
SendMessage(hWnd,WM_SYSCOMMAND,SC_DRAGMOVE,0);
break;
能使用WM_SYSCOMMAND消息来移动窗口,得益于一个新近扩展的SC_DRAGMOVE标志(从字面上可看出是"拖曳移动"的意思。该标志在低版本的编译器中找不到,所以,引用该标志前应预先声明:
#define SC_DRAGMOVE 0xF012
也可直接使用常量值:
SendMessage(hWnd,WM_SYSCOMMAND,0xF012,0);
上述2种无标题栏窗口移动机理与窗口有标题栏时相似,相同处都是由DefWindowProc函数来完成实际操作;不同处是发送消息的方式不同,一个是由系统隐含发送;另一个则是由应用程序公开发送。采取上述2种方法移动窗口,同有标题栏移动窗口的视觉效果一样,都是先移动一个指示框(一个虚线框),一旦窗体移动后的新位置确定,松开鼠标左键时窗口才被真正移动到虚线框所指向的位置处。那么,能否直接移动窗口而不出现虚线框呢?答案是肯定的。
实际上,操作系统准备了两种移动窗口的方式。一种是有虚线框,另一种则是没有虚线框。只是Windows系统默认的是有虚线框。如果在上述示例代码中再添加下列语句:
SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,TRUE,NULL,0);
即:
case WM_LBUTTONDOWN:
SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,TRUE,NULL,0);
SendMessage(hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0);
break;
这样在移动窗口时,虚线指示框将不会出现。注意,上述语句位置顺序不能错,否则,在移动时虚线框还会出现。不过,系统默认按有虚线指示框的方式来移动窗口是通过权衡利弊的。这是因为有虚线框方式移动窗口,在开始时并未真正地移动窗口,而是用一个线框来指定窗口将要到达的位置,然后一次性将窗口移到指定位置(在指定位置重绘)。也就是说,
1
移动窗口过程中只需重绘一次。如果不用虚线框而是直接移动窗口,窗口移动过程中将会有N次绘制,从而增加系统处理图形的负担,导致窗口绘制质量严重下降,造成不好的视觉效果。为此,编程实践中可这样安排:对异形窗口,为体现异形视觉效果,可使用无虚线框的方式来移动;对一般矩形窗口,可按有虚线框的方式来移动,以确保重绘质量。
诚然,上面代码可使异形窗口无虚线框方式移动,但由于SystemParametersInfo函数是系统级的,对它的调用将会影响电脑桌面上所有程序窗口都会按无虚线框方式移动。如果这样,必将使桌面整体视觉效果大打折扣。如果不想影响其它窗口的移动效果,而只是要求在移动本异形窗口时不出现虚线框的话,则可在窗口过程中再增添下列代码:
case WM_MOUSEMOVE:
SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,false ,NULL,0);
break;
3.自编代码
自编代码同样可实现无标题栏窗口的移动操作,且更加灵活多样。例如,操作可由鼠标左键或右键来完成。下面是通过鼠标右键来完成窗口移动的实际代码,在窗口过程中添加下列代码:
static RECT rt,re;
static POINT pt,pe;
case WM_RBUTTONDOWN:
SetCapture(hwnd); //设置鼠标捕获(防止光标跑出窗口失去鼠标热点)
GetCursorPos(&pt); //获取鼠标光标指针当前位置
GetWindowRect(hwnd,&rt); //获取窗口位置与大小
=; //保存窗口宽度
=; //保存窗口高度
break;
case WM_RBUTTONUP:
ReleaseCapture(); //释放鼠标捕获,恢复正常状态
break;
case WM_MOUSEMOVE:
GetCursorPos(&pe); //获取光标指针的新位置
if(wParam==MK_RBUTTON) //当鼠标右键按下
{
=+(pe.x-pt.x); //窗口新的水平位置
=+(pe.y-pt.y); //窗口新的垂直位置
MoveWindow(hwnd,,,,,true); //移动窗口
}
break;
2


发布评论