无标题栏窗口移动方法种种

首先,看看在正常情况下系统是怎样来移动程序窗口的。
当用户在程序窗口标题栏区域内按下鼠标左键时将会发生下列事情:

  • 系统向该窗口过程函数发送 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); 

上述无标题栏窗口移动窗体的机理与窗口有标题栏时是相似的,相同处最后都是由 DefWindowProc()函数来完成实际的操作;不同处是发送消息的方式不同,一个是由系统隐含发送;另一个则是由程序公开发送。

采取上述两种方法来移动窗口,同有标题栏移动窗口的视觉效果是一样的,那就是在移动时,先移动一个指示框(一个虚线框),等确定好窗体移动后的新位置后,当松开鼠标左键时窗口的实体才被正真移动到虚线框所指向的位置处。那么,能否直接移动窗口实体而不出现虚线框呢?答案是肯定的。

实际上,操作系统准备了两种移动窗口的方式,一种是有虚线框,另一种则是没有虚线框,只是 Windows 系统默认的是有虚线框。不过,如果我们在上述示例代码中再添加下列语句:

SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,true,NULL,0); 

即:

case WM_LBUTTONDOWN:
  SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,true,NULL,0);
  SendMessage(hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0);
  break; 

这样在移动窗口时,虚线指示框将不会出现。注意,上述语句的位置顺序不能错,否则,在移动时虚线框还会出现。

不过,系统默认按有虚线指示框的方式来移动窗口是通过权衡利弊的。这是因为有虚线框方式移动窗口,在开始时并未正真地移动窗口,而是用一个线框来指定窗口将要到达的位置,然后一次 性的将窗口移动到指定的位置(在指定的位置重新绘制)处。也就是说,在移动窗口的过程中,窗口实体只需要重绘一次即可。如果不用虚线框,而是直接移动窗口实体的话,在窗口的移动过程中,将会形成 N 次绘制窗口,增加系统处理图形的负担,而使窗口的绘制质量严重下降,造成不好的视觉效果。

为此,在编程实践中可以这样来安排:对异形窗口为了体现异形视觉效果,可以使用无虚线框的方式来移动窗口;而对于一般矩形窗口就可按有虚线框的方式来移动窗口,以求确保窗口的重绘质量。

诚然, 上面设置的代码可以使异形窗口无虚线框方式移动, 但由于 SystemParametersInfo()函数是系统级的,对它的调用将会影响电脑桌面上所有程序窗口都会按无虚线框方式移动,如果这样的话。必将会使桌面整体的视觉效果大打折扣。如果不想影响其它窗口的移动效果,而只是要求在移动本异形窗口时不出现虚线框的话,则可在窗口过程函数中再增添下列代码即可:

case WM_MOUSEMOVE:
  SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,false ,NULL,0); break;

3、自编代码

自编代码同样可以实现无标题栏窗口的移动操作,而且更加灵活多样。例如,操作可以由鼠标左键或鼠标右键来完成。下面是通过鼠标左键来完成窗口移动的实际代码,在程序窗口的过程函数中添加下列代码语句:

static POINT pt, pe;
static RECT rt, re;

case WM_LBUTTONDOWN:
  SetCapture(hWnd); // 设置鼠标捕获(防止光标跑出窗口失去鼠标热点)
  GetCursorPos(&pt); // 获取鼠标光标指针当前位置
  GetWindowRect(hWnd,&rt); // 获取窗口位置与大小
  re.right=rt.right-rt.left; // 保存窗口宽度
  re.bottom=rt.bottom-rt.top; // 保存窗口高度
  break;
case WM_LBUTTONUP:
  ReleaseCapture(); // 释放鼠标捕获,恢复正常状态
  break;
case WM_MOUSEMOVE:
  GetCursorPos(&pe); // 获取光标指针的新位置
  if(wParam==MK_LBUTTON) // 当鼠标左键按下
  {
    re.left=rt.left+(pe.x - pt.x); // 窗口新的水平位置
    re.top =rt.top+(pe.y - pt.y); // 窗口新的垂直位置
    MoveWindow(hWnd,re.left,re.top,re.right,re.bottom,true); // 移动窗口
  }
  break;

(上述所有代码语句均在 VC++ 6.0 中编译通过)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值