Replacing a component class in delphi
Asked Answered
A

2

15

I know I've seen an example somewhere of a hack to define a custom version of an existing VCL component, like TButton or TEdit, with the same class name and do something to make it so that the DFM streamer will instantiate your version instead of the original. Unfortunately, I'm in a situation where I need to be able to do that and I can't find the write-up. Does anyone know where to find information on how to accomplish this?

Anastatius answered 13/1, 2011 at 22:6 Comment(0)
D
24

In your form you can override the ReadState method like so:

type
  TMyForm = class(TForm)
  protected
    procedure ReadState(Reader: TReader); override;
  end;

procedure TMyForm.ReadState(Reader: TReader);
begin
  Reader.OnFindComponentClass := FindComponentClass;
  inherited;
end;

procedure TMyForm.FindComponentClass(Reader: TReader; const ClassName: string;
  var ComponentClass: TComponentClass);
begin
  if ComponentClass=TButton then begin
    ComponentClass := TMySuperDuperButton;
  end else if ComponentClass=TEdit then begin
    ComponentClass := TMyTotallyAwesomeEdit;
  end;
end;

There are likely numerous other ways to do this, but this is how I do it!

EDIT: Inspecting TReader.GetFieldClass(Instance: TObject; const ClassName: string) suggests the hack that Mason recalls. The first line sets ClassType := Instance.ClassType. So I suspect that by changing the declaration in the pas file from Button1: TButton to Button1: MyUnit.TButton will result in your button being created. Or perhaps the hack was to add MyUnit to the uses clause right at the end so that your version of TButton is the one that is in scope. However, none of this sounds very practical.

Darkling answered 13/1, 2011 at 22:11 Comment(4)
Is there any way to make this global for all forms in the app instead of just this one?Anastatius
@Mason all forms in my app derive from TMyForm. That's something I'd recommend to everyone!Darkling
@Mason Otherwise you could just patch it at runtime in the way the Andreas Hausladen does with his stuffDarkling
@David: Aha! Yeah, that's what it is. You need to put the new class's unit in the form's uses clause so the compiler sets up the classes right. But your version actually works better because most of the forms involved do descend from a common base form class anyway. So I'll mark this as accepted.Anastatius
D
13

I guess what you're trying to remember is an "interposer class": inheriting a class giving the same name as the ancestor, by prefixing the ancestor's unit name. Since the class name is not changed, the dfm streaming mechanism is not disturbed. Would only affect the unit the class is re-declared in, unless it is put in a separate unit and that unit is included in the uses section after the base class'es. Obviously, you cannot have published properties in an interposed class.

type
  TButton = class(stdctrls.TButton)
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    [...]
  private
Diverticulitis answered 14/1, 2011 at 0:32 Comment(1)
Yeah, that's what I was thinking of. But David's solution turned out to be more effective for the problem I was trying to solve.Anastatius

© 2022 - 2024 — McMap. All rights reserved.