2

是否可以在 Delphi 7 中创建一个可以通过 Visual Form Inheritance 技术继承的抽象类?如果是这样,请提供一个例子。

任务是这样的。我想创建一个表单,作为其他两个表单的基本表单,这些表单将继承该表单的所有属性。这两个继承表单将用于向数据库添加新内容(例如,创建产品项)和编辑该内容。所以,我猜应该将基本表单视为一个抽象类,它应该有好的和取消按钮以及所有继承类将共享的东西。好吧,它显然是一个抽象类,因为除了作为其他表单的基础之外,没有其他用途。

这是一个简单的图表,可以更清楚地说明这一点:

在此处输入图像描述

4

4 回答 4

2

您使用了abstract一词,但阅读您的问题,我严重怀疑您是否真的按照 Delphi 中定义的方式来表示。我认为您的意思是一般简单的抽象术语:您想要设计一个包含必须由后代更改或添加的部分的表单。Delphi 中的抽象方法意味着没有实现的类例程。但这并不重要,因为完全有可能设计一个基本形式,有或没有抽象方法。

您可以创建如下图/图表中所示的设置:

  • 设计一个TBaseForm包含 2 个编辑、2 个标签、2 个按钮和 1 个动作列表的表单,
  • 向 ActionList 添加 3 个操作:创建、保存和取消,
  • 提前将 Cancel 动作分配给 CancelButton.Action,
  • 保存表格,
  • 使用菜单命令设计一个继承自 TBaseForm 的新表单:File > New > Other > [Project Name] > BaseForm
  • 您将拥有一个包含编辑、标签、按钮和操作的新表单,
  • 将 Save 操作分配给另一个按钮的 action 属性,
  • 给它一个“编辑项目”标题,
  • 保存表单,并为“创建新项目”表单重复此操作。

如果您愿意,基本形式可能具有抽象方法。当你在运行时创建一个 TBaseForm 实例时,编译器会给出一个警告,它正在构建包含抽象方法 'TBaseForm.MethodName' 的 'TBaseForm' 实例。它仍然是一个警告,直到您在运行时调用将产生抽象错误的方法。创建一个实现该方法的后代表单,则不会有警告。在设计器中使用抽象方法创建表单不会产生警告。但是,运行时错误仍然可能发生。

于 2013-04-10T17:24:30.500 回答
2

不,您不能在 Delphi Visual Form Inheritance 中创建严格的 Delphi 单词“abstract”意义的“abstract”基本表单。

但是,根据您的描述,听起来您实际上并不需要严格抽象的基本形式。您根本没有提到定义抽象方法的要求。

听起来您只需要一个基本表单,您可以从中创建多个不同专业的后代,这些后代可以与基本表单共享 UI 和实现。

这就是 VFI 的用途,所以是的,您可以这样做。

于 2013-04-10T17:47:04.133 回答
2

首先我们需要定义抽象类的含义。在我看来,有两个相互竞争的定义:

  1. 抽象类是不能被实例化的。这是最常用的定义。
  2. 抽象类是包含多个抽象方法的类。

由于 Delphi 没有执行定义 1 的语言机制,因此定义 2 似乎是适用于该问题的定义。

这个问题的答案是包含抽象方法的类可以与可视表单继承一起使用。

现代版本的 Delphi 允许您使用abstract关键字装饰类。但是,这没有效果。您仍然可以实例化这样的类。据我了解,该abstract关键字是为了 Delphi .net 编译器的利益而添加的。

同样,在现代版本的 Delphi 中,您可以将编译器配置为将使用抽象方法的类的实例化视为编译错误。这可能是您在 Delphi 中最接近定义 1 的地方。

然而,即使这样也不完全符合定义 1,因为这些类可以通过 RTTI 或虚拟构造函数进行实例化。实例化设计组件的机制就是一个很好的例子。

以这个类为例:

type
  TForm1 = class(TForm)
  public
    procedure Boo; virtual; abstract;
  end;

即使您将W2000 Constructing instance contains abstract method的选项设置为Error,您仍然可以让框架实例化该类。仅当您编写TForm1.Create编译器对象时。

于 2013-04-10T16:52:17.000 回答
0

我已经成功创建了一个带有 BaseForm 的演示,它具有抽象方法,并且很有魅力......
我的 IDE 是 Rad Studio RIO
我的 BaseForm 代码:

unit UBaseForm;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  Vcl.ExtCtrls, Vcl.StdCtrls;

type
  TBaseForm = class(TForm)
    Lbl_IndexPage: TLabel;
    procedure Abstracted_Event(Sender: TObject); virtual; abstract;
    procedure Abstracted_Proc; virtual; abstract;
    function Abstracted_Func: string; virtual; abstract;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  procedure Get_SubForm(var Ref; AFormClass: TFormClass;
  aOwner: TComponent; aParent: TWinControl);

