Another Way to Drag a Window

The most common way to move an application window is to drag it by its title bar. The following article lets you provide dragging capabilities for Delphi forms without a caption, so the user can move them by clicking anywhere on their client area.

   Step aside, junior
For example, consider the case of a Windows application that doesn't have a title bar, how can we move such a window?. In fact, it's possible to create windows with a nonstandard title bar and even non rectangular forms. In this case, how could Windows know where the borders and the corners of the window are?

Messages
The Windows operating system is heavily based on messages. For example, when you click on a window or a control, Windows sends it a wm_LButtonDown message, with additional information about where the mouse cursor is and which control keys are currently pressed. Sounds familiar? Yes, this is nothing more than an OnMouseDown event in Delphi.
Similarly, Windows sends a wm_NCHitTest message whenever a mouse event occurs, that is, when the cursor moves, or when a mouse button is pressed or released. If the mouse is not captured, the message is sent to the window beneath the cursor. Otherwise, the message is sent to the window that has captured the mouse.

If we can make Windows think that the user is dragging (has clicked on) the title bar rather than the client area, then the user could drag the window by clicking in the client area. The easiest way to do this is to "fool" Windows into thinking that you're actually clicking on the title bar of a form. We will do this by handling the WM_NCHitTest windows message. Here's what you have to do:

1. insert the following line into your form's "Private declarations" section (message handling procedure declaration):

 
procedure WMNCHitTest(var Msg: TWMNCHitTest);
  message wm_NCHitTest;

2. add the following code into the "implementation" section of your form's unit (where Form1 is assumed form name):

 
procedure TForm1.WMNCHitTest(var Msg: TWMNCHitTest);
begin
  inherited;
  if  Msg.Result = htClient then
    Msg.Result := htCaption;
end;


The first line of code in the message handler calls the inherited method to obtain the default handling for the wm_NCHitTest message.
The if part in the procedure intercepts and changes your window's behavior. This is what actually happens: when the operating system sends a wm_NCHitTest message to the window, together with the mouse coordinates, the window returns a code that states which portion of itself has been hit. The important piece of information, for our task, is in the value of the Msg.Result field. At this point, we have an opportunity to modify the message result.
This is what we do: if the user has clicked in the form's client area we make Windows to think the user clicked on the title bar. In Object Pascal "words": if the message return value is HTCLIENT, we simply change it to HTCAPTION.

Be carefull: mouse events, no more!
By changing the default behaviour of our forms we remove the ability of Windows to notify you when the mouse is over the client area. One side effect of this trick is that your form will no longer generate events for mouse messages.

Captionless-Borderless Window
If you want a captionless borderless window similar to a floating toolbar, set the Form's Caption to an empty string, disable all of the BorderIcons, and set the BorderStyle to bsNone.

Show window contents while dragging
To show the window contents while dragging the window we can use the next API procedure calls:

 
//to Show window contents while dragging:
SystemParametersInfo
    (SPI_SETDRAGFULLWINDOWS, 1, nil, 0);

//to disable this option call the function:
SystemParametersInfo
    (SPI_SETDRAGFULLWINDOWS, 0, nil, 0);

   More wm_NCHitTest tricks
If you look more carefully at the wm_NCHitTest message you'll see that return value of the function indicates the position of the cursor hot spot. This enables us to play some more with the message to create strange results.

. The following code fragment will prevent users to close your forms by clicking on their Close buttons.

 
  if  Msg.Result = htClose then
    Msg.Result := htNowhere;

. If the user is trying to move the form by clicking on the caption bar and dragging, the code replaces the result of the message with a result which indicates the user clicked on the client area. This prevents the user from moving the window with the mouse (opposite to what we were doing in the begging of the article).

 
  if  Msg.Result = htCaption then
    Msg.Result := htClient;

. A "funny" peace of code, minimize is maximize and maximize is minimize.

 
 if Msg.Result = htMaxButton then
   Msg.Result := htMaxButton
 else if Msg.Result = htMinButton then
   Msg.Result := htMinButton;

   But, I have components on a form!?
In most cases we'll have some components on a form. Let's say, for example, that one Panel object is on a form. If Align property of a panel is set to alClient, the Panel fills the entire client area so that it is impossible to select the parent form by clicking on it. The code above will not work! Why? The code above will not work because the mouse is always moving over the Panel component not the form.

To move our form by dragging a panel on the form we have to add few lines of code in the OnMouseDown event procedure for the Panel component:

 
procedure TForm1.Panel1MouseDown
  (Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  ReleaseCapture;
  SendMessage(Form1.Handle, WM_SYSCOMMAND, 61458, 0);
end;

Note: this code will not work with non-window controls such as TLabel components.

Moving, moving... That's it. If you have some discussion ideas on this topic, please post to the Delphi Programming Forum.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值