0
votes

I have a form with about 168 Timage objects containing Icons that are user selectable.

I wish to make each Icon move Down and Right by 3 pixels when the mouse is over the Timage object. I want it to return to its original position when the mouse leaves the Timage. This will add a pleasing effect to the user interface.

I know I can do this in the OnMouseEnter and OnMouseLeave events and it works well - however I cannot help but think that there must be a more elegant / efficient method to produce this effect for all 168 Timage objects, rather than creating 168 OnMouseEnter procedures and 168 OnMouseLeave procedures.

Any help much appreciated ...

2

2 Answers

4
votes

It is enough to create a single OnMouseEnter event handler procedure and assign it to every component (similar for OnMouseLeave).

If these components were created in design-time (hard to imagine), then you can select all 168 images in the Form Designer, and then go to the Object Inspector and assign the events in a single go, as Remy Lebeau wrote in comments. Alternative way - use existing list of components (assuming that owner is form and there is no other TImages on the form):

for i := 0 to Components.Count - 1 do
  if Components[i] is TImage then  //perhaps more conditions to separate needed images
     TImage(Components[i]).OnMouseEnter := EnterHandler;

If components were created in run-time and they are stored in array or list, handler assigning is simpler:

for i := 0 to Images.Length - 1 do
   Images[i].OnMouseEnter := EnterHandler;

Then you can work with each component using the event's Sender argument:

procedure TMyForm.EnterHandler(Sender: TObject);
begin
  TImage(Sender).Left := TImage(Sender).Left + 3;
  TImage(Sender).Top := TImage(Sender).Top + 3;
end;

procedure TMyForm.LeaveHandler(Sender: TObject);
begin
  TImage(Sender).Left := TImage(Sender).Left - 3;
  TImage(Sender).Top := TImage(Sender).Top - 3;
end;
3
votes

The cleanest solution here would be to create a custom component and to sanitize your design away from such a heavy and flat design-time layout. These naturally become difficult to maintain and to modify.

That said, if you want a quick hack to save yourself a lot of typing and clicking, you can use an interposer class to inject this mouse behaviour.

In the interface section of your Form's unit, add the following class above the Form's class declaration:

type
  TImage = class(Vcl.ExtCtrls.TImage)
  private
    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
  end;       

  TForm1 = class(TForm)
    { ... rest of your form as normal }
  end;

And then, in the implementation section, add this:

procedure TImage.CMMouseEnter(var Message: TMessage);
begin
  inherited;
  Top := Top + 3;   
  Left := Left + 3;
end;

procedure TImage.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  Top := Top - 3;   
  Left := Left - 3;
end;

Defining an interposer like this effectively causes your modified TImage class to replace all of the existing TImage components that are placed on the Form at design-time.

Note that this example is only for VCL on Windows. For a cross-platform solution using FMX, all UI controls have virtual DoMouseEnter() and DoMouseLeave() methods that you can override instead, eg:

type
  TImage = class(FMX.Objects.TImage)
  protected
    procedure DoMouseEnter; override;
    procedure DoMouseLeave; override;
  end;       

...

procedure TImage.DoMouseEnter;
begin
  inherited;
  Top := Top + 3;   
  Left := Left + 3;
end;

procedure TImage.DoMouseLeave;
begin
  inherited;
  Top := Top - 3;   
  Left := Left - 3;
end;