var
  BaseForm: TBaseForm;

implementation

{$R *.dfm}

procedure Get_SubForm(var Ref; AFormClass: TFormClass;
  aOwner: TComponent; aParent: TWinControl);
var
  Instance: TBaseForm;
begin
  if not Assigned(TBaseForm(Ref)) then
  begin
    Instance       := TBaseForm(AFormClass.NewInstance);
    TBaseForm(Ref) := Instance;
    Instance.Create(aOwner);
  end
  else Instance := TBaseForm(Ref);

  Instance.Parent      := aParent;
  Instance.Align       := alClient;
  Instance.BorderStyle := bsNone;
  Instance.OnShow      := Instance.Abstracted_Event;
  Instance.Show;
end;

end.

在我的应用程序中,我有三个表单可以继承该 BaseForm 甚至方法之上的所有属性......

unit UFirstPage;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  UBaseForm,
  Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TFrmFirstPage = class(TBaseForm)
    Pnl_1: TPanel;
    Edt_Abst_Msg_Event: TEdit;
    Pnl_2: TPanel;
    Pnl_3: TPanel;
    Btn_Get_Abstract_Func: TButton;
    Btn_Get_Abstract_Proc: TButton;
    procedure Btn_Get_Abstract_FuncClick(Sender: TObject);
    procedure Btn_Get_Abstract_ProcClick(Sender: TObject);
  published
    procedure Abstracted_Event(Sender: TObject); override;
    procedure Abstracted_Proc; override;
    function Abstracted_Func: string; override;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FrmFirstPage: TFrmFirstPage;

implementation

{$R *.dfm}

{ TFrmFirstPage }

{$REGION ' Overridden Abstract Methodes ..'}
procedure TFrmFirstPage.Abstracted_Event(Sender: TObject);
begin
  inherited;
  Pnl_1.Color := clBlue; Pnl_2.Color := clGray; Pnl_3.Color := clRed;
  Edt_Abst_Msg_Event.Text  := 'All this Properties can changed using [Abstracted_Event] | (Owner Form is: ['+ Self.ClassName +'])';
end;

function TFrmFirstPage.Abstracted_Func: string;
begin
  Result := 'I''m Just an Override of Abstracted_Func ['+ Self.ClassName +']';
end;

procedure TFrmFirstPage.Abstracted_Proc;
begin
  inherited;
  ShowMessage('I''m Just an Override of Abstracted_Proc ['+ Self.ClassName +']');
end;
{$ENDREGION}

procedure TFrmFirstPage.Btn_Get_Abstract_FuncClick(Sender: TObject);
begin
  ShowMessage(Abstracted_Func);
end;

procedure TFrmFirstPage.Btn_Get_Abstract_ProcClick(Sender: TObject);
begin
  Abstracted_Proc;
end;

end.

在此处输入图像描述

我的第二种形式:

unit USecondPage;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  UBaseForm,
  Vcl.StdCtrls;

type
  TFrmSecondPage = class(TBaseForm)
    Lbl_Abst_Msg_Event: TLabel;
    Lbl_1: TLabel;
    Lbl_2: TLabel;
    Lbl_3: TLabel;
    Btn_Do_Abst_Proc: TButton;
    Btn_Get_Abst_Func: TButton;
    procedure Btn_Do_Abst_ProcClick(Sender: TObject);
    procedure Btn_Get_Abst_FuncClick(Sender: TObject);
  published
    procedure Abstracted_Event(Sender: TObject); override;
    procedure Abstracted_Proc; override;
    function Abstracted_Func: string; override;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FrmSecondPage: TFrmSecondPage;

implementation

{$R *.dfm}

{ TFrmSecondPage }

{$REGION ' Overridden Abstract Methodes ..'}
procedure TFrmSecondPage.Abstracted_Event(Sender: TObject);
begin
  inherited;
  Lbl_1.Font.Color := clBlue; Lbl_2.Font.Color := clGray; Lbl_3.Font.Color := clRed;
  Lbl_Abst_Msg_Event.Caption := 'All this Properties can changed using [Abstracted_Event] | (Owner Form is: ['+ Self.ClassName +'])';
end;

function TFrmSecondPage.Abstracted_Func: string;
begin
  Result := 'I''m Just an Override of Abstracted_Func ['+ Self.ClassName +']';
end;

procedure TFrmSecondPage.Abstracted_Proc;
begin
  inherited;
  ShowMessage('I''m Just an Override of Abstracted_Proc ['+ Self.ClassName +']');
end;
{$ENDREGION}

procedure TFrmSecondPage.Btn_Do_Abst_ProcClick(Sender: TObject);
begin
  Abstracted_Proc;
