粘粘窗口-如何在桌面边缘停靠你的 Delphi 窗口

本文介绍如何通过处理WM_WINDOWPOSCHANGING消息使Delphi窗口在接近屏幕边缘时吸附。利用WindowPos结构获取窗口位置信息,并根据设定的距离调整窗口位置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
本文将向您展示如何通过编写数个消息处理函数来实现当把窗口移动到屏幕边缘的时"snap"的效果.

当然,Win API中没有任何内建的功能允许窗口在屏幕边缘停靠 - 我们必须自己处理 Windows 消息. 正如我们已经知道的, Delphi 能让消息处理非常容易的通过它的事件; 事件通常在应答某个发送给应用程序的 Windows 消息时产生.
 
即使许多 Windows 消息都被 Delphi 事件处理了, 但是仍然有一些消息忽略了. 例如, 我们知道, 如果一个窗口被 Resized 的话, 我们可以得到 Resize 事件 (OnResize) - Delphi 处理了 WM_SIZE 消息, 但是我们如何知道窗口被移动了呢? Delphi 窗体能获知该消息, 但是却并没有对它做任何处理.
 
用户在移动窗口的时候, WM_MOVING 消息将被发送到目标窗口. 通过处理这个消息, 应用程序可以监视拖曳矩形的 Size 和 Position 的变化, (如果需要)还能改变它的 Size 和 Position.
 
当调用 SetWindowPos 函数或者其它窗口管理函数的时候, WM_WINDOWPOSCHANGING 消息将会作为上述函数产生的结果发送给目标窗口, 其中包含了窗口的 Size, Position 以及 Z Order 中的位置等信息.
 
大多数时候一个简单的消息是不够的, 我们可以通过消息携带的参数得到更多信息. 例如, WM_MOVE 消息告诉我们, 我们的窗口已经改变了位置, 并且 LPARAM 参数给出了我们需要的 X 和 Y 轴的位置信息.
 
使用 WM_WINDOWPOSCHANGING 消息仅给我们提供了一个参数 - 一个指向 WindowPos 结构的一个指针, 该结构包含 Windows 的新 Size 和 Position 信息. 这是 WindowPos 结构的定义:
TWindowPos = packed record
  hwnd: HWND; {Window 标识.}
  hwndInsertAfter: HWND; {此窗口上面的 Window}
  x: Integer; {Window 的左边界}
  y: Integer; {Window 的右边界}
  cx: Integer; {Window 宽度}
  cy: Integer; {Window 高度}
  flags: UINT; {Window-positioning 选项.}
end;


我们的任务很简单: 在设定的屏幕边缘的距离(假设20个象素)之内, 当 Delphi 窗口靠近的时候, 让它吸附上去.
 
新建一个 Delphi 窗口, 然后添加一个 TLabel, 一个 TEdit 和 4 个 TCheckBox 控件. 将 TEdit 控件的名称改为 edStrickAt. 将 TCheckBox 控件的名称改为 chkLeft, chkTop... 依此类推. 我们用edStickAt 设定一个象素数, 当我们的窗口距离屏幕边缘的距离小余这个象素的时候, 让它吸附上去. 窗口效果图如下:
 
 
我们唯一感兴趣的消息就是 WM_WINDOWPOSCHANGING. 在我们窗口的声明的 private 部分做声明这个消息的处理函数. 我将在这里给出 "sticking" 过程的全部代码以及代码描述. 注意, 您可以取消有关 TCheckBox 的勾选来防止某个方向的吸附.
使用 SPI_GETWORKAREA 作为 SystemParametersInfo 函数第一个参数, 可以得到工作(桌面)区域的大小信息. 我们用它减去 appbars, 任务栏, IE 工具栏等的大小来确定屏幕的区域.
//...
  private
   procedure WMWINDOWPOSCHANGING
            (Var Msg: TWMWINDOWPOSCHANGING);
             message WM_WINDOWPOSCHANGING;
//...
procedure TfrMain.WMWINDOWPOSCHANGING
          (var Msg: TWMWINDOWPOSCHANGING);
const
  Docked: Boolean = FALSE;
var
  rWorkArea: TRect;
  StickAt : Word;
begin
  StickAt := StrToInt(edStickAt.Text);
  SystemParametersInfo
     (SPI_GETWORKAREA, 0, @rWorkArea, 0);
  with Msg.WindowPos^ do begin
    if chkLeft.Checked then
     if x <= rWorkArea.Left + StickAt then begin
      x := rWorkArea.Left;
      Docked := TRUE;
     end;
    if chkRight.Checked then
     if x + cx >= rWorkArea.Right - StickAt then begin
      x := rWorkArea.Right - cx;
      Docked := TRUE;
     end;
    if chkTop.Checked then
     if y <= rWorkArea.Top + StickAt then begin
      y := rWorkArea.Top;
      Docked := TRUE;
     end;
    if chkBottom.Checked then
     if y + cy >= rWorkArea.Bottom - StickAt then begin
      y := rWorkArea.Bottom - cy;
      Docked := TRUE;
     end;
    if Docked then begin
      with rWorkArea do begin
      // no moving out of the screen
      if x < Left then x := Left;
      if x + cx > Right then x := Right - cx;
      if y < Top then y := Top;
      if y + cy > Bottom then y := Bottom - cy;
      end; {with rWorkArea}
    end; {if Docked}
  end; {with  Msg.WindowPos^}
  inherited;
end;
end.

现在,运行项目,并且把窗口移动到屏幕的任意边缘,就可看到它停靠过去了.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值