无标题栏窗口移动方法种种
首先,看看在正常情况下系统是怎样来移动程序窗口的。当使用者在程序窗口标题栏区域(非工作区)内,按下鼠标左键时将会发生下列事情:
系统向该窗口过程函数发送WM_NCLBUTTONDOWN消息。
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风格标志,该标志从字面上就能看出是"拖曳移动"的意思。该标志在低版本的编译器(VC 6.0中就没有)中是不到的,所以在引用该标志时应预先声明:
#define        SC_DRAGMOVE  0xF012
也可以直接使用其常量值:
SendMessage(hWnd,WM_SYSCOMMAND,0xF012,0);
上述无标题栏窗口移动窗体的机理与窗口有标题栏时是相似的,相同处最后都是由DefWin
dowProc()函数来完成实际的操作;不同处是发送消息的方式不同,一个是由系统隐含发送;另一个则是由程序公开发送。
采取上述两种方法来移动窗口,同有标题栏移动窗口的视觉效果是一样的,那就是在移动时,先移动一个指示框(一个虚线框),等确定好窗体移动后的新位置后,当松开鼠标左键时窗口的实体才被正真移动到虚线框所指向的位置处。那么,能否直接移动窗口实体而不出现虚线框呢?答案是肯定的。
实际上,操作系统准备了两种移动窗口的方式,一种是有虚线框,另一种则是没有虚线框,只是Windows系统默认的是有虚线框。不过,如果我们在上述示例代码中再添加下列语句:
SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,true,NULL,0);
即:
case WM_LBUTTONDOWN:
      SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,true,NULL,0);
            SendMessage(hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0);mousemove是什么键
            break;
这样在移动窗口时,虚线指示框将不会出现。注意,上述语句的位置顺序不能错,否则,在移动时虚线框还会出现。
不过,系统默认按有虚线指示框的方式来移动窗口是通过权衡利弊的。这是因为有虚线框方式移动窗口,在开始时并未正真地移动窗口,而是用一个线框来指定窗口将要到达的位置,然后一次性的将窗口移动到指定的位置(在指定的位置重新绘制)处。也就是说,在移动窗口的过程中,窗口实体只需要重绘一次即可。如果不用虚线框,而是直接移动窗口实体的话,在窗口的移动过程中,将会形成N次绘制窗口,增加系统处理图形的负担,而使窗口的绘制质量严重下降,造成不好的视觉效果。
为此,在编程实践中可以这样来安排:对异形窗口为了体现异形视觉效果,可以使用无虚线框的方式来移动窗口;而对于一般矩形窗口就可按有虚线框的方式来移动窗口,以求确保窗口的重绘质量。
诚然,上面设置的代码可以使异形窗口无虚线框方式移动,但由于SystemParametersInfo()函数是系统级的,对它的调用将会影响电脑桌面上所有程序窗口都会按无虚线框方式移动,如果这样的话。必将会使桌面整体的视觉效果大打折扣。如果不想影响其它窗口的移动效果,而只是要求在移动本异形窗口时不出现虚线框的话,则可在窗口过程函数中再增添下列代码即可:
    case WM_MOUSEMOVE:
            SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,false ,NULL,0);
            break;
3、自编代码
自编代码同样可以实现无标题栏窗口的移动操作,而且更加灵活多样。例如,操作可以由鼠标左键或鼠标右键来完成。下面是通过鼠标右键来完成窗口移动的实际代码,在程序窗口的过程函数中添加下列代码语句:
static POINT pt, pe;
static RECT rt, re;
case WM_RBUTTONDOWN:
    SetCapture(hWnd);                    // 设置鼠标捕获(防止光标跑出窗口失去鼠标热点)
    GetCursorPos(&pt);                    // 获取鼠标光标指针当前位置
    GetWindowRect(hWnd,&rt);    // 获取窗口位置与大小
    re.right=rt.right-rt.left;            // 保存窗口宽度
    re.bottom=p; // 保存窗口高度
    break;
case WM_RBUTTONUP:
    ReleaseCapture();                    // 释放鼠标捕获,恢复正常状态
    break;
case WM_MOUSEMOVE:
    GetCursorPos(&pe);                        // 获取光标指针的新位置
    if(wParam==MK_RBUTTON)            // 当鼠标右键按下
    {
        re.left=rt.left+(pe.x - pt.x);    // 窗口新的水平位置
        re.top =rt.top+(pe.y - pt.y); // 窗口新的垂直位置
        MoveWindow(hWnd,re.p,re.right,re.bottom,true); // 移动窗口
    }
    break;
(上述所有代码语句均在VC++ 6.0中编译通过)
4、用易语言实现
在易语言中移动无标题栏窗口时有虚线框的方法很简单,只需在编译前在窗口的属性栏中,将
“随意移动”属性设置为“真”即可。不过,此时系统拦截了窗口的鼠标左键事件,因此就不能再利用窗口鼠标左键按下事件来进行其它的操作。
而对于移动时没有虚线框出现的方法,则需要通过另外编程来实现。具体见下面的示例程序:
1、【_启动窗口】
只需要一个窗口组件。将窗口的【边框】属性设置为无边框。
2、【窗口程序集1】
1、定义几个全局变量。
标识, 逻辑型 // 用于判断鼠标左键状况
窗口_上边, 整数型 // 窗口位置 x坐标
窗口_顶边, 整数型 // 窗口位置 y坐标
鼠标_x, 整数型 // 单击鼠标左键时鼠标 x 坐标
鼠标_y, 整数型 // 单击鼠标左键时鼠标 y 坐标
2、__启动窗口_创建完毕
标识 = 假
3、__启动窗口_鼠标左键被按下
标识 = 真
捕获鼠标 (取鼠标所在窗口句柄 ())
鼠标_x = 取鼠标水平位置 ()
鼠标_y = 取鼠标垂直位置 ()
窗口_上边 = _启动窗口.左边
窗口_顶边 = _启动窗口.顶边
其中,捕获鼠标()方法,在【应用接口支持库】-【窗口操作】中可以到。其作用是将鼠标捕获设置到指定的窗口。这样在鼠标按钮按下的时候,整个系统所发生的鼠标事件,都将被该窗口接收到。调用该方法,可以防止在移动窗口时,当鼠标偶尔跑出被拖动的窗口时,不至于鼠标事件被其它的窗口捕获去,从而使得移动窗口的操作失败。因此,在这里该方法是必须要设置的。
4、__启动窗口_鼠标左键被放开
标识 = 假
释放鼠标 ()
由于在“__启动窗口_鼠标左键被按下”子程序中调用了“捕获鼠标()”方法,为了能使对鼠标事件的捕获恢复到正常状态,这里必须调用“释放鼠标 ()”方法。同样,“释放鼠标()”方法可以在【应用接口支持库】-【窗口操作】中到。
5、__启动窗口_鼠标位置被移动