磁性窗体

相信大家都用过Winamp,一定会对她那种具有吸附效果的窗口感到很惊喜吧。其实要实现这样的效果并不难,下面我就用bcb做出这样的效果。为了将问题简化,窗口只在屏幕顶端产生吸附效果。

       打开bcb6.0,新建一工程,将Form1的BorderStyle的属性改为bsNone,做成一个无标题栏,无边框的窗体。正常情况下,我们是无法移动这样的窗体的。我们必须自己给窗口发送一个移动的消息,在Form1的MouseDown的事件中添加如下代码:

        ReleaseCapture();

        SendMessage(this->Handle,WM_SYSCOMMAND,SC_MOVE+HTCAPTION,0);

        其实道理很简单,相信大家都会这个,我也就不多说了,:)

        为了窗口在接近屏幕顶端的时候有一种磁性效果,我们必须在窗口的移动之前做一个判断,判断窗口将要到达的新位置的Top值是否小于一定范围(这个范围我称之为磁性距离,大小可由自己定义,为方便起见,我在这里定为20)。但是bcb里面并没有窗体移动事件,我们必须自己在程序里面拦截窗体移动消息,为此,我们要重载WndProc这个函数:

void __fastcall TForm1::WndProc(TMessage& Message)
{
        switch(Message.Msg)
        {
                /*在窗口移动之前,判断窗口新位置的Top值是不是在磁性范
                围内,如果在,让它归0,表示不产生窗体移动,避免在磁性
                范围内移动窗口的时候窗口过于闪烁*/
                case WM_WINDOWPOSCHANGING:
                        if ((LPWINDOWPOS(Message.LParam))->y<20)
                                (LPWINDOWPOS(Message.LParam))->y=0;
                        break;
                default:
                        break;
        }
        TForm::WndProc(Message);  //自己处理完消息后再让系统做剩下的事情
}
        OK,代码就这么多,是不是很简单?运行一下程序,看是不是果真如此呢?:)

       也许有人会问为什么不是拦截WM_MOVE这个消息,其实只要你试一下就知道了,如果拦截这个消息,窗口在磁性范围内移动的时候会很闪烁。原因很简单,WM_WINDOWPOSCHANGING是在窗口移动之前就做出了判断,如果窗口将要在磁性范围内移动(此时尚未移动),则不让窗口移动。而WM_MOVE在窗口移动以后才做判断,这样的结果就是不断让Top复位成0,当然会造成窗口闪烁了。

        另外,大家都知道,根据系统本身的设置不同,窗口在移动的过程中,有两种显示效果,一种是显示虚线框,一种是显示窗口内容。细心的朋友可能已经发现,在Winamp中,不管系统的设置是如何,移动Winamp的窗口的过程中总是会显示窗口内容。其实这样做的道理很简单,因为在显示虚线框的时候,磁性窗口的吸附效果不怎么好,大家可以亲手试试。

        那如何也做成和Winamp一样呢?其实只要一个API函数就搞定了,:),代码如下:

SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,true,NULL,SPIF_SENDCHANGE);

        在窗口移动之前,通过调用上面的函数,就能达到Winamp一样的效果了。关于SystemParametersInfo这个函数及参数的具体用法,请各位参见MSDN,再没有比MSDN解释的更清楚了。

        以上代码在Win2k Professional+bcb6.0下测试通过。

 

       呼,总算写完了,这是我第一次写东西,请大家多多指教,:)

      让板砖来得更猛烈一些吧!

 

 

另外一篇有实现代码的:

一些著名的共享软件不但功能卓著,而且在程序界面的设计技巧上往往领导了一种时尚,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系统下调试通过。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值