I'm trying to write a function that returns either of two TForm
instances, according to the user-set configuration:
function TfrmMain.GetCurrentRamEditFrm: TForm;
{ Get the RAM Editor Form instance according to currenttly-set protocol. }
begin
if frmSetup.GetCurrentProtocol() = FooBus then
result := RAM_Editor_FooBus.frmRAM_Editor_FooBus
else
result := RAM_Editor_SXcp.frmRAM_Editor_SXcp;
end;
I need this function because this unit (Main.pas
) reads/writes a lot of variables in the RAM Editor Form.
The compiler trips up on lines like:
GetCurrentRamEditFrm().StatusBar1.Panels[1].Text := get_text(96);
with the error message: Undeclared identifier 'StatusBar1'
If I provide the TForm instance explicitely there is no error:
RAM_Editor_SXcp.frmRAM_Editor_SXcp.StatusBar1.Panels[1].Text := get_text(96);
StatusBar
is declared like this, in both Forms:
type
TfrmRAM_Editor_SXcp = class(TForm)
StatusBar1: TStatusBar; // i.e. the scope is "published"
...
Interestingly, the compiler doesn't mind the following:
GetCurrentRamEditFrm().show();
The compiler error is quite understandable because TForm
does not have a member named StatusBar1
. You introduced that in your derived forms, which I presume are of type TfrmRAM_Editor_FooBus
and TfrmRAM_Editor_SXcp
.
Now, if the two forms derive from a common base that introduces StatusBar1
you could return that common base class instead and your code would compile. This would look like this:
type
TfrmRAM_Editor_Base = class(TForm)
StatusBar1: TStatusBar;
....
end;
TfrmRAM_Editor_FooBus = class(TfrmRAM_Editor_Base)
....
end;
TfrmRAM_Editor_SXcp = class(TfrmRAM_Editor_Base)
....
end;
function TfrmMain.GetCurrentRamEditFrm: TfrmRAM_Editor_Base;
{ Get the RAM Editor Form instance according to currently-set protocol. }
begin
if frmSetup.GetCurrentProtocol() = FooBus then
result := RAM_Editor_FooBus.frmRAM_Editor_FooBus
else
result := RAM_Editor_SXcp.frmRAM_Editor_SXcp;
end;
However, that doesn't feel like a great solution. The problem I have with this is that inheritance is a very rigid mechanism, and I am not happy at all with exposing UI controls to be used outside the form itself. In realise that Delphi's streaming mechanism forces design-time controls to be published and therefore visible from the outside, but in my view that was a terrible mistake that promotes poor design.
Personally I'd define an interface that can be used to set status text.
type
ISetStatusText = interface
[...add GUID here]
procedure SetStatusText(const Value: string);
end;
Have each form implement this interface and then you can query for it using as
. Or perhaps better, have your GetCurrentRamEditFrm
function return the interface rather than the form.
This avoids your having to expose the form's UI implementation details to all and sundry.