建立磁性窗体

2008-02-23 05:37:14来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

一些著名的共享软件不但功能卓著,而且在程式界面的设计技巧上往往领导了一种时尚,WinAmp就是其中的一个代表。WinAmp有两个绝活,一是能够更换窗体的外观,也就是现在俗称的给软件换“皮肤”;另一个即是磁性窗体技巧。
磁性窗体即若干窗体靠近到一定距离以内时会互相粘在一起,或说相互吸附在一起,然后在拖动主窗体时,粘在其上的其他窗体也一起跟着移动,似乎变成了一个窗体。国内的MP3播放器新秀CDOK也实现了这种技巧,而且更绝,把几个窗体粘在一起后,窗体没有主从之分,拖动其中任意一个窗体都会使其他的窗体一起移动。在CSDN上有关怎样设计磁性窗体的帖子很多,说明这个技巧深得广大程式员的青睐。
本文先把几位网友的方法略加分析,然后给出我认为比较可行的实现方法和源代码。
实现磁性窗体基本上分为两步,第一步是实现当两个窗体靠近到一定距离以内时实现窗体间的粘贴操作,第二步是移动窗体时,同时移动和他粘在一起的其他窗体。

实现窗体的粘贴
实现粘贴的难点在于什么时候进行这个操作,假设有两个窗体Form1和Form2,移动Form2向Form1靠近,当Form2和Form1的最近距离小于distance时粘贴在一起。显然,应该在移动Form2的过程中进行判断,问题是在程式的什么位置插入判断代码呢?
CSDN上有人认为能够使用定时器,每隔一定的时间检查各个窗体的位置。这种方法有着明显的弊病,不说定时器要无谓地浪费系统资源,单单他的实时性就难以确保。假如缩短计时值,浪费的CPU资源就更多了,所以我也就不多说了。
合理的方法是利用系统产生的消息,但是利用什么消息呢?窗体在移动时会产生WM_WINDOWPOSCHANGING和WM_MOVING消息,移动结束后会产生WM_WINDOWPOSCHANGED和WM_MOVE消息。WM_WINDOWPOSCHANGING和WM_WINDOWPOSCHANGED消息的参数lParam是结构WINDOWPOS的指针,WINDOWPOS定义如下:
typedef struct _WINDOWPOS {
HWND hwnd; // 窗口句炳
HWND hwndInsertAfter; // 窗口的Z顺序
int x; // 窗口x坐标
int y; // 窗口的y坐标
int cx; // 窗口的宽度
int cy; // 窗口的高度
UINT flags; // 标志位,根据他设定窗口的位置
} WINDOWPOS;
能够看出,WM_WINDOWPOSCHANGED消息不但仅在窗口移动时产生,而且在他的Z顺序发生变化时也会产生,包括窗口的显示和隐藏。所以我认为这个消息不是最好选择。
WM_MOVING和WM_MOVE消息的参数lParam是个RECT结构指针,和WM_WINDOWPOSCHANGED消息相比较为单纯,我采用的即是这个消息。下面我给出用C Builder写的示例程式。
为了方便程式的阅读,先定义了一个枚举数据类型,表示窗体的粘贴状态。同时定义了一个类,封装了窗体粘贴相关的数据,其中的Enable是为了防止重复进行操作,方法是操作时配置Enable为否,操作结束时恢复为真,而在操作前检查这个标志是否为否,否则直接返回。

图2 窗体的粘贴状态示例

// 窗体粘贴状态,含义见图2
enum enumAttachStyle
{
AS_NONE, // 没有粘贴
AS_TOP,
AS_BOTTOM,
AS_T_TOP,
AS_LEFT,
AS_RIGHT,
AS_L_LEFT
};
// 处理窗体粘贴的类,为了简化,采用了public声明
class CFormAttachStyle
{
public:
bool Enabled; // 防止重复进行粘贴相关的操作
HWND AttachTo; // 被粘贴到哪个窗口
int XStyle; // 左右方向的粘贴状态
int YStyle; // 上下方向的粘贴状态
int xPos; // 粘贴到的x坐标
int yPos; // 粘贴到的y坐标
CFormAttachStyle() // 初使化数据
{
XStyle =AS_NONE;
YStyle =AS_NONE;
Enabled=true;
hAttachTo=NULL;
}
};
函数DistanceIn用于判断两个整数的距离是否在指定范围内:
// 整数i1和i2的差的绝对值小于i3
bool DistanceIn(unsigned int i1,unsigned int i2,unsigned int i3)
{
if(i1>i2)
{ // 确保i2>=i1;
int t=i1;
i1=i2;
i2=t;
}
return i2-i1<=i3;
}
//---------------------------------------------------------------------------
// i1<=i2 bool Mid(unsigned int i1,unsigned int i2,unsigned int i3)
{
return ((i1<=i2) && (i2 }
//---------------------------------------------------------------------------
AttachToForm是处理窗体粘贴的关键函数,假如进行了粘贴,则保存粘贴到的窗体的句柄,并调整窗体的位置。在函数中使用了窗体的Tag属性保存了一个CFormAttachStyle类的实例指针,原因将在稍后进行说明,参数distance表示能够进行粘贴的距离。窗口粘贴在上下、左右各有3种形式,都需要加以判断。
// 把窗体My粘到主窗体上
bool AttachToForm(TForm *My, TForm *Form, RECT *r,int distance)
{
CFormAttachStyle *MyStyle=(CFormAttachStyle *)My->Tag;
if(MyStyle==NULL)return false; // 这个窗体不支持粘贴
//准备粘贴到的窗体的位置
RECT rMain;
GetWindowRect(Form->Handle,&rMain);
MyStyle->AttachTo=NULL;
MyStyle->yPos=r->top;
MyStyle->xPos=r->left;
// 上下方向判断
MyStyle->YStyle=AS_NONE;
if( Mid(rMain.left,r->left,rMain.right)
|| Mid(r->left,rMain.left,r->right)
|| (MyStyle->XStyle!=AS_NONE))
{
if(DistanceIn(r->top,rMain.bottom,space))
{
MyStyle->YStyle=AS_BOTTOM;
MyStyle->yPos=rMain.bottom;
}else if(DistanceIn(r->top,rMain.top,space))
{

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇: 防止一个没有窗体的WINDOWS程式的重复运行

下一篇: C程式研发经典实例之5