delphidelphi-2010tpersistent

Collection editor does not open for a TCollection property in a TPersistent property


I've got my custom collection property which is working great when it is a direct member of my component.

But I want to move the collection property to a TPersistent propery within my component. And now comes the problem, it doesn't work: double clicking on the collection property in the object inspector normally opens the collection editor, but it does not anymore.

Fist of all - what should I pass to the contructor of the TPersistent property?

TMyCollection = class(TCollection)
  constructor Create(AOwner: TComponent); // TMyCollection constuctor
  ...

I can't pass Self, so should I pass my persistent owner?

constructor TMyPersistent.Create(AOwner: TComponent);
begin
  inherited Create;
  fOwner := AOwner;
  fMyCollection := TMyCollection.Create(AOwner); // hmmm... doesn't make sense
end;

I think I'm missing something. If more code is needed just please comment this post.

Da visualizationz


Solution

  • A TCollection's constructor does not need a TComponent, but a TCollectionItemClass.

    Your collection now being a member of a TPersistent property instead of being a direct member of the component makes no difference for the constructor.


    Update

    What dóes differ is the ownership, but then at the TPersistent level, which should be managed by a correct implementation of GetOwner:

    GetOwner returns the owner of an object. GetOwner is used by the GetNamePath method to find the owner of a persistent object. GetNamePath and GetOwner are introduced in TPersistent so descendants such as collections can appear in the Object Inspector.

    You have to tell the IDE that your TCollection property is owned by the TPersistent property, which in turn is owned by the component.

    The tutorial you are using has several errors regarding this implementation:

    This is how it should look like:

    unit MyComponent;
    
    interface
    
    uses
      Classes, SysUtils;
    
    type
      TMyCollectionItem = class(TCollectionItem)
      private
        FStringProp: String;
      protected
        function GetDisplayName: String; override;
      public
        procedure Assign(Source: TPersistent); override;
      published
        property StringProp: String read FStringProp write FStringProp;
      end;
    
      TMyCollection = class(TCollection)
      private
        FOwner: TPersistent;
        function GetItem(Index: Integer): TMyCollectionItem;
        procedure SetItem(Index: Integer; Value: TMyCollectionItem);
      protected
        function GetOwner: TPersistent; override;
      public
        constructor Create(AOwner: TPersistent);
        function Add: TMyCollectionItem;
        function Insert(Index: Integer): TMyCollectionItem;
        property Items[Index: Integer]: TMyCollectionItem read GetItem
          write SetItem;
      end;
    
      TMyPersistent = class(TPersistent)
      private
        FOwner: TPersistent;
        FCollectionProp: TMyCollection;
        procedure SetCollectionProp(Value: TMyCollection);
      protected
        function GetOwner: TPersistent; override;
      public
        procedure Assign(Source: TPersistent); override;
        constructor Create(AOwner: TPersistent);
        destructor Destroy; override;
      published
        property CollectionProp: TMyCollection read FCollectionProp
          write SetCollectionProp;
      end;
    
      TMyComponent = class(TComponent)
      private
        FPersistentProp: TMyPersistent;
        procedure SetPersistentProp(Value: TMyPersistent);
      public
        constructor Create(AOwner: TComponent); override;
        destructor Destroy; override;
      published
        property PersistentProp: TMyPersistent read FPersistentProp
          write SetPersistentProp;
      end;
    
    procedure Register;
    
    implementation
    
    procedure Register;
    begin
      RegisterComponents('Samples', [TMyComponent]);
    end;
    
    { TMyCollectionItem }
    
    procedure TMyCollectionItem.Assign(Source: TPersistent);
    begin
      if Source is TMyCollectionItem then
        FStringProp := TMyCollectionItem(Source).FStringProp
      else
        inherited Assign(Source);
    end;
    
    function TMyCollectionItem.GetDisplayName: String;
    begin
      Result := Format('Item %d',[Index]);
    end;
    
    { TMyCollection }
    
    function TMyCollection.Add: TMyCollectionItem;
    begin
      Result := TMyCollectionItem(inherited Add);
    end;
    
    constructor TMyCollection.Create(AOwner: TPersistent);
    begin
      inherited Create(TMyCollectionItem);
      FOwner := AOwner;
    end;
    
    function TMyCollection.GetItem(Index: Integer): TMyCollectionItem;
    begin
      Result := TMyCollectionItem(inherited GetItem(Index));
    end;
    
    function TMyCollection.GetOwner: TPersistent;
    begin
      Result := FOwner;
    end;
    
    function TMyCollection.Insert(Index: Integer): TMyCollectionItem;
    begin
      Result := TMyCollectionItem(inherited Insert(Index));
    end;
    
    procedure TMyCollection.SetItem(Index: Integer; Value: TMyCollectionItem);
    begin
      inherited SetItem(Index, Value);
    end;
    
    { TMyPersistent }
    
    procedure TMyPersistent.Assign(Source: TPersistent);
    begin
      if Source is TMyPersistent then
        CollectionProp := TMyPersistent(Source).FCollectionProp
      else
        inherited Assign(Source);
    end;
    
    constructor TMyPersistent.Create(AOwner: TPersistent);
    begin
      inherited Create;
      FOwner := AOwner;
      FCollectionProp := TMyCollection.Create(Self);
    end;
    
    destructor TMyPersistent.Destroy;
    begin
      FCollectionProp.Free;
      inherited Destroy;
    end;
    
    function TMyPersistent.GetOwner: TPersistent;
    begin
      Result := FOwner;
    end;
    
    procedure TMyPersistent.SetCollectionProp(Value: TMyCollection);
    begin
      FCollectionProp.Assign(Value);
    end;
    
    { TMyComponent }
    
    constructor TMyComponent.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      FPersistentProp := TMyPersistent.Create(Self);
    end;
    
    destructor TMyComponent.Destroy;
    begin
      FPersistentProp.Free;
      inherited Destroy;
    end;
    
    procedure TMyComponent.SetPersistentProp(Value: TMyPersistent);
    begin
      FPersistentProp.Assign(Value);
    end;
    
    end.
    

    But may I say that you can also inherit from TOwnedCollection, which makes the use and the declaration of TMyCollection much simpler:

      TMyCollection = class(TOwnedCollection)
      private
        function GetItem(Index: Integer): TMyCollectionItem;
        procedure SetItem(Index: Integer; Value: TMyCollectionItem);
      public
        function Add: TMyCollectionItem;
        function Insert(Index: Integer): TMyCollectionItem;
        property Items[Index: Integer]: TMyCollectionItem read GetItem
          write SetItem;
      end;