tags:

views:

190

answers:

2

I have a class defined as:

TfraFrame = class(TFrame);

then I have several subclasses all inherting from this, such as:

TfraUsers = class(TfraFrame);
TfraGroups = class(TfraFrame);
TfraMenus = class(TfraFrame);

In my main form i have declared variables as:

var
  fraUsers: TfraUsers;
  fraGroups: TfraGroups;
  fraMenus: TfraMenus;

Now my question is, I would like to have one single function to control the generation of the instances of each class. There should only be once instance of each class, but i only want to create the instances if the user requires them.

I would like to pass the variable like this:

procedure ShowFrame(Frame: TfraFrame)
begin
  if Frame = nil then
  begin
    Frame := TfraFrame.Create(self);
    Frame.Init(Panel1);
  end;

  Frame.Show;
end;

and call it like this;

ShowFrame(fraUsers);

I was expecting it to create a instance of TfraUsers (because that is what fraUsers is declared as), however, i suspect it may be creating an instance of fraFrame.

Is there a way to create an instance of the type that the variable was declared as?

+4  A: 

Yep, you need to tell showFrame which class to instantiate:

add

type
  tfraFrameClass = class of TfraFrame;

just after the declaration of tfraFrame. And change showFrame to:

function ShowFrame(Frame: TfraFrame; FrameClass: TfraFrameClass): TfraFrame;
begin
  if Frame = nil then
  begin
    Result := FrameClass.Create(self);
    Result.Init(Panel1);
  end
  else
    Result := Frame;

  Result.Show;
end;

Note that you cannot pass Frame as a var parameter as the compiler will insist on declared and actual types of the passed parameter to be exactly the same.

Update Updated example to show frame when it was already assigned.

Update I neglected to mention that assigning the result of the Create call in the OP's code for the showFrame procedure wouldn't work. If it wasn't declared as var, the assignment would not go beyond the scope of the showFrame procedure, the value of the variable passed into showFrame would not be changed. Declaring it as a var would not work either as mentioned above. The solution is to do what @Andreas suggests: use an untyped pointer; or make it a function as I did. Of course (grin) I prefer mine as that preserves type safety just a little bit better.

Also, of course in my example the intention was to have the result of the showFrame function assigned to the appropriate frame variable, like so:

fraUsers := showFrame(fraUsers, TfraUsers);

Update Or, as Sertac pointed out, you can still use procedure with a var parameter when you do a cast on the variable you pass to showForm.

procedure showFrame(var Frame: TfraFrame; FrameClasse: TfraFrameClass);
begin
  if Frame = nil then
  begin
    Frame := FrameClass.Create(self);
    Frame.Init(Panel1);
  end;

  Frame.Show;
end;

and call it as:

showFrame(TfraFrame(fraUsers), TFraUsers);
Marjan Venema
I think the OP wants the frame to show if it is assigned...
Andreas Rejbrand
Yes, so what is to stop him assigning the result of the function to the var? He was probably thinking in his original code that it would work by assigning the result of the Create to the passed in parameter, but that isn't going to work, even if it were declared as var because with var parameters Delphi will complain about actual and declared types being different.
Marjan Venema
@Andreas, oh heck, forget my comment. Read yours wrong.
Marjan Venema
@Marjan - "cannot pass Frame as a var parameter" - Why not cast? `ShowFrame(TfraFrame(fraUsers), TfraUsers)`, then we can make it a 'procedure'.
Sertac Akyuz
@Sertac: of course, yes, that works as well. Didn't think of it. Edited my answer to include that option.
Marjan Venema
A: 

I suggest that you make your frame variables properties

property fraUsers: TfraUsers read GetfraUsers write ffraUsers;
..

function GetFraUsers: TFraUsers;
begin
  if fFraUsers = nil then
     fFraUsers := TfraUsers.Create(...);
  Result := ffraUsers;
end;

Then

procedure ShowFrame(Frame: TfraFrame)
begin
  Frame.Init(Panel1);
  Frame.Show;
end;

procedure form1.Button1Click(...)
begin
  ShowFrame(fraUsers); // creates the frame if it does not exist
end;
Despatcher