Another Way to Drag a Window

原创 2001年12月06日 09:54:00
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?

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);
  if  Msg.Result = htClient then
    Msg.Result := htCaption;

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:



   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);
  SendMessage(Form1.Handle, WM_SYSCOMMAND, 61458, 0);

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.

Another Way to Drag a Window

  • zgqtxwd
  • zgqtxwd
  • 2008年04月30日 17:22
  • 108

【译文】如何培养自信的习惯(How to Bulid the Confidence Habit)

How to Build the Confidence Habit如何培养自信的习惯“If you hear a voice within you say “you cannot paint,” th...
  • csmqq
  • csmqq
  • 2011年02月17日 18:15
  • 1466

Java大数简单题---Yet another A + B

PracticeGym 100735IDescription StatementsYou are given three numbers. Is there a way to replace var...
  • why850901938
  • why850901938
  • 2016年04月06日 21:18
  • 503

杭电 HDU ACM 2612 Find a way (简单两路广搜)

欢迎参加——每周六晚的BestCoder(有米!) Find a way Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/...
  • lsgqjh
  • lsgqjh
  • 2015年07月31日 09:47
  • 1634

去掉Drag a column header

  • flying_fei
  • flying_fei
  • 2011年03月04日 11:34
  • 1961


  • leopard21
  • leopard21
  • 2014年05月28日 13:25
  • 2154

HDU 2612 find a way

  • sky_miange
  • sky_miange
  • 2015年02月06日 11:05
  • 962

[ACM] hdu Find a way

Find a way Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total...
  • sr19930829
  • sr19930829
  • 2014年01月18日 14:57
  • 2570

杭电-2612 Find a way(广搜)

Find a way Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...
  • lu_1110
  • lu_1110
  • 2016年07月30日 09:46
  • 217

有关datagrid控件显示的drag a column header here to group by that column

  很久以前用VS2005的时候用到了第三方控件,其中就遇到了datagrid控件显示的drag  a column header here to group by that column,当时折腾了...
  • wangjuan8808
  • wangjuan8808
  • 2010年08月27日 18:50
  • 2443
您举报文章:Another Way to Drag a Window