I have an application which includes the "main" form as well as four other forms (formA, formB, formC, formD). Each one of the four forms has a unique class function execute()
.
Now, in formD, I put an edit box and a button. On the button's OnClick
event, depending on which form's name I pass in the edit box, I want to run the appropriate class function.
I tried to implement this task by creating a TDictionary
in which I add 3 pairs of values, but it didn't work. Specifically I did the following:
unit FormDU;
interface
type
TFormD = class(TForm)
Edit1: TEdit;
fcShapeBtn1: TfcShapeBtn;
.............
.............
public
class function execute:boolean;
..........
..........
end;
var
FormD: TFormD;
MyList:TDictionary<string,TForm>;
implementation
class function TFormD.execute:boolean;
begin
FormD:= TFormD.Create(nil);
MyList:= TDictionary<string,TForm>.create;
MyList.Add('FormA',TFormA);
MyList.Add('FormB',TFormB);
MyList.Add('FormC',TFormC);
FormD.showmodal;
end;
procedure TFormD.fcShapeBtn1Click(Sender: TObject);
begin
// Here I check whether the text in Edit1 box has the value of one of the
// keys that are included in the MyList dictionary and if yes I want to
// trigger the class function execute of the appropriate form...
if MyList.ContainsKey(Edit1.text) then // suppose that text= formA
MyList.Items[Edit1.text].execute // which doesn't work....
// I thought that the 'Items' method of the dictionary would return back
// to me the appropriate form type - which is connected to the specific
// key - and thus I could call each form's class function execute()
end;
I don't know how to solve this problem.
There are two problems with your approach:
your TDictionary
is declared to hold TForm
object pointers, but your code is trying to insert TForm
-derived class types instead. That will not compile.
Your Form classes do not derive from a common base class that has an Execute()
method for them to override. So you can't just retrieve a value from your TDictionary
and call Execute
directly. You would have to resort to using RTTI to find and invoke Execute()
instead.
There are some possible ways to address this:
derive your Form classes from a common base class, and store derivatives of that base class in your TDictionary
:
unit FormBaseU;
interface
uses
Forms;
type
TFormBase = class(TForm)
public
class function Execute: Boolean; virtual;
end;
TFormBaseClass = class of TFormBase;
implementation
class function TFormBase.Execute: Boolean;
begin
Result := False;
end;
end.
unit FormDU;
interface
uses
..., FormBaseU;
type
TFormD = class(TFormBase)
Edit1: TEdit;
fcShapeBtn1: TfcShapeBtn;
...
public
class function Execute: Boolean; override;
...
end;
var
FormD: TFormD;
MyList: TDictionary<string, TFormBaseClass>;
implementation
uses
FormAU, FormBU, FormCU;
class function TFormD.Execute: Boolean;
begin
MyList := TDictionary<string, TFormBaseClass>.Create;
// make sure TFormA, TFormB, and TFormC all derive
// from TFormBase and override Execute() ...
MyList.Add('FormA', TFormA);
MyList.Add('FormB', TFormB);
MyList.Add('FormC', TFormC);
FormD := TFormD.Create(nil);
FormD.ShowModal;
FormD.Free;
end;
procedure TFormD.fcShapeBtn1Click(Sender: TObject);
var
FormClass: TFormBaseClass;
begin
if MyList.TryGetValue(Edit1.Text, FormClass) then
FormClass.Execute;
end;
do something similar but using an interface
instead of a base class (this only works with objects, not class types, though):
unit MyIntfU;
interface
type
IMyIntf = interface
['{41BEF2B6-C27F-440E-A88B-9E5CF8840034}']
function Execute: Boolean;
end;
implementation
end.
unit FormDU;
interface
uses
..., MyIntfU;
type
TFormD = class(TForm, MyIntf)
Edit1: TEdit;
fcShapeBtn1: TfcShapeBtn;
...
public
function Execute: Boolean;
...
end;
var
FormD: TFormD;
MyList: TDictionary<string, TForm>;
implementation
uses
FormAU, FormBU, FormCU;
function TFormD.Execute: Boolean;
begin
MyList := TDictionary<string, TForm>.Create;
// make sure TFormA, TFormB, and TFormC are all
// instantiated beforehand and implement IMyIntf ...
MyList.Add('FormA', FormA);
MyList.Add('FormB', FormB);
MyList.Add('FormC', FormC);
FormD := TFormD.Create(nil);
FormD.ShowModal;
FormD.Free;
end;
procedure TFormD.fcShapeBtn1Click(Sender: TObject);
var
Form: TForm;
Intf: IMyIntf;
begin
if MyList.TryGetValue(Edit1.Text, Form) then
begin
if Supports(Form, IMyIntf, Intf) then
Intf.Execute;
end;
end;
don't store classes/objects in your TDictionary
at all, store the actual class methods instead:
unit FormDU;
interface
uses
...;
type
TFormD = class(TForm)
Edit1: TEdit;
fcShapeBtn1: TfcShapeBtn;
...
public
class function Execute: Boolean;
...
end;
TMyClassMethod = function: Boolean of object;
var
FormD: TFormD;
MyList: TDictionary<string, TMyClassMethod>;
implementation
uses
FormAU, FormBU, FormCU;
class function TFormD.Execute: Boolean;
begin
MyList := TDictionary<string, TMyClassMethod>.Create;
MyList.Add('FormA', TFormA.Execute);
MyList.Add('FormB', TFormB.Execute);
MyList.Add('FormC', TFormC.Execute);
FormD := TFormD.Create(nil);
FormD.ShowModal;
FormD.Free;
end;
procedure TFormD.fcShapeBtn1Click(Sender: TObject);
var
Meth: TMyClassMethod;
begin
if MyList.TryGetValue(Edit1.Text, Meth) then
Meth;
end;