I would like to simplify the following code in Delphi XE6 on Windows, which becomes unmaintainable as I am adding more and more types of components.
Portability note: I would like to use the same code later in Lazarus 2.0.2 on Linux with little to no change, so the Windows message handling is out of the question.
The problem is, I can't seem to find a way to assign the OnMouseEnter event handler to all components on the whole Form.
I tried all I could including various classes common in their object tree. The OnMouseEnter event seems to be nowhere in the common ground.
The event handler itself contains just one command (procedure = void function), and it will have no more, maybe this could simplify the whole problem?
As you can see below, at this moment I need to add each type of component (currently only TLabel, TButton, and TEdit) to the for-loop.
procedure TFormMain.FormCreate(Sender: TObject);
var
I: Integer;
begin
for I := 0 to FormMain.ComponentCount - 1 do
begin
if FormMain.Components[I] is TLabel then
begin
(FormMain.Components[I] as TLabel).OnMouseEnter
:= @CustomGenericMouseEnter;
end;
if FormMain.Components[I] is TButton then
begin
(FormMain.Components[I] as TButton).OnMouseEnter
:= @CustomGenericMouseEnter;
end;
if FormMain.Components[I] is TEdit then
begin
(FormMain.Components[I] as TEdit).OnMouseEnter
:= @CustomGenericMouseEnter;
end;
end;
end;
procedure TFormMain.CustomGenericMouseEnter(Sender: TObject);
begin
SingleCustomProcedure; // no arguments, nor return value
end;
I am programming a color picker application and thus want to show the user the mouse coordinates.
I have a Polling timer in there, I don't want to add more code, than necessary, so I hope this is self-explanatory:
procedure TFormMain.TimerMousePollTimer(Sender: TObject);
begin
if MousePosChanged then
begin
LabelEdit_MousePosX.Text := MousePosX.ToString;
LabelEdit_MousePosY.Text := MousePosY.ToString;
end;
end;
Further, I do have OnMouseLeave implemented.
The OnMouseEnter event seems to be nowhere in the common ground.
Actually, it is. OnMouseEnter
is a member of TControl
, which all visual controls derive from, but most controls do not promote it to published
. However, since it is declared as protected
, you can use an accessor class to reach it on any control, eg:
type
TControlAccess = class(TControl)
end;
procedure TFormMain.FormCreate(Sender: TObject);
var
I: Integer;
Comp: TComponent;
begin
for I := 0 to ComponentCount - 1 do
begin
Comp := Components[I];
if Comp is TControl then
TControlAccess(Comp).OnMouseEnter := CustomGenericMouseEnter;
end;
end;
This works because TControlAccess
gains access to all of TControl
's protected members, and the unit that declares TControlAccess
has access to all of TControlAccess
's protected members.
On the other hand, OnMouseEnter
is initially protected
so controls can decide whether they want to expose access to it. If you want to respect that decision and only set it for controls that have promoted it, you can use RTTI for that, eg:
uses
..., TypInfo;
procedure TFormMain.FormCreate(Sender: TObject);
var
I: Integer;
Comp: TComponent;
Prop: PPropInfo;
M: TMethod;
begin
TNotifyEvent(M) := CustomGenericMouseEnter;
for I := 0 to ComponentCount - 1 do
begin
Comp := Components[I];
if not (Comp is TControl) then Continue;
Prop := GetPropInfo(Comp, 'OnMouseEnter', [tkMethod]);
if Prop <> nil then
SetMethodProp(Comp, Prop, M);
end;
end;
Alternatively (Delphi 2010+ only):
uses
..., System.Rtti;
procedure TFormMain.FormCreate(Sender: TObject);
var
I: Integer;
Ctx: TRttiContext;
Comp: TComponent;
Prop: TRttiProperty;
V: TValue;
begin
V := TValue.From<TNotifyEvent>(CustomGenericMouseEnter);
for I := 0 to ComponentCount - 1 do
begin
Comp := Components[I];
if not (Comp is TControl) then Continue;
Ctx.GetType(Comp.ClassType).GetProperty('OnMouseEnter');
if (Prop <> nil) and (Prop.Visibility in [TMemberVisibility.mvPublic, TMemberVisibility.mvPublished]) then
Prop.SetValue(Comp, V);
end;
end;