【问题标题】:Proper way to use forms with the same name使用同名表单的正确方法
【发布时间】:2016-12-18 20:37:59
【问题描述】:

我有一个项目有两个同名的表单。我需要使用其中一个。我以为我可以使用 IFDEF 来区分它们,但我发现我不能在没有编译器抱怨的情况下将它们都添加到项目中。我的 uses 子句如下所示。

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

{$R *.dfm}

“uSDR”和“uR7000”单元中都有一个名为“Receiver”的表格。当我尝试添加“uR7000”单元时,我得到的项目是: "该项目已经包含一个名为 Receiver 的表单或模块"

如何将两个单元添加到项目中?

【问题讨论】:

  • 给他们起不同的名字
  • ...如果他们打算以类似的方式进行交互,请使用接口将其形式化。
  • 您尝试使用两种形式的同名只是给自己制造了一个问题。尽管您可以将他们的代码 IFDEF 导入到 sanme 单元中,但您不能对他们的 DFM 文件执行此操作。
  • Delphi 的“添加到项目”在涉及 $IFDEF 时并没有太多的聪明之处。如果您手动编辑 .DPR 文件(例如在记事本中),您的代码可能会与 $IFDEF 一起使用。但是一旦你这样做了,你就再也不能使用“添加到项目”了。

标签: delphi delphi-10-seattle


【解决方案1】:

首先,抱怨的不是编译器,而是 IDE。

编译器 并不关心您是否有同名的表单或其他类型,只要它们在不同的单元中即可。 VCL 中的一个著名示例是存在两种 TBitmap 类型,一种在 Graphics 中,另一种在 Windows 中。如果您需要明确说明您所指的类型,您只需在代码中限定类型名称,编译器就会按照它的指示进行操作。

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

没问题。

然而,Delphi 中的持久性框架确实关心您是否有同名的持久性类,因为持久性框架仅通过以下方式识别类型他们的不合格名。

这就是为什么 Delphi 的每个第三方组件框架都在其类名上使用前缀的原因。这不仅仅是虚荣或时尚。它确保一个库中的组件不会(通过 Delphi 持久性机制)与不同库中的另一个组件在同一个项目中使用时混淆。

底线:坚持为您的表单使用唯一名称,并在需要时找到其他方式来区分或切换它们。

如果没有有关您项目的更多详细信息,很难确切地建议如何管理您对正在使用的特定表单的引用。您可以从一个公共基类派生出两者,也可以为每个基类定义一个接口来实现。

例如(这只是一个说明性草图,不是推荐或完全可行的解决方案):

// 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.

然后,您的应用程序代码使用 uReceiver 单元(如果您想引用接口类型,例如在变量声明中,则使用 uiReceiver)并访问特定的 Receiver 通过提供的静态类实现,例如:

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;

【讨论】:

  • 感谢您的回答。它消除了我长期以来的误解。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2012-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-14
  • 1970-01-01
相关资源
最近更新 更多