Solved

Accessing a method of a class instance at runtime

Posted on 2004-04-17
10
390 Views
Last Modified: 2010-04-05
I have a base class called TForm_Clean:


  TForm_Clean = class(TForm)
  public
    procedure ActivateReports; vritual; abstract;
  end;


All the forms I create in the project, inherits from TForm_Clean and they all implement the ActivateReports methods which is declared as abstract in the base class.

I also have a class called ActivateReports that keeps track of all the opened forms as well as the current active form in the application.

Now, at runtime, I need to call the ActivateReports method of the current active form.


procedure TForm1.Button1Click(Sender: TObject);
var
  Form: TCustomForm;
  FormClass: TPersistentClass;
begin
  Form := TCustomForm(Application.FindComponent(ActiveForm.ActiveForm));

  if not Assigned(Form) then
  begin
    FormClass := GetClass('T' + ActiveForm.ActiveForm);

    { correct code needs to be inserted here }

  end;
end;

I can get hold of the correct form object (Form) as well as the correct class type (FormClass), but I haven't managed to cast the Form to the correct class to be able to call the correct ActivateReports method.


I tried the following without success

TFormClass(FormClass)(Form).Test;
or
(Form as TFormClass(FormClass)).Test;

after I saw the following sample code to create a new instance of a certain class at runtime:
Form := TFormClass(FormClass).Create(Application);


Any ideas ?
0
Comment
Question by:LiveBootleg
  • 4
  • 3
  • 2
  • +1
10 Comments
 
LVL 11

Expert Comment

by:shaneholmes
Comment Utility
LiveBootLeg,

You dont show how you have derived and created your form descendent.

If you derive your class from TCustomForm or TForm, do not call the
inherited Create constructor. Instead, call CreateNew and
InitInheritedComponent. Look at the Create constructors in TForm and
TDataModule for examples.

If, on the other hand, your custom form will have a DFM file, you
might be able to call the inherited Create constructor, which makes
your job a little easier.

2. Register a custom module for your form. See DsgnIntf.pas for the
TCustomModule class and the RegisterCustomModule procedure. You can
use TCustomModule as the custom module class, or you can derive your
own from TCustomModule. TCustomModule is similar to a component
editor, but for a custom form.

3. Define a form expert. When the user invokes the form expert, create
an instance of your new form. The easiest way to write a form expert
is to download the Expert Tool Kit from
http://www.tempest-sw.com/opentools/. A future version of the expert
tool kit will have a custom form wizard, too, which will simplify the
task of creating custom form and custom module classes.


Shane
0
 
LVL 3

Expert Comment

by:MikProg
Comment Utility
I suppose you have some forms&classes like this

  TForm_Clean = class(TForm)

  TForm_Clean1 = class(TForm_Clean)

  TForm_Clean2 = class(TForm_Clean)

  TForm_Clean3 = class(TForm_Clean2)

Each of them have ActivateReport. Any count of TForm_Clean may coexists at same time and not child of any parent or all of them are MDI forms. My way follows

procedure TForm1.Button1Click(Sender: TObject);
var
  Form: TCustomForm;
  FormClass: TPersistentClass;
begin

