利用BCB编写具有"磁性"特征的窗口
黑龙江省五大连池市马明臣
一些著名的共享软件不但功能卓著,而且在程序界面的设计技巧上往往领导了一种时尚,WinAmp就是其中的一个代表。WinAmp有两个绝活,一是可以更换窗体的外观,也就是现在俗称的给软件换“皮肤”;另一个是及时磁性窗体技巧。
那么什么是磁性窗体呢?用过Winamp的用户都知道,Winamp的播放列表或均衡器在被移动的时候,仿佛会受到一股磁力,每当靠近主窗口时就一下子被“吸附”过去,自动沿边对齐。这就是磁性窗体,那么我们如何在自己的程序中实现这种奇妙的特性呢?经过笔者摸索琢磨出了实现这种“磁化”窗口的方法。该法适用于C++ builder的各个版本。下面笔者给大家介绍一下该技术。
实现粘贴的难点在于什么时候进行这个操作,假设有两个窗体Form1和Form2,移动Form2向Form1靠近,当Form2与Form1的最近距离小于一个给定值时粘贴在一起。显然,应该在移动Form2的过程中进行判断,问题是在程序的什么位置插入判断代码呢?
合理的方法是利用系统产生的消息,我们知道窗体在移动时会产生WM_WINDOWPOSCHANGING和WM_MOVING消息,移动结束后会产生WM_WINDOWPOSCHANGED和WM_MOVE消息。我们这里使用WM_WINDOWPOSCHANGING消息来实现这一功能。
下面笔者就把实现磁性窗体的方法介绍如下:
先新建一应用程序项目,把主窗口Form1适当放小些,放一个按钮组件,修改该按钮组件的Caption属性为“创建磁性窗口”双击它并在OnClick事件中写入如下代码:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Form2= new TForm2(Application);
Form2->Show();//显示磁性窗口
}
选择“File”—>“New Form”创建一个新窗口Form2,在Form2上加入一个Edit组件Edit1和一个按钮组件Button1,修改Edit的TEXT属性为50(该值为当Form2靠近Form1时距离为50像素时进行吸附,一般设为20左右即可,这里为了效果明显所以设为50像素),修改按钮的Caption属性为“磁距”,接着在这个新窗口的unit2.h文件中的private:下面加入如下变量定义:
HWND snapwin;//定义Form2吸附到哪一个窗口的句柄
RECT work_area;
bool snapped;//是否吸附标志
bool winprocthing;
int thresh;//距离多远开始吸附
void __fastcall SettingChanged(TMessage &msg);//改变窗口大小时产生
void __fastcall WMWindowPosChanging(TWMWindowPosChanging &msg);//移动窗口时产生
void __fastcall UpdateWorkArea();刷新窗口
接着在public: // User declarations下加入如下消息:
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_WINDOWPOSCHANGING,TWMWindowPosChanging,WMWindowPosChanging);//定义消息当窗口移动时调用WMWindowPosChanging
MESSAGE_HANDLER(WM_SETTINGCHANGE,TMessage,SettingChanged);
END_MESSAGE_MAP(TForm);//当窗口改变大小时调用SettingChanged
在Form2的单元文件unit.cpp中加入如下代码:
void __fastcall TForm2::SettingChanged(TMessage &msg)
{
UpdateWorkArea();
}
//---------------------------------------------------------------------------
void __fastcall TForm2::WMWindowPosChanging(TWMWindowPosChanging &msg)
{
RECT sr;
snapped=false;//设为不吸附
//test window
snapwin = Form1->Handle;//定义Form2吸附到Form1上这里如改为别的软件的窗口句柄还可以吸附到别的软件窗口上
if (snapwin && IsWindowVisible(snapwin))
/*该段得到窗口Form1的当前位置,当Form2移动时该函数判断Form2与Form1在上下左右方向上是否距离小于50,如小于50则自动改变Form2的位置,自动吸附到Form1上*/
{
if (GetWindowRect(snapwin,&sr))//得到Form1的位置
{
if ( (msg.WindowPos->x <= (sr.right+thresh)) &&
(msg.WindowPos->x >= (sr.right-thresh)) ) {
if ((msg.WindowPos->y > sr.top) && (msg.WindowPos->y < sr.bottom)) {
snapped=true;
msg.WindowPos->x = sr.right;
}
}
else if ((msg.WindowPos->x + msg.WindowPos->cx) >= (sr.left-thresh) &&
(msg.WindowPos->x + msg.WindowPos->cx) <= (sr.left+thresh)) {
if ((msg.WindowPos->y > sr.top) && (msg.WindowPos->y < sr.bottom)) {
snapped=true;
msg.WindowPos->x = sr.left-msg.WindowPos->cx;
}
}
if ( (msg.WindowPos->y <= (sr.bottom+thresh)) &&
(msg.WindowPos->y >= (sr.bottom-thresh)) ) {
if ((msg.WindowPos->x > sr.left) && (msg.WindowPos->x < sr.right)) {
snapped=true;
msg.WindowPos->y = sr.bottom;
}
}
else if ((msg.WindowPos->y + msg.WindowPos->cy) <= (sr.top+thresh) &&
(msg.WindowPos->y + msg.WindowPos->cy) >= (sr.top-thresh)) {
if ((msg.WindowPos->x > sr.left) && (msg.WindowPos->x < sr.right)) {
snapped=true;
msg.WindowPos->y = sr.top-msg.WindowPos->cy;
}
}
}
}
//测试屏幕
sr = work_area;
if (abs(msg.WindowPos->x) <= (sr.left+thresh)) {
snapped=true;
msg.WindowPos->x = sr.left;
}
else if ((msg.WindowPos->x + msg.WindowPos->cx) >= (sr.right-thresh) &&
(msg.WindowPos->x + msg.WindowPos->cx) <= (sr.right+thresh)) {
snapped=true;
msg.WindowPos->x = sr.right-msg.WindowPos->cx;
}
if (abs(msg.WindowPos->y) <= (sr.top+thresh)) {
snapped=true;
msg.WindowPos->y = sr.top;
}
else if ((msg.WindowPos->y+msg.WindowPos->cy) >= (sr.bottom-thresh) &&
(msg.WindowPos->y+msg.WindowPos->cy) <= (sr.bottom+thresh)) {
snapped=true;
msg.WindowPos->y = sr.bottom-msg.WindowPos->cy;
}
}
//---------------------------------------------------------------------------
void __fastcall TForm2::UpdateWorkArea()
{
SystemParametersInfo(SPI_GETWORKAREA, 0, &work_area, 0);
}
双击“磁距”按钮加入如下代码:
void __fastcall TForm2::Button1Click(TObject *Sender)
{
thresh = StrToInt(Edit1->Text);//设定磁性窗口距Form1为edit1中的数值时开始吸附
}
在窗体Form2的OnCreate事件中加入如下代码:
void __fastcall TForm2::FormCreate(TObject *Sender)
{
snapped=false;//设置为不吸附
UpdateWorkArea();
thresh = StrToInt(Edit1->Text);
snapwin = Form1->Handle;
}
该程序在c++ builder 5.0、windows98系统下调试通过。