• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 372
  • Last Modified:

How to dynamically invoke Create(Owner) on a component?

I have a frame TBaseFrame and two derived frames TDerivedFrameAlpha and TDerivedFrameBeta. In a form I put a TBaseFrame, but the TBaseFrame is abstract and will never be used. Only the derived frames. Because there will be a lot more derived frames than Alpha and Beta I want to use the Factory pattern to create derived frames. Like this:

function GetDerivedFrame(ClassName: String): TBaseFrame;

Now I have a detail question. Components need to be created with Create(Owner). Like this:

Button := TButton.Create(Self);

How to do this dynamically?

Like this:

// Globals.ClassMap is a TList with registered classes
Clazz := Globals.ClassMap.Get('TButton');
Button := Clazz.NewInstance();

But this only calls:

Button := TButton.Create();
0
dnoelpp
Asked:
dnoelpp
  • 6
  • 3
1 Solution
 
DMNCommented:
It's is a very simple demo below:

----------------8<---------------------------

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TAbstractComponent=class(TComponent)
    procedure Whoami; virtual; abstract;
  end;
  TAbstractComponentClass=class of TAbstractComponent;

  TComponentAlpha=class(TAbstractComponent)
    procedure Whoami; override;
  end;

  TComponentBeta=class(TAbstractComponent)
    procedure Whoami; override;
  end;

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    FList:TStrings;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TComponentAlpha.WhoAmI;
begin
  ShowMessage('Alpha');
end;

procedure TComponentBeta.WhoAmI;
begin
  ShowMessage('Beta');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FList := TStringList.Create; //Creating factory
  try
    FList.AddObject('Alpha',pointer(TComponentAlpha));
    FList.AddObject('Beta',pointer(TComponentBeta));

    // Now we will create Alpha by name, next Beta.
    with TAbstractComponentClass(FList.Objects[FList.IndexOf('Alpha')]).Create(Self) do
      begin
        WhoAmI; // Should be Alpha, it's Alpha component, not Abstract...
        Free;
      end;

    with TAbstractComponentClass(FList.Objects[FList.IndexOf('Beta')]).Create(Self) do
      begin
        WhoAmI; // Should be Beta...
        Free;
      end;

  finally
    FList.Free; // Destroying factory
  end;
end;

end.

---------------->8---------------------------

Hope this helps...
0
 
dnoelppAuthor Commented:
Thanks! And what happens if you call .Create(Self) on a stored class which is not a TComponent?

TAbstractComponentClass(FList.Objects[FList.IndexOf('Alpha')]).Create(Self)
0
 
DMNCommented:
What do you mean? When you creating factory you should define one common "abstract", all other objects in factory should be objects of common class or its descendants....
0
Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 
DMNCommented:
What do you mean? When you creating factory you should define one common "abstract" class, all other objects in factory should be objects of common class or its descendants....
0
 
dnoelppAuthor Commented:
Yes it SHOULD. But what, if you make a mistake and put erroneously an other TComponent into the TList? Code defensively and think about, what happens... :-)
0
 
dnoelppAuthor Commented:
Er, an other TObject, I wanted to say!
0
 
dnoelppAuthor Commented:
By the way, I tried your solution. But I get an Access Violation.

--------8<-----------8<--------------

procedure TClassMap.Add(Clazz: TClass);
begin
  ClassList.AddObject(Clazz.ClassName(), Pointer(Clazz));
end;

function TClassMap.Get(const Name: String): TClass;
var
  Index: Integer;
begin
  Index := ClassList.IndexOf(Name);
  if Index < 0 then Result := nil
  else Result := TClass(ClassList.Objects[Index]);
end;

function TClassMap.CreateComponent(
  const Name: String; Owner: TComponent
): TComponent;
var
  Clazz: TClass;
begin
  Clazz := Get(Name);
  if Clazz <> nil then begin
    Result := TComponent(Clazz).Create(Owner);
  end else begin
    Result := nil;
  end;
end;

--------8<-----------8<--------------

I replaced CreateComponent with following code. This works perfectly, but I don't have a Create(Owner) any more, and we are again at the beginning of my question.

--------8<-----------8<--------------

function TClassMap.CreateComponent(
  const Name: String; Owner: TComponent
): TComponent;
var
  Clazz: TClass;
begin
  Clazz := Get(Name);
  if Clazz <> nil then begin
    Result := Clazz.Create() As TComponent;
  end else begin
    Result := nil;
  end;
end;

--------8<-----------8<--------------

0
 
dnoelppAuthor Commented:
This question is still open, I up to 100 points!
0
 
dnoelppAuthor Commented:
Your solution doesn't quite work... It gives AV, but since nobody else answered I honor your work...
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 6
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now