delphitcollectiontcollectionitem

No reaction when "..." clicked on a TCollectionItem property that represents another TCollection


I've never been in a situation that needed it, and this is the first time I try to have a TCollection as TCollectionItem of another TCollection. It all compiles fine, but there is no reaction when the three dots behind the TCollectionItem's TCollection property are clicked, ie. the dialog with the list of that sub-TCollection does not appear.

I was under the impression that, since no fancy property editors should be necessary (the sub-TCollection only carries items that have a string and a single property), the IDE would pretty much handle it automatically.

Apparently that's not the case, or I'm overseeing the obvious, which is a chronic affliction.

The implementation (run-time) unit has this:

type

  TBitmapItemTag = class(TCollectionItem)
  private
    FTagName: string;
    FTagFloat: Single;
  published
    property TagName: string read FTagName write FTagName;
    property TagFloat: Single read FTagFloat write FTagFloat;
  end;

  TBitmapItemTags = class(TOwnedCollection)

  end;

  TBitmapItem = class(TCollectionItem)
  private
    FBitmap: TBitmap;
    FBitmapItemTags: TBitmapItemTags;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
  published
    property Bitmap: TBitmap read FBitmap write FBitmap;
    property Tags: TBitmapItemTags read FBitmapItemTags write FBitmapItemTags;
  end;

  TBitmaps = class(TCollection)

  end;

  TBitmapCollection = class(TComponent)
  private
    FBitmaps: TBitmaps;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Bitmaps: TBitmaps read FBitmaps write FBitmaps;
  end;

implementation

{ TBitmapItem }

constructor TBitmapItem.Create(Collection: TCollection);
begin
  inherited Create(Collection);
  FBitmap := TBitmap.Create(0, 0);
  FBitmapItemTags := TBitmapItemTags.Create(Self,TBitmapItemTag);
end;

destructor TBitmapItem.Destroy;
begin
  FBitmap.Free;
  FBitmapItemTags.Free;
  inherited;
end;

{ TBitmapCollection }

constructor TBitmapCollection.Create(AOwner: TComponent);
begin
  inherited;
  FBitmaps := TBitmaps.Create(TBitmapItem);
end;

destructor TBitmapCollection.Destroy;
begin
  FBitmaps.Free;
  inherited;
end;

The Register procedure is implemented in the design-time unit and just calls the RegisterComponents procedure. And holds some lazy RegisterPropertyEditor tries that were to no avail.

If anyone can point me to the shortest path in order for the IDE to recognize the TBitmapItemTag TCollectionItem, I'd be grateful.


Solution

  • You need to change TBitmaps to derive from TOwnedCollection instead of TCollection.

    I also suggest defining explicit constructors for TBitmapItemTags and TBitmaps.

    You also need to add some setter methods to your object-based properties, otherwise you risk memory leaks at runtime. Your setters should be calling Assign() on your objects to copy property values from one object to another. TCollection already implements Assign() for you, but you will have to implement Assign() in your collection items.

    Try this:

    type
      TBitmapItemTag = class(TCollectionItem)
      private
        FTagName: string;
        FTagFloat: Single;
      public
        procedure Assign(ASource: TPersistent); override;
      published
        property TagName: string read FTagName write FTagName;
        property TagFloat: Single read FTagFloat write FTagFloat;
      end;
    
      TBitmapItem = class;
    
      TBitmapItemTags = class(TOwnedCollection)
      public
        constructor Create(AOwner: TBitmapItem); reintroduce;
      end;
    
      TBitmapItem = class(TCollectionItem)
      private
        FBitmap: TBitmap;
        FTags: TBitmapItemTags;
        procedure SetBitmap(AValue: TBitmap);
        procedure SetTags(AValue: TBitmapItemTags);
      public
        constructor Create(Collection: TCollection); override;
        destructor Destroy; override;
        procedure Assign(ASource: TPersistent); override;
      published
        property Bitmap: TBitmap read FBitmap write SetBitmap;
        property Tags: TBitmapItemTags read FTags write SetTags;
      end;
    
      TBitmapCollection = class;
    
      TBitmaps = class(TOwnedCollection)
      public
        constructor Create(AOwner: TBitmapCollection); reintroduce;
      end;
    
      TBitmapCollection = class(TComponent)
      private
        FBitmaps: TBitmaps;
        procedure SetBitmaps(AValue: TBitmaps);
      public
        constructor Create(AOwner: TComponent); override;
        destructor Destroy; override;
      published
        property Bitmaps: TBitmaps read FBitmaps write SetBitmaps;
      end;
    

    { TBitmapTagItem }
    
    procedure TBitmapItemTag.Assign(ASource: TPersistent);
    begin
      if ASource is TBitmapItemTag then
      begin
        FTagName := TBitmapItemTag(ASource).TagName;
        FTagFloat := TBitmapItemTag(ASource).TagFloat;
      end
      else
        inherited;
    end;
    
    { TBitmapItemTags }
    
    constructor TBitmapItemTags.Create(AOwner: TBitmapItem);
    begin
      inherited Create(AOwner, TBitmapItemTag);
    end;
    
    { TBitmapItem }
    
    constructor TBitmapItem.Create(Collection: TCollection);
    begin
      inherited Create(Collection);
      FBitmap := TBitmap.Create(0, 0);
      FTags := TBitmapItemTags.Create(Self);
    end;
    
    destructor TBitmapItem.Destroy;
    begin
      FBitmap.Free;
      FTags.Free;
      inherited;
    end;
    
    procedure TBitmapItem.Assign(ASource: TPersistent);
    begin
      if ASource is TBitmapItem then
      begin
        FBitmap.Assign(TBitmapItem(ASource).Bitmap);
        FTags.Assign(TBitmapItem(ASource).Tags);
      end
      else
        inherited;
    end;
    
    procedure TBitmapItem.SetBitmap(AValue: TBitmap);
    begin
      FBitmap.Assign(AValue);
    end;
    
    procedure TBitmapItem.SetTags(AValue: TBitmapItemTags);
    begin
      FTags.Assign(AValue);
    end;
    
    { TBitmaps }
    
    constructor TBitmaps.Create(AOwner: TBitmapCollection);
    begin
      inherited Create(AOwner, TBitmapItem);
    end;
    
    { TBitmapCollection }
    
    constructor TBitmapCollection.Create(AOwner: TComponent);
    begin
      inherited;
      FBitmaps := TBitmaps.Create(Self);
    end;
    
    destructor TBitmapCollection.Destroy;
    begin
      FBitmaps.Free;
      inherited;
    end;
    
    procedure TBitmapCollection.SetBitmaps(AValue: TBitmaps);
    begin
      FBitmaps.Assign(AValue);
    end;