零点教学网(www.0djx.com)-学电脑,学软件,学编程,学网站,学设计!

建立磁性窗体

零点教学网 c++教程 2020-07-08 11:49:25 22
一些著名的共享软件不但功能卓著,而且在程序界面的设计技巧上往往领导了一种时尚,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
int t=i1;
i1=i2;
i2=t;
}
return i2-i1
}
//---------------------------------------------------------------------------
// i1 =i2实现窗体的关联移动
与处理窗体粘贴相比,关联窗体的难度小一些。但是从CSDN上的帖子看,采用的方法都单调而且不佳,我都不推荐。
比较直观的方法是使用窗体的MOUSEDOWN、MOUSEMOVE和MOUSEUP事件,先定义一个标志鼠标是否按下的变量:
bool bMouseDown;
在MOUSEDOWN事件中设置:
bMouseDown=true;
在MOUSEUP事件中设置:
bMouseDown=false;
在MOUSEMOVE事件中作如下处理:
if(bMouseDown)
{
// 移动当前窗体
……
// 计算窗体移动的位移
int dx;
int dy;
// 计算出dx和dy
……
// 移动其它粘贴到当前窗体的窗体
……
}
这个方法的最明显的问题有两个:1、鼠标在窗体上的控件上按下时,不能收到窗体的MOUSEDOWN和MOUSEUP事件,如果同时监控各个控件的事件,麻烦是相当大的。2、窗口标题栏的鼠标事件难以正常处理。
其实,同上一段落类似,处理窗体的WM_MOVING事件是比较好的方法。即在WM_MOVING事件中同步移动其它窗体。
移动其它窗体的方法也有多种,有人采用发送消息的方式,具体如下:
// dx和dy是当前窗体移动的距离
// hMove是要移动的窗体
// WM_MOVEFORM是自定义的消息
PostMessage(hMove, WM_MOVEFORM,dx,dy);
被移动的窗体处理WM_MOVEFORM消息时,移动自己到新的位置。
如果是VB、Delphi一类的语言,可以直接设置其Left和Top属性。我采用的方法是使用API函数SetWindowPos,该函数重新设置指定窗口的位置。我的参考代码如下:
// 移动被粘贴在一起的其它窗体
void UnionOtherForm(TForm *My,TForm *Form,int dx,int dy)
{
if(Form==NULL)return;
CFormAttachStyle *My >if(MyStyle)
{
if(MyStyle- Enabled MyStyle- AttachTo==My)
{
MyStyle- Enabled=false;
int X1=Form- Left;
int Y1=Form-
SetWindowPos(Form- Handle,My- Handle,
X1+dx,Y1+dy,Form- Width,Form- Height,
SWP_NOSIZE|SWP_NOACTIVATE);
MyStyle- Enabled=true;
}
}
}
// 移动被粘贴在一起的其它窗体
void AdjuctFormPos(TForm *My,RECT *r)
{
// 调整窗口位置
int dy=r- top-My-
int dx=r- left-My- Left;
My- Top=r-
My- Left=r- left;
// 逐一检查创建的窗体
for(int i=0;iFormCount;i++)
{
TForm *Form=Screen- Forms[i];
if(Form!=My)
{
// 调整被吸附的窗口位置
UnionOtherForm(My,Form,dx,dy);
}
}
}
// 处理WM_MOVE事件
void Do_WM_MOVE(TForm *My,TMessage Msg)
{
// 处理粘贴成功后的位置调整
CFormAttachStyle *My >if(MyStyle MyStyle- Enabled)
{
if(MyStyle- Enabled MyStyle- AttachTo)
{ // 粘贴成功
My- Left=MyStyle- xPos;
My- Top=MyStyle- yPos;
}
}
Msg.Result=0; // 通知系统,消息已经处理
}
在这里有一个C++ Builder编程的技巧,即使用Screen全局对象。如果在初使化需要使用粘贴功能的窗体时,把一个CFormAttachStyle实例的指针赋值给该窗体的Tag窗体,那么除了处理它的WM_MOVING和WM_MOVE事件外,其它的操作都可以省略了。关键的代码如下:
// 注:应把这个函数的声明加到TForm1的类声明中
void __fastcall TForm1::WndProc(TMessage Msg)
{
TForm::WndProc(Msg);
switch(Msg.Msg)
{
case WM_MOVING: // 处理移动事件
{
Do_WM_MOVING(this,Msg);
break;
}
case WM_MOVE: // 处理移动事件
{
Do_WM_MOVE(this,Msg);
break;
}
}
}
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// 建立磁性窗体特性类
CFormAttachStyle *Attach >Tag=(int)AttachStyle;
}
void __fastcall TForm1::FormDestroy(TObject *Sender)
{ // 删除CformAttachStyle实例
CFormAttachStyle *Attach >delete AttachStyle;
}
以下是主窗体处理WM_MOVING消息的代码:
void __fastcall TfmMain::WndProc(TMessage Msg)
{
TForm::WndProc(Msg);
switch(Msg.Msg)
{
case WM_MOVING: // 处理移动事件
{
AdjuctFormPos(this,(RECT *)(Msg.LParam));
break;
}
}
}

 


如果以上内容你喜欢,请持续关注:零点教学网软件编程教程栏目

分享:

本文来自投稿,不代表本人立场,如若转载,请注明出处:http://0djx.com/ruanjianbianchengjiaocheng/cjj/7620.html

  c语言错误提示的详情   建立磁性窗体   零点教学网C++教程   免费下载建立磁性窗体  

(22)
打赏 微信扫一扫
« 上一篇 2020年07月08日 11:49:13
下一篇 » 2020年07月08日 11:49:38

建立磁性窗体_相关内容

免费下载建立磁性窗体_相关内容