Delphi Tutorial - How to Move Controls and Save their Positions

'

Introduction

The aim of this tutorial is to show, how you can allow the user to move any control, with the minimum of coding on your part. It also shows how to save these new positions to registry so that next time the application is started all the controls will appear in the position they where left in last time.

There are only 3 short procedures in the unit, these are :

FormCreate

This loops through all the components owned by the form, if the component is a TControl then it looks in the registry for its left and top property and if its not there then it places it where ever it was placed at design time. Also it changes the cursor for each control to a cross to indicate to the user that its movable. Finally we need to assign a new application message handler so that when we get a mouse down we can perform the actual component moving.

AppMessage

This is the application message handler that we referenced earlier. This procedure is called every time our application receives a windows message, but only does anything on the Left Mouse Button Down message (wm_LBUTTONDOWN). When this message is received it releases the mouse capture, and sends a message to the control under the mouse to move mode. Finally it sets the handled var to true to indicate that it has dealt with the message.

FormDestroy

All the formDestroy does is save all the controls position to the registry by looping through all the components on the form (like in the formCreate) and saving their left and top properties.

The Code

Create a new application and drop as many controls as you want onto the main form. (I used a TButton, a TEdit, aTListBox and a TComboBox) You may call your controls anything you like as when their positions are saved to the registry the actual name you choose is used by using the name property of each control. Use the Object Inspector to produce stubs for the Create and Destroy events of the main form and modify your code with that shown below:

unit Main;

interface

uses
  Windows,WinProcs, Messages, SysUtils, Classes, 
  Graphics, Controls, Forms, Dialogs, StdCtrls, Menus, 
  registry;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    ListBox1: TListBox;
    ComboBox1: TComboBox;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    procedure AppMessage(var Msg: TMsg; 
	                     var Handled: Boolean);
  public
    { Public declarations }
  end;

const
  SC_SIZE = $F012;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  iTemp: Integer;
  r : TRegIniFile;
begin
  (* Open the key in the registry where we store 
     all the positions *)
  r := TRegIniFile.Create('Resize Test');

  (* Loop through all the components on a form *)
  for iTemp := 0 to ComponentCount -1 do
    (* Get the position of all controls *)
    if Components[iTemp] is TControl then
    with Components[iTemp] as TControl Do
    begin
      left:=r.readinteger(Name,'Left',left);
      top:=r.readinteger(Name,'Top',Top);
      (* Make there cursors crosses *)
      cursor:=crCross;
    end;
  (* Release the registry object *)
  r.free;

  (* Use our own message handler for all message 
     sent  to the application *)
  Application.OnMessage := AppMessage;
end;

procedure TForm1.AppMessage(var Msg: TMsg; 
                            var Handled: Boolean);
begin
  (* Only do anything if the left mouse button is 
     pressed *)
  if  msg.message = wm_LBUTTONDOWN then
  begin
   (* Release the mouse capture *)
   WinProcs.ReleaseCapture;
   (* Send a message to the control under the 
      mouse to go into move mode *)
   postmessage    ( msg.hwnd,WM_SysCommand, SC_SIZE, 0 );
   (* Say we have handled this message *)
   Handled:=true;
  end;
end;


procedure TForm1.FormDestroy(Sender: TObject);
var
  iTemp: Integer;
  r : TRegIniFile;
begin
  (* As in the form create loop through all the 
     components But this time write the left and 
	 top properties to the registry         *)

  r := TRegIniFile.Create('Resize Test');
  for iTemp := 0 to ComponentCount -1 do
    if Components[iTemp] is TControl then
    with Components[iTemp] as TControl Do
    begin
      r.writeinteger(Name,'Left',left);
      r.writeinteger(Name,'Top',Top);
    end;
  r.free;
  end;
end.

The following code can be modified easily to have a design mode and a running mode by adding a flag indicating whether the Application.OnMessage := AppMessage is needed e.g.

If fDesigning then 
  Application.OnMessage := AppMessage
else
  Application.OnMessage := nil;

If you only want a few controls to be movable just make those controls respond to the onMousedown event.

Well I hope this is enough to get you started, and if you want further information including how to resize controls then have a look at the article A Cool and Easy Method for Resizing and Moving Controls at Run Time by Brent Eagles

 


Google
Web www.Delphi-Central.com
Delphi Central - Delphi Programming Tutorials, Hints and Tips