{Straight way}
  Form := Screen.ActiveForm;
{another way For Non MDI app's}
  Form := Application.MainForm;
{else MDI app}
  Form := Application.MainForm.ActiveMDIChild;

  if not Assigned(Form) then
  begin
    {FormClass := GetClass('T' + ActiveForm.ActiveForm);}
    { >>correct code needs to be inserted here }
    {There is now correct code becose you find not the instance of object. If you want new instance you must CREATE it}
    FormClass := GetClass('T' + ActiveForm.ActiveForm).Create(...).....
  else
     if FormClass is TForm_Clean then
       with FormClass as TForm_Clean do
           ActivateReport;
  end;
end;

0
 
LVL 17

Expert Comment

by:geobul
Comment Utility
Hi,

I think you need this:

  ...
  if (Screen.ActiveForm is TForm_Clean) then
    (Screen.ActiveForm as TForm_Clean).ActivateReports; // will call the appropriate method of the currently active form
  ...

Regards, Geo
0
 
LVL 1

Author Comment

by:LiveBootleg
Comment Utility
MikProg,

I don't use MDI, I dock the forms within the applications main form. Then it doesn't show the close/minimize/restore icons for the MDI child.

I use the FindComponent method to get hold of the current active form like this:

Form := TForm_Clean(Application.FindComponent(ActiveForms.ActiveForm));

At this stage Form is of type TForm_Clean and when I call Form.ActivateReports I get an abstract error because it tries to call TForm_Clean.ActivateReports but this is an abstract method. I need a way to cast the form to it's declared type like TForm_Clean1 to be able to access the correct ActivateReports method.


shaneholmes,

The setup is really quite simple. I created Form_Clean (File -> New -> Form) and it inherits from TForm. All the other forms are created normally like any other Delphi form with its own DFM file. I only used the TCustomForm class, because I saw it in an example.


I searched the web for something similar to this and the closest I could come up with is the following link, unfortunately it is in Java:

http://www.experts-exchange.com/Programming/Programming_Languages/Java/Q_10038796.html

Have you guys ever worked with interfaces ? I tried to implement the abovementioned example in Delphi, but I don't understand interfaces all that well, so I could not get to cast a TObject to an interface...


0
 
LVL 3

Accepted Solution

by:
MikProg earned 250 total points
Comment Utility
I think you forget override specificator of ActivateReports in descendant classes. It is frequent reason of abstract errors (if you not forget to define them at all). Furthermore,

GetClass('T' + ActiveForm.ActiveForm) returns CLASS not instance of any CREATED object. Using Methods & propertyes of CLASS possible only for CLASS functions and procedures (they are can't get SELF variable value). You do not need to use GetClass at this point.

Try this. Stop program execution wit breakpoint at
FormClass := GetClass('T' + ActiveForm.ActiveForm).Create(...).....
Open Evaluation window and
1. type ActiveForm.ActiveForm. If value NIL then ActiveForm.ActiveForm d's not work well
2. If value not nil type ActiveForm.ActiveForm.ClassName. If value is not name of TForm_Clean( descendants then ActiveForm.ActiveForm d's not work well.
3. End point. Type ActiveForm.ActiveForm.ActivateReport if ActiveForm.ActiveForm does not stores right initialized object error.
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 1

Author Comment

by:LiveBootleg
Comment Utility
OK,

I figured it out. I still don't quite understand the concept, but it works !!!


procedure TForm1.Button1Click(Sender: TObject);
var
  Form: TForm;
  FormClass: TPersistentClass;
begin
  Form := TForm_Clean(Application.FindComponent(ActiveForms.ActiveForm));

  if Assigned(Form) then
  begin
    FormClass := GetClass('T' + ActiveForms.ActiveForm);
    Form := Form as TFormClass(FormClass);
    TForm_Clean(Form).ActivateReports;
  end;
end;


As far as I understand it must still call TForm_Clean.ActivateReports, but it calls the correct one !

geobul,

I did it that way, but I want it dynamic to be used for any class, beacuse there are about 100 forms in the project.

Otherwise I must write:

if (ActiveForms.ActiveForm) = 'Form_Clean1' then
  TForm_Clean1(Form).ActivateReports
else if (ActiveForms.ActiveForm) = 'Form_Clean2' then
  TForm_Clean2(Form).ActivateReports
else if (ActiveForms.ActiveForm) = 'Form_Clean3' then
  TForm_Clean3(Form).ActivateReports
else if ....
0
 
LVL 3

Expert Comment

by:MikProg
Comment Utility
If you need explanation this is what you do exactly:

Form := TForm_Clean(Application.FindComponent(ActiveForms.ActiveForm));
    The result is Form and ActiveForms.ActiveForm have identical value same result produce
       Form:=TForm(ActiveForms.ActiveForm);
FormClass := GetClass('T' + ActiveForms.ActiveForm);
Form := Form as TFormClass(FormClass);
    Have dummy result "as TFormClass(FormClass)" needed for type checking and does not produce any changes Form variable
TForm_Clean(Form).ActivateReports;
    the only string you need.
Optimized source follows

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Assigned(ActiveForms.ActiveForm) then
{if ActiveForms.ActiveForm  have type TForm_Clean}
    ActiveForms.ActiveForm.ActivateReports;
{else you mast shure that ActiveForms.ActiveForm have correct type of value no metter what decalred type is}
    if  ActiveForms.ActiveForm is TForm_Clean then
       TForm_Clean(ActiveForms.ActiveForm).ActivateForm;
{end. use only one of this}
end;

Try this. This work and it is elegant. "Only beutiful planes can fly well" :)
0
 
LVL 1

Author Comment

by:LiveBootleg
Comment Utility
MikProg,

Thanx dude. I have a problem with Delphi in this regard. It allows you to do basically anything and it doesn't force you to structure things in a certain way like C++. That caused me never to use proper inheritance and so on - now when I want to use it, I struggle.
0
 
LVL 17

Expert Comment

by:geobul
Comment Utility
I can't understand what you meant. It is dynamic ! There is only one class mentioned in my code above - the base class where the abstract  method is declared. You don't have to write any if..else there. Have you tried it at all?
0
 
LVL 3

Expert Comment

by:MikProg
Comment Utility
Word about interfaces.
You mean interfaces defined with Interface keyword? If so, they are no need in Delphi programming until you wish to create anything out of range (multiple inheritance, COM objects wich are all multiple inherited and so on). One clue to previous sentance. You want to call method of objects that descendants of different ancestors. This method have name common to all classes TheMethod.

Normal way
TAnc1 = class
procedure TheMethod; virtual;
...
end;

TAnc2 = class
  procedure TheMethod; virtual;
end;

procedure CallTheMethod(Object: TObject);
begin
   if Object is TAnc1 then
       TAnc1(Object).TheMethod
   else
     if Object is TAnc1 then
       TAnc1(Object).TheMethod;
end;

You like if else if else if else if else if else? I'm not! Interface is solution for this problem.

TheMethodAble = interface
   procedure TheMethod;
end;

TAnc1 = class (TheMethodAble)
procedure TheMethod; virtual;
...
end;

TAnc2 = class (TheMethodAble)
  procedure TheMethod; virtual;
end;

procedure CallTheMethod(Object: TheMethodAble);
begin
   Object.TheMethod
end;

It's all about interfaces. Magic vanishes.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
This video discusses moving either the default database or any database to a new volume.

772 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now