Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(Delphi) Call Child's procedure from Parent

How can parent procedure call it's child's procedure which was override from itself?

type
  TBase = class(TForm)
  procedure BtnRefreshClick(Sender: TObject);
  protected
    text: string;
end;

procedure TBase.BtnRefreshClick(Sender: TObject);
begin
  showmessage(text);
end;

type
  TParent = class(TBase)
  protected
    procedure doThis;
  end;

procedure TParent.doThis;
begin
  // blah blah do something
  BtnRefreshClick(nil);
end;

type
  TChild = class(TParent)
  procedure BtnRefreshClick(Sender: TObject);
  protected
    procedure clicky; override;
  end;

procedure TChild.BtnRefreshClick(Sender: TObject);
begin
  text := 'Hello, World!';
  inherited;
end;

Actual calling procedure will be somewhat like :

child := TChild.Create;
child.doThis;

If I try to do child.BtnRefreshClick; then it will produce a dialog of Hello, World! since TChild.BtnRefreshClick was called and the text variable were set.

But when I call child.doThis it will only shows an empty dialog box since child.doThis calls parent.doThis and then ??.BtnRefreshClick. How to make parent.doThis call child.BtnRefreshClick? Is it not possible?

Thanks in advance,
Yohan W.

like image 971
saintfalcon Avatar asked Dec 10 '25 09:12

saintfalcon


2 Answers

The parent class calls the base class's method because that's the only method that existed within the scope of the parent class. When the compiler compiled the TParent code, it bound the name BtnRefreshClick to the method in the base class, not the one in the child class, because the child class is not an ancestor of the parent.

In general, for a parent object to call a method of a child class, that method should be declared in the parent object (or higher) and be virtual. If you change TBase to make BtnRefreshClick virtual, and you change TChild to override that same method, then when TParent.doThis calls it, the call will be dispatched to the TChild method.

type
  TBase = class(TForm)
    procedure BtnRefreshClick(Sender: TObject); virtual;
  end;

  TChild = class(TParent)
    procedure BtnRefreshClick(Sender: TObject); override;
  end;

In the particular case of a form with method properties assigned by name via DFM settings, another solution is as Saintfalcon's answer demonstrates, which is to call the associated button's Click method. When the button on the TChild form is instantiated, the VCL reads the DFM resource and finds the string "BtnRefreshClick" associated with the button's OnClick event. It uses the form's MethodAddress function to look up the address of the method with that name, and it finds the one belonging to TChild. It assigns that value to the OnClick property. The Click method reads that property and calls whatever method is there.

I've written before about the differences between calling an event-handler method directly, calling the event-handler property, and calling the event trigger, but at that time, I hadn't considered the aspect illustrated here, where a handler is hidden or overridden in a descendant class.

like image 89
Rob Kennedy Avatar answered Dec 12 '25 04:12

Rob Kennedy


As stated by @StefanGlienke to use btnRefresh.click

procedure TParent.doThis;
begin
  // blah blah do something
  // BtnRefreshClick(nil); // this will call TBase.BtnRefreshClick
  btnRefresh.Click; // use this instead to call TChild.BtnRefreshClick
end;

Thank you

like image 38
saintfalcon Avatar answered Dec 12 '25 06:12

saintfalcon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!