Delphi 下 QQ 窗体自动隐藏探索【一】 窗体自动隐藏探索【
一、问题的提出
熟悉 QQ 使用的朋友都知道,当 QQ 窗体区域超出屏幕四边时,窗体就会自动“消失”, 只留下窗体一边的小部分显露在桌面上。 当用鼠标移动到显露部分之上, 窗体就会在隐藏位 置重新完整显示;但当鼠标离开窗体区域后,窗体便会重新进入隐藏状态。
对隐藏的全过程进行分析,能得出两点推测:第一,窗体隐藏的处理是和窗体移动过程 有关;第二,窗体隐藏的触发条件。
对第一点推测, 能通过对窗体移动时产生的视窗系统消息进行拦截处理加以实现。 对第 二点推测,怎么去表示“窗体区域已超出屏幕可视范围”这一条件为实现的关键。
二、基本的分析
让我们先留意一下视窗系统环境下窗体移动的过程和效果。当使用鼠标移动窗体的时 候,窗体本身并没有即时随鼠标的移动而发生位置的改动;相反,鼠标正在拖动的是个大小 和窗体一致的透明区域 (确切的说一个虚线边框的矩形) 如图一所示。 , 当鼠标释放矩形后, 窗体本身才会在矩形最后停留的地方出现, 从而完成整个移动的过程, 如图二所示。 (注意: 在视窗系统 2000 及 XP 环境下,如果在显示属性中选中“拖动时显示窗体内容”的显示效 果选项,则上述过程无法观察。)
图一 窗体移动过程
注意鼠标拖动的是个矩形
图二 窗体移动过程 拖动结束
窗体出目前矩形最后停留位置
对 QQ 窗体,其移动过程和上述无异,但却有一处不同。当我们把矩形移动到屏幕四边 且已有部分超出时, 矩形就会自动地停留在超出位置上并完整显示。 此时不论我们怎样试图 把矩形再向超出方向上移动,矩形也只保持在该位置,如图三所示。当释放鼠标之后,窗体 的隐藏效果也就出现了,如图四所示。
图三 QQ 窗体移动过程
矩形保持位置而不向超出方向移动
图四 QQ 窗体移动过程 窗体在矩形保持位置处实现隐藏
从上述过程能推断,触发隐藏条件后,即使仍处于移动过程但矩形本身却已被锁定,因 此对窗体位置的判断是发生在移动过程中,也就是说我们要拦截处理的视窗系统消息是 WM_MOVING。其次,在移动过程中首先发生位置变化的是矩形而不是窗体本身,因此实现隐 藏的关键是对矩形参数的判断和设置。
我们能先留意一下 WM_MOVING 消息的语法结构:
WM_MOVING WPARAM wParam LPARAM lParam,
其中,WPARAM 不被使用,而 LPARAM 则是个指针,所指向的是个 RECT 结构。RECT 结构 中包含了 Left、Top、Right、Bottom 四个参数,分别用于描述矩形的左上角和右下角,“该 RECT 记录了窗体相对于屏幕的当前位置;当要改动拖动
矩形的位置时,程式本身必须改动 RECT 结构中各成员变量的相关值”。由此可知,我们要处理的矩形其实已在 WM_MOVING 消 息中被提到,我们要处理的也就是 LPARAM 所指向的 RECT 结构的有关参数。
接下来我们要设置一个由隐藏条件激活的计时器, 目的是监视鼠标相对窗体的位置。 因 为窗体隐藏后的隐现是靠鼠标激活的, 所以若检测到鼠标位于窗体之上, 则说明窗体在显示 状态;反之,窗体在隐藏状态。我们只需在相关的判断下加入对窗体 Top 和 Left 属性的赋 值即可实现隐现效果。
至此,有关自动隐藏效果的实现分析就基本完成了。不过还要注意一点,因为我们是在 WM_MOVING 消息的拦截处理中判断隐藏条件,而通过计时器的 OnTimer 事件处理隐现效果。 在此隐藏条件是否满足在两个过程中的传递将成为关键。 同时我们要知道的不仅是隐藏条件 是否满足,还必须知道窗体是在屏幕的那一边上发生隐藏。为此,我们需要定义一个集合去 描述窗体隐藏的位置,例如:
type
HidePosKind = (hpTop,hpLeft,hpBottom,hpRight); type THidePos = set of HidePosKind;
不过,类似的集合在
Delphi 本身就已存在,譬如 TAnchors 集合。TAnchors 集合原来 是用