|
|
 |
 |
|
Join the Discussion
|
"Post your views, comments, questions and doubts to this article."
Discuss!
|
|
 |
 |
|
|
 |
 |
 |
This is the last part of the "How to move and resize Delphi form controls at run-time" series. In the first part you learned how to enable dragging and resizing controls with the mouse, while the application is running.
The second part described how to add eight sizing handles to mimic the resizing of the control in the IDE (design-time), at run-time.
Storing and swapping event-handling procedures...
While the code provided in the first two parts does the job of allowing a user to drag and resize controls at run time it has some side effects and can be hard to manage.
Firstly, the code is attached to the OnMouseDown, OnMouseMove, and OnMouseUp events for a control. Any code not related to run-time movement needs to be mixed with the "ControlMouseDown/Move/Up" procedures.
Secondly, if for example the TPanel object is allowed to be moved at run-time the OnClick event handling procedure *will* get executed if panel reposition is taking place.
To overcome those issues, it would be best to pull out the "run-time movement" code into a separate unit (defining a custom component). This brings us to the custom "TMover" component:
type
TMover = class(TComponent)
public
procedure Add(Control : TControl);
...
property MovableControls : TComponentList
property Enabled : boolean
...
end;
|
The TMover class defines a property MovableControls of type TComponentList. A public procedure Add is used to add a control to the list of controls that are allowed to be moved (and resized) at run time.
Given the Delphi form (from the last two parts), the OnCreate event handler might look like:
var
mover : TMover;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
mover := TMover.Create(self);
mover.Add(Button1);
mover.Add(Edit1);
mover.Add(Panel1);
mover.Add(Button2);
mover.Add(ListBox1);
end; (*FormCreate*)
|
The TMover's Enabled property specifies if controls added to the MovableControls list can be moved and resized, or not. This is where the fun begins...
For each of the controls in the MovableControls list we have to:
1. Remeber (store in some list) any existing mouse related event handlers,
2. Attach custom handling procedures,
3. When run-time moving and sizing is finished (TMover.Enabled := false) restore original event handling procedures to mouse related events.
Now, one could say that this task is easy. You simply use an assignment like:
// no-go code
TWinControl(MovableControls[i]).OnMouseUp := ControlMouseUp
|
Unfortunately, the TWinControl does not provide the "OnMouse???" events as published.
RTTI, help!
In short, Runtime Type Information provides information about an object's data type that is set into memory at run-time.
Using Delphi' RTTI methods GetMethodProp and SetMethodProp we can read and assign event handling procedure to a set of controls that are not inherited from the same ancestor.
Here's what happens when you set the Enabled property of the TMover component (simplified version):
procedure TMover.SetEnabled(const Value: boolean);
var
idx : integer;
oldMethod : TMethod;
newMethod : TMethod;
ctrl : TControl;
begin
if value = FEnabled then Exit;
FEnabled := Value;
if Enabled then
begin
MouseDownMethods := nil; //clear
SetLength(MouseDownMethods,MovableControls.Count);
//store mouse related event handlers
for idx := 0 to -1 + MovableControls.Count do
begin
ctrl := TControl(MovableControls[idx]);
oldMethod := GetMethodProp(ctrl, 'OnMouseDown');
MouseDownMethods[idx].Code := oldMethod.Code;
MouseDownMethods[idx].Data := oldMethod.Data;
newMethod.Code := MethodAddress('ControlMouseDown');
newMethod.Data := Pointer(self);
SetMethodProp(ctrl, 'OnMouseDown', newMethod);
end;
end
else //disabled
begin
//restore default Mouse related event handler
for idx := 0 to -1 + MovableControls.Count do
begin
ctrl := TControl(MovableControls[idx]);
oldMethod.Code := MouseDownMethods[idx].Code;
oldMethod.Data := MouseDownMethods[idx].Data;
SetMethodProp(ctrl, 'OnMouseDown', oldMethod);
end;
end;
end; (*SetEnabled*)
|
The MouseDownMethods is a dynamic array of TMethod records - and is used to store original event handling procedures.
For each control in the MovableControls, the RTTI's GetMethodProp method returns a value of type TMethod. This value (Control's OnMouseDown event handling method!) is stored in the MouseDownMethods array. Using the SetMethodProp RTTI method, the ControlMouseDown procedure is assigned to handle the OnMouseDown Event.
When Enabled is set to false, we simply revert to the original event handling procedures!
While this might sound like magic, it works like a charm! Download the sample application and try for yourself.
If you need any kind of help at this point, please post to the Delphi Programming Forum where all the questions are answered and beginners are treated as experts.
First part > Move and resize at run-time > Part 1, 2, 3
|