end;

procedure TFrmSecondPage.Btn_Get_Abst_FuncClick(Sender: TObject);
begin
  ShowMessage(Abstracted_Func);
end;

end.

在此处输入图像描述

我的第三个表格:

unit UThirdPage;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  UBaseForm,
  Vcl.StdCtrls;

type
  TFrmThirdPage = class(TBaseForm)
  published
    procedure Abstracted_Event(Sender: TObject); override;
    procedure Abstracted_Proc; override;
    function Abstracted_Func: string; override;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FrmThirdPage: TFrmThirdPage;

implementation

{$R *.dfm}

{ TFrmThirdPage }

{$REGION ' Overridden Abstract Methodes ..'}
procedure TFrmThirdPage.Abstracted_Event(Sender: TObject);
begin
  inherited;
// your Code Goes Here ..
// call this methode or fill it with code Not a Mandatory :)
// The Mandatory thing is to implement this Methodes Exactly where BASEFORM HAS & without Missing any one of them from the Base Class...
// Enjoy ...
end;

function TFrmThirdPage.Abstracted_Func: string;
begin
// your Code Goes Here ..
// call this methode or fill it with code Not a Mandatory :)
// Enjoy ...
end;

procedure TFrmThirdPage.Abstracted_Proc;
begin
  inherited;
// your Code Goes Here ..
// call this methode or fill it with code Not a Mandatory :)
// Enjoy ...
end;
{$ENDREGION}

end.

在此处输入图像描述

我的主要形式:

unit UMain;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  Vcl.StdCtrls,
  Vcl.ExtCtrls,
// My Abstracted Views ..
  UFirstPage,
  USecondPage,
  UThirdPage;

type
  TFrmMain = class(TForm)
    Pnl_ToolBar: TPanel;
    Pnl_StatusBar: TPanel;
    Btn_Previous: TButton;
    Btn_Next: TButton;
    Notebook_SubForms: TNotebook;
    Pnl_First_PAGE: TPanel;
    Pnl_Second_PAGE: TPanel;
    Pnl_Third_PAGE: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure Notebook_SubFormsPageChanged(Sender: TObject);
    procedure Btn_NextClick(Sender: TObject);
    procedure Btn_PreviousClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FrmMain: TFrmMain;

implementation

uses
// My Abstracted Base Template ..
  UBaseForm;

{$R *.dfm}

procedure TFrmMain.Btn_NextClick(Sender: TObject);
begin
  case Notebook_SubForms.PageIndex of
    0:begin
        Notebook_SubForms.PageIndex := 1;
      end;
    1:begin
        Notebook_SubForms.PageIndex := 2;
      end;
    2:begin
        Notebook_SubForms.PageIndex := 0;
      end;
  end;
end;

procedure TFrmMain.Btn_PreviousClick(Sender: TObject);
begin
  case Notebook_SubForms.PageIndex of
    0:begin
        Notebook_SubForms.PageIndex := 2;
      end;
    1:begin
        Notebook_SubForms.PageIndex := 0;
      end;
    2:begin
        Notebook_SubForms.PageIndex := 1;
      end;
  end;
end;

procedure TFrmMain.FormCreate(Sender: TObject);
begin
  Get_SubForm(FrmFirstPage, TFrmFirstPage, Self, Pnl_First_PAGE);
end;

procedure TFrmMain.Notebook_SubFormsPageChanged(Sender: TObject);
begin
  case Notebook_SubForms.PageIndex of
    0:begin
        Get_SubForm(FrmFirstPage, TFrmFirstPage, Self, Pnl_First_PAGE);
        if Assigned(FrmSecondPage) then FreeAndNil(FrmSecondPage);
        if Assigned(FrmThirdPage) then FreeAndNil(FrmThirdPage);
      end;
    1:begin
        Get_SubForm(FrmSecondPage, TFrmSecondPage, Self, Pnl_Second_PAGE);
        if Assigned(FrmFirstPage) then FreeAndNil(FrmFirstPage);
        if Assigned(FrmThirdPage) then FreeAndNil(FrmThirdPage);
      end;
    2:begin
        Get_SubForm(FrmThirdPage, TFrmThirdPage, Self, Pnl_Third_PAGE);
        if Assigned(FrmSecondPage) then FreeAndNil(FrmSecondPage);
        if Assigned(FrmSecondPage) then FreeAndNil(FrmSecondPage);
      end;
  end;
end;

end.

在此处输入图像描述

当编译 0 错误和 0 警告时..
结果: 在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

从我的 Github Repo下载整个演示的链接。
Demo 也可以在 Delphi 7 中工作(我对其进行了测试):)

于 2021-09-16T23:58:02.107 回答