Search code examples
delphidelphi-2010vcl

Custom Component TImage events causing error "Does not exist"


I have a custom component witht the following events

 private
    { Private declarations }
    ...
    fOnImageClick: TNotifyEvent;
    fOnImageMouseUp: TMouseEvent;
    fOnImageMouseDown: TMouseEvent;
    fOnImageMouseMove:  TMouseMoveEvent;
    fOnImageMouseEnter: TNotifyEvent;
    fOnImageMouseLeave: TNotifyEvent;
    fOnImageSelect: TNotifyEvent;
    fOnImageDblClick: TNotifyEvent;
  protected
    ...
  public
    { Public declarations }
     ...
  published
     ...
     property OnImageClick: TNotifyEvent read  fOnImageClick write  fOnImageClick;
     property OnImageSelect: TNotifyEvent read  fOnImageSelect write  fOnImageSelect;
     property OnImageDblClick: TNotifyEvent read  fOnImageDblClick write  fOnImageDblClick;
     property OnImageMouseDown: TMouseEvent read  fOnImageMouseDown write  fOnImageMouseDown;
     property OnImageMouseUp: TMouseEvent read  fOnImageMouseUp write  fOnImageMouseUp;
     property OnImageMouseMove:  TMouseMoveEvent read  fOnImageMouseMove write  fOnImageMouseMove;
     property OnImageMouseLeave: TNotifyEvent read  fOnImageMouseLeave write  fOnImageMouseLeave;
     property OnImageMouseEnter: TNotifyEvent read  fOnImageMouseEnter write  fOnImageMouseEnter;
  end;

I assign them to a TImage whose parent is TPanel whos parent is TScrollBox

 img:= TImage.Create(ThumbPnl);
 img.Parent:= ThumbPnl;
 img.Tag:= I;
 img.Align:= alClient;
 img.Stretch:= true;
 img.OnClick:= fOnImageClick;
 img.OnDblClick:= fOnImageDblClick;
 img.OnMouseEnter:= fOnImageMouseEnter;
 img.OnMouseLeave:= fOnImageMouseLeave;
 img.OnMouseDown:= fOnImageMouseDown;
 img.OnMouseUp:= fOnImageMouseUp;
 img.OnMouseMove:= fOnImageMouseMove;

The component compiles and bulds just fine. The application with this component also compiles and runs jus fine. If I assign an OnClick event, it works. All other events, if I assign them and try to run the app, i get an error saying the event doesn't exist

Anyone know why that is?


Solution

  • What is an event

    The event in Delphi is a method pointer. A method pointer is basically two pointers, one points to the method you assign to the event, and the other points to the live instance (object) you assign.

    What I assume you're expecting

    I think you expect the event in your inner object follows the events assigned to your outer object, and that will not happen automatically.

    When you assign it, you're doing pointer assignments. So to take an example, your line

    img.OnDblClick:= fOnImageDblClick;
    

    Performs a pointer assignment with the value fOnImageDblClick have at the time. If it is nil, img.OnDblClick will be nil from now. If it points to Form1.MyComponentImageClick at the time, img.OnDblClick will point to the same method on the same object from now, but, if you later change where fOnImageDblClick points, the imgOnDblClick will not follow... it will remains pointing to the same old address. If you want it to change, you have to make that change by code also.

    If you want that to happen, you can do that in a event setter in the outer class.

    First, declare your events like this:

    published
      ...
      property OnImageDblClick: TNotifyEvent read  fOnImageDblClick write  SetOnImageDblClick; 
    
    ...
    procedure TMyClass.SetOnImageDblClick(Value: TNotifyEvent);
    begin
      FOnImageDblClick := Value;
      //pass the new value to the inner object.
      if Assigned(Img) then
        Img.OnDblClick := Value;
    end;
    

    If your Img inner object exists all the time, you don't need the FOnImageDblClick variable, you can write also a getter for the property and take the value directly from the inner object, like this:

    function TMyClass.GetOnImageDblClick: TNotifyEvent;
    begin
      Result := Img.OnDblClick;
    end;