【问题标题】:Wrong display of properties under component组件下属性显示错误
【发布时间】:2017-09-24 21:45:16
【问题描述】:

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

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 不包含任何属性,即使我定义为仅发布Select,Insert

我做错了什么?

【问题讨论】:

    标签: delphi components


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

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

      然而,尽管TFieldsTFieldsItem 对象的集合,您还没有声明一个属性来公开直接 访问您的TFieldsItem 对象指针(尽管您已经声明了getter/setter这种属性的方法)。您从TCollection 继承了基本Items[] 属性,并且该属性公开了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 派生它。

    【讨论】:

    • 是的,它现在可以工作了!这也是逻辑......还有很多东西要学......谢谢雷米!
    【解决方案2】:

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

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

    关于你的第二个问题:

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

    在您的TCustomSqlCommands 中,您已经发布了这些属性,并且在TFieldsSqlCommands 中您再次发布了它们。我不知道这是否有问题,但也许您可以尝试在TCustomSqlCommands 中公开它们,并将已发布的保留在TFieldsSqlCommands 中。但是,由于它们的可见性,您仍然应该能够在代码中访问它们。

    【讨论】:

    • 现在,在我看到 Remy 的回答后,我明白你的回答 Serraniel ;)
    猜你喜欢
    • 1970-01-01
    • 2021-03-06
    • 2020-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-24
    • 2019-02-25
    相关资源
    最近更新 更多