2009. január 10., szombat

Dragging controls and forms the easy way


Problem/Question/Abstract:

This article shows a technique to drag a form without caption other than responding to NC_HITTEST messages. This technique can also be used to accomplish the dragging of Windowed controls inside the form.

Answer:

The code bellow was created when I was writting a component to allow the dragging of forms without captions. First I found code using the NC_HITTEST message, but the technique presented here offers a lot of other possibilities since it can be applied to any windowed control (not only forms), and will allow you to move them on the form with only 2 or 3 lines of code.

It consists of sendind a WM_SYSCOMMAND message to the desired window (remember that all windowed controls are considered windows on the Windows OS :-) with the correct parameters set, and the window will behave as if the user had started dragging the window by clicking on its caption (this works even with windows without captions, like text boxes.)

The funny part was that this parameter for the WM_SYSCOMMAND message isn't documented (it isn't on my Windows SDK help). I've discovered it while debugging an application. I've put a handler for the WM_SYSCOMMAND message and was showing on the screen all the values for its parameters and to my surprise, when I started to drag the form the value $F012 poped-up. Then I tried to send it to the form and it didn't worked. After a while I figure out how to do it correctly and the code for this follows:

Put the code bellow on the OnMouseDown handler for any form:

procedure TForm1.FormMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
  begin
    ReleaseCapture;
    Perform(WM_SYSCOMMAND, $F012, 0);
  end;
end;

You can also put this code on the OnMouseDown of a single panel or a group of panels, effectively creating a new drag point for the form. When the user tries to drag the panel you send the message above to the form and a dragging operation will start. It is easier to accomplish this with this method than using the NC_HITTEST message:

procedure TForm1.Panel1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
  begin
    ReleaseCapture;
    Perform(WM_SYSCOMMAND, $F012, 0);
  end;
end;

If you write

Panel1.Perform(WM_SYSCOMMAND, $F012, 0)

the panel will start moving inside the form as if it was itself a form. When you release the mouse it will stay were you left it (no additional code required).

This code can be much useful sometimes, but it is very very simple. Hope you liked it.

I played a bit with the code modifying the $F000 Part.

$F000 (Center cursor on the form)
$F001 (Resize from left)
$F002 (Resize from right)
$F003 (Resize from up)
$F004 (Lock the bottom right corner of the form, the up left corner move for resize)
$F005 (Same from bottom left corner)
$F006 (Lock up right and left border, resize other)
$F007 (Lock up and right border, resize other border)
$F008 (Lock left and up border and resize other)
$F009 (Drag from anywhere)
$F010 (Put cursor centered at the upper order)
$F020 (Auto-Minimize Form)
$F030 (Auto-Maximize Form)
$F040 (Stop! You don't want that, it will lock all mouse click and make
you reboot)
$F148 (Activate ScreenSaver)
$F13E (Activate StartButton)

Nincsenek megjegyzések:

Megjegyzés küldése