组件下的属性显示错误

时间:2017-04-27 08:02:33

标签: delphi components

我想创建一个结构如下的组件:

type
  TCustomMyComp = class;
  TMyComp = class;
  TCustomSqlCommands = class;
  TSqlCommands = class;
  TFields = class;
  TFieldsItem = class;
  TFieldsSqlCommands = class;

 TCustomMyComp = class(TComponent)
  private
    FFields: TFields;
    FSqlCommands: TSqlCommands;
    procedure SetFields(Value: TFields);
    procedure SetSqlCommands(Value: TSqlCommands);
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property Fields: TFields read FFields write SetFields;
    property SqlCommands: TSqlCommands read FSqlCommands write SetSqlCommands;
  end;

TCustomSqlCommands = class(TPersistent)
  private
    FOwner: TCustomMyComp;
    FSelect: TStrings;
    FInsert: TStrings;
    FUpdate: TStrings;
    FDelete: TStrings;
    procedure SetSql(Index: Integer; Value: TStrings);
  public
    constructor Create(AOwner: TCustomMyComp); virtual;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
  published
    property Select: TStrings index 0 read FSelect write SetSql;
    property Insert: TStrings index 1 read FInsert write SetSql;
    property Update: TStrings index 2 read FUpdate write SetSql;
    property Delete: TStrings index 3 read FDelete write SetSql;
  end;

[ComponentPlatformsAttribute(pidWin32 or pidWin64)]
  TMyComp = class(TCustomMyComp)
  published
    property Fields;
    property SqlCommands;
  end;

TSqlCommands = class(TCustomSqlCommands)
  published
    property Insert;
    property Update;
    property Delete;
  end;

//------- Fields -----------

  TFields = class(TCollection)
  private
    FOwner: TCustomMyComp;
    function GetItem(Index: Integer): TFieldsItem;
    procedure SetItem(Index: Integer; Value: TFieldsItem);
  protected
    function GetOwner: TPersistent; override;
    procedure Update(Item: TCollectionItem); override;
  public
    constructor Create(AOwner: TCustomMyComp);
    function Add: TFieldsItem;
    function Owner: TCustomMyComp;
  end;

  TFieldsItem = class(TCollectionItem)
  private
    FOwner: TCustomMyComp;
    FSqlCommands: TFieldsSqlCommands;
    procedure SetSqlCommands(Value: TFieldsSqlCommands);
  protected
    function GetDisplayName: String; override;
    procedure SetIndex(Value: Integer);  override;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
  published
    property SqlCommands: TFieldsSqlCommands read FSqlCommands write SetSqlCommands;
  end;

  TFieldsSqlCommands = class(TCustomSqlCommands)
  published
    property Select;
    property Insert;
  end;

我面临两个问题:

  1. 属性SqlCommands: TSqlCommands包含TCustomSqlCommands下定义的所有属性,即使我定义为仅发布Insert,Update,Delete

  2. 属性Fileds->SqlCommands: TFieldsSqlCommands包含NO属性,即使我定义为仅发布Select,Insert

  3. 我做错了什么?

2 个答案:

答案 0 :(得分:2)

  1. 典型的设计模式是让TCustom...类将其属性定义为publicprotected,但绝不定义为published。这样,派生类可以根据需要决定要将哪些属性提升为published。这将允许TSqlCommands不发布Select属性,TFieldsSqlCommands不发布UpdateDelete属性。

  2. 您的Fields属性是TFields对象(顺便说一句,VCL已在TFields单元中有DB个类,因此您应该考虑重命名上课到更独特的东西)。您的TFields类没有SqlCommands属性,而是TFieldsItem的属性。因此,您需要访问其中一个字段项才能访问其SqlCommands属性。

    但是,尽管TFieldsTFieldsItem个对象的集合,但您尚未声明属性以公开直接TFieldsItem对象指针的访问权限(尽管你已经为这样的属性声明了getter / setter方法)。您从Items[]继承了基本TCollection属性,该属性公开了TCollectionItem对象指针,您必须将其输入到TFieldItem。因此,您应该将自己的属性(将其命名为任意名称)添加到TFields类以处理该类型转换:

    TFields = class(TCollection)
    private
      ...
      function GetField(Index: Integer): TFieldsItem;
      procedure SetField(Index: Integer; Value: TFieldsItem);
      ...
    public
      ...
      property Fields[Index: Integer]: TFieldsItem read GetField write SetField default;
    end;
    

    顺便说一句,由于TFields有一个Owner,您应该考虑直接从TOwnedCollection而不是TCollection派生。

答案 1 :(得分:1)

我们是在讨论代码中的可见性吗?如果是,那么您将所有属性视为公共属性是正确的,因为已发布的属性具有相同的可见性。这两者之间的唯一区别是已发布的属性包含在RTTI中,因此可以在Object Inspector中看到它们。但在代码中,您可以平等地访问公共和已发布的属性。

因此,如果您的第一个问题来自编码方面,这是正确的行为。

关于你的第二个问题:

如果从Fields实例访问TMyCustomComp属性,则不直接拥有SqlCommands属性,因为您只能访问TFields对象,继承自TCollection。您首先必须访问其中一个项目,如果需要将其转换为TFieldsItem,然后您可以访问其SqlCommands属性。

TCustomSqlCommands中,您已发布了这些属性,并在TFieldsSqlCommands中再次发布了这些属性。我不知道这是否是一个问题,但也许您可以尝试在TCustomSqlCommands中将其公开,并将已发布的内容保存在TFieldsSqlCommands中。但是,由于其可见性,您仍然可以在代码中访问它们。