Proper way to use forms with the same name

462 views Asked by At

I have a project that has two forms that have the same name. I need to use one or the other. I assumed I could use an IFDEF to differentiate between them but I find I can't add both of them into the project without the compiler complaining. My uses clause looks like this.

uses
uFileManager, uMount, uSkyMap, uAntenna,
{$IFDEF SDR}
 uSDR,
{$ENDIF}
{$IFDEF R7000Serial}
 uR7000,
{$ENDIF}
uDatabase;

{$R *.dfm}

Both the 'uSDR' and the 'uR7000' units have a form named 'Receiver' in them. When I attempt to add 'uR7000' unit the project I get: "The project already contains a form or module named Receiver"

How can I add both units to a project?

1

There are 1 answers

1
Deltics On BEST ANSWER

First of all, it's not the compiler complaining but the IDE.

The compiler doesn't care if you have forms - or other types - with the same name as long as they are in different units. A famous example from the VCL was the presence of two TBitmap types, one in Graphics the other in Windows. If you ever need to be explicit as to which type you mean, you simply qualify the type name in the code and the compiler does as it's told.

bmpA: Graphics.TBitmap;   // bmpA is a TBitmap as defined in the Graphics unit
bmpB: Windows.TBitmap;    // bmpB is a TBitmap as defined in the Windows unit

No problem there.

However, the persistence framework in Delphi does care if you have persistent classes with the same name since the persistence framework identifies types only by their unqualified name.

This is why every third party component framework for Delphi uses a prefix on their class names. It's not just vanity or fashion. It ensures that a component in one library cannot be confused (by Delphi persistence mechanisms) with another from a different library if both libraries are used in the same project.

Bottom line: Stick to unique names for your forms and find some other way to distinguish or switch between them if/as needed.

Precisely how to then go about managing your reference to which particular form is in use is difficult to suggest without further details about your project. You might derive both from a common base class or you might define an interface for each to implement.

For example (and this is just an illustrative sketch, not a recommendation or fully worked solution):

// Define the interface that your Receiver implementations 
//  must satisfy.  This might include returning a reference to the implementing form.
//
// e.g. in a unit "uiReceiver"

type
  IReceiver = interface
    function Form: TForm;  // returns the form using the common base type, not the specific implementation class
  end;


// in unit uSDR
TfrmSDRReceiver = class(TForm, IReceiver)
  ..implements IReceiver as well as your SDR specific needs
end;

// in unit u7000
TfrmR7000SerialReceiver = class(TForm, IReceiver)
  ..implements IReceiver as well as your R7000 Serial specific needs
end;


// In uReceiver (some unit to "resolve" the receiver)
interface

uses
  uSDR,
  uR7000;

  type
    TReceiver = class
      class function GetReceiver: IReceiver;
    end;

implementation

  class function TReceiver.GetReceiver: IReceiver;
  begin
  {$ifdef SDR}
     result := frmSDRReceiver;
  {$endif}
  {$ifdef R7000} 
     result := frmR7000SerialReceiver;
  {$endif}
  end;

end.

Your application code then uses the uReceiver unit (and uiReceiver if you want to refer to the interface type, e.g. in a variable declaration) and accesses the particular Receiver implementation through the static class provided, e.g.:

uses
  uReceiver;


implementation

  uses
    uiReceiver;


  ..
  var
    rcvr: IReceiver;
  begin
    rcvr := TReceiver.GetReceiver;

    rcvr.... // work with your receiver through the methods/properties on the interface

    // You can also work with the receiver form, accessing all aspects
    //  common to any TForm via the Form function on the interface (assuming
    //  you chose to provide one):

    rcvr.Form.Show;

    ..
  end;