0

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.

Domus
  • 1,263
  • 7
  • 23

1 Answers1

2

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;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • This is so much more than "pointing me in the right direction"! Immensely grateful, Remy, for the six hundredth time. – Domus Apr 24 '18 at 20:59
  • @Domus How can you thank him 600 times when you've only asked 25 questions? =P – Jerry Dodge Apr 24 '18 at 21:21
  • @JerryDodge I've been bugging Remy with questions since 1995, I think; long before the idea of anything resembling Stack Overflow started germinating in Jeff or Joel's brain. ;) – Domus Apr 24 '18 at 21:38
  • @RemyLebeau Would there be any problem if I changed the constructor of TBitmaps to "constructor Create(AOwner: TPersistent); reintroduce?" I need to do that to avoid incompatibilities with the rest of the application, where I need TBitmaps without it being owned by the TBitmapCollection component. – Domus Apr 24 '18 at 22:15
  • 2
    @Domus: that will work fine. The important thing is that the `ItemClass` parameter should not be exposed, as that allows the user of the collection to potentially set a class type that is not compatible with the rest of the collection's code. The collection's constructor should assign the appropriate class type internally. That is why I like to `reintroduce` a new constructor to hide the `ItemClass` parameter – Remy Lebeau Apr 24 '18 at 22:43