0

I've been fighting this crazy problem for hours and have gotten nowhere. I have this problem in two completely different projects using a TCollection. When a new collection item is added, I need to initialize the values of that item. However, they're not defaulting at all. I'm even setting them in two completely different places, in the item's constructor, and in the collection's add function - neither of them are working. I can set the values once the items are there, but I need to set default values. I've done collections in the past and never had this problem, I must be missing something here...

unit JDGrids;

interface

uses
  Classes, Windows, SysUtils, Grids, StrUtils;

type
  TJDGridCol = class;
  TJDGridCols = class;  

  TJDGridCols = class(TCollection)
  private
    fOnEvent: TNotifyEvent;
  private
    fOwner: TComponent;
    procedure DoEvent;
    property OnEvent: TNotifyEvent read fOnEvent write fOnEvent;
  protected
    function GetItem(Index: Integer): TJDGridCol;
    procedure SetItem(Index: Integer; Value: TJDGridCol);
    function GetOwner: TPersistent; override;
  public
    constructor Create(AOwner: TComponent);
    destructor Destroy; override;
    function Add: TJDGridCol;
    procedure Assign(Source: TPersistent); override;
    procedure Clear;
    procedure Delete(Index: Integer);
    property Items[Index: Integer]: TJDGridCol read GetItem write SetItem; default;
  end;

  TJDGridCol = class(TCollectionItem)
  private
    fOwner: TComponent;
    fWidth: Integer;
    fTitle: String;
    fCols: TJDGridCols;
    fOnEvent: TNotifyEvent;
    fVisible: Bool;
    procedure SetTitle(const Value: String);
    procedure SetWidth(const Value: Integer);
    procedure DoEvent;
    property OnEvent: TNotifyEvent read fOnEvent write fOnEvent;
    procedure SetVisible(const Value: Bool);
  protected
    function GetDisplayName: String; override;
  public
    constructor Create(AOwner: TJDGridCols);
    destructor Destroy; override;
  published
    property Title: String read fTitle write SetTitle;
    property Width: Integer read fWidth write SetWidth;
    property Visible: Bool read fVisible write SetVisible;
  end;

implementation

{ TJDGridCols }

constructor TJDGridCols.Create(AOwner: TComponent);
begin
  inherited Create(TJDGridCol);
  fOwner:= AOwner;
end;

destructor TJDGridCols.Destroy;
begin

  inherited Destroy;
end;

function TJDGridCols.Add: TJDGridCol;
begin
  Result:= TJDGridCol(inherited Add);
  Result.fCols:= Self;
  Result.fTitle:= 'Column '+IntToStr(Result.ID);
  Result.fWidth:= 30;
  Result.fVisible:= True;
  DoEvent;
end;

procedure TJDGridCols.Assign(Source: TPersistent);
begin
  inherited Assign(Source);
  DoEvent;
end;

procedure TJDGridCols.Clear;
begin
  inherited Clear;
  DoEvent;
end;

procedure TJDGridCols.Delete(Index: Integer);
begin
  inherited Delete(Index);
  DoEvent;
end;

function TJDGridCols.GetItem(Index: Integer): TJDGridCol;
begin
  Result:= TJDGridCol(inherited Items[Index]);
end;

function TJDGridCols.GetOwner: TPersistent;
begin
  Result:= fOwner;
end;

procedure TJDGridCols.SetItem(Index: Integer; Value: TJDGridCol);
begin
  inherited Items[Index]:= Value;
  DoEvent;
end;

procedure TJDGridCols.DoEvent;
begin
  if assigned(fOnEvent) then fOnEvent(Self);
end;

{ TJDGridCol }

constructor TJDGridCol.Create(AOwner: TJDGridCols);
begin
  inherited Create(AOwner);
  fOwner:= AOwner.fOwner;
  fCols:= AOwner;
  fTitle:= 'Column '+IntToStr(ID);
  fWidth:= 30;
  fVisible:= True;
end;

destructor TJDGridCol.Destroy;
begin

  inherited Destroy;
end;

procedure TJDGridCol.DoEvent;
begin
  if assigned(fOnEvent) then fOnEvent(Self);
end;

function TJDGridCol.GetDisplayName: String;
begin
  Result:= fTitle;
end;

procedure TJDGridCol.SetTitle(const Value: String);
begin
  fTitle:= Value;
  DoEvent;
end;

procedure TJDGridCol.SetVisible(const Value: Bool);
begin
  fVisible := Value;  
  DoEvent;
end;

procedure TJDGridCol.SetWidth(const Value: Integer);
begin
  fWidth := Value;
  DoEvent;
end;

end.
menjaraz
  • 7,551
  • 4
  • 41
  • 81
Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327

1 Answers1

2

You are not overriding the constructor of TCollection, so TCollection.Add() cannot call your constructor. That is why you needed to have Add() set the default values.

Even then, you are setting default property values during construction, but you are not specifying those same default values in your property declarations. You need to do so for DFM streaming to work correctly.

You should also use TOwnedCollection instead of using TCollection directly. Let TOwnedCollection manage the Owner for you.

Try this:

unit JDGrids;

interface

uses
  Classes, Windows, SysUtils, Grids, StrUtils;

type
  TJDGridCol = class;

  TJDGridCols = class(TOwnedCollection)
  private
    fOnEvent: TNotifyEvent;
  private
    procedure DoEvent;
    property OnEvent: TNotifyEvent read fOnEvent write fOnEvent;
  protected
    function GetItem(Index: Integer): TJDGridCol;
    procedure SetItem(Index: Integer; Value: TJDGridCol);
  public
    constructor Create(AOwner: TComponent); reintroduce;
    destructor Destroy; override;
    function Add: TJDGridCol; reintroduce;
    procedure Assign(Source: TPersistent); override;
    procedure Clear; reintroduce;
    procedure Delete(Index: Integer); reintroduce;
    property Items[Index: Integer]: TJDGridCol read GetItem write SetItem; default;
  end;

  TJDGridCol = class(TCollectionItem)
  private
    fWidth: Integer;
    fTitle: String;
    fOnEvent: TNotifyEvent;
    fVisible: Bool;
    procedure SetTitle(const Value: String);
    procedure SetWidth(const Value: Integer);
    procedure DoEvent;
    procedure SetVisible(const Value: Bool);
    property OnEvent: TNotifyEvent read fOnEvent write fOnEvent;
  protected
    function GetDisplayName: String; override;
    function GetCols: TJDGridCols;
    function GetOwner: TComponent;
  public
    constructor Create(AOwner: TCollection); override;
    destructor Destroy; override;
  published
    property Title: String read fTitle write SetTitle;
    property Width: Integer read fWidth write SetWidth default 30;
    property Visible: Bool read fVisible write SetVisible default True;
  end;

implementation

{ TJDGridCols }

constructor TJDGridCols.Create(AOwner: TComponent);
begin
  inherited Create(AOwner, TJDGridCol);
end;

destructor TJDGridCols.Destroy;
begin
  inherited Destroy;
end;

function TJDGridCols.Add: TJDGridCol;
begin
  Result := TJDGridCol(inherited Add);
  DoEvent;
end;

procedure TJDGridCols.Assign(Source: TPersistent);
begin
  inherited Assign(Source);
  DoEvent;
end;

procedure TJDGridCols.Clear;
begin
  inherited Clear;
  DoEvent;
end;

procedure TJDGridCols.Delete(Index: Integer);
begin
  inherited Delete(Index);
  DoEvent;
end;

function TJDGridCols.GetItem(Index: Integer): TJDGridCol;
begin
  Result:= TJDGridCol(inherited Items[Index]);
end;

procedure TJDGridCols.SetItem(Index: Integer; Value: TJDGridCol);
begin
  inherited SetItems(Index, Value);
  DoEvent;
end;

procedure TJDGridCols.DoEvent;
begin
  if Assigned(fOnEvent) then fOnEvent(Self);
end;

{ TJDGridCol }

constructor TJDGridCol.Create(AOwner: TCollection);
begin
  inherited Create(AOwner);
  fTitle := 'Column ' + IntToStr(ID);
  fWidth := 30;
  fVisible := True;
end;

destructor TJDGridCol.Destroy;
begin
  inherited Destroy;
end;

procedure TJDGridCol.DoEvent;
begin
  if Assigned(fOnEvent) then fOnEvent(Self);
end;

function TJDGridCol.GetDisplayName: String;
begin
  Result := fTitle;
end;

function TJDGridCol.GetCols: TJDGridCols;
begin
  Result := Collection as TJDGridCols;
end;

function TJDGridCol.GetOwner: TComponent;
begin
  Result := GetCols.GetOwner as TComponent;
end;

procedure TJDGridCol.SetTitle(const Value: String);
begin
  if fTitle <> Value then
  begin
    fTitle := Value;
    DoEvent;
  end;
end;

procedure TJDGridCol.SetVisible(const Value: Bool);
begin
  if fVisible <> Value then
  begin
    fVisible := Value;  
    DoEvent;
  end;
end;

procedure TJDGridCol.SetWidth(const Value: Integer);
begin
  if fWidth <> Value then
  begin
    fWidth := Value;
    DoEvent;
  end;
end;

end.
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • HA! It's working! I've never ever heard of this "reintroduce" nor the "TOwnedCollection", but as long as it works, who cares. Thank you. – Jerry Dodge Nov 14 '11 at 09:11
  • Just one question: I had an issue a while back putting a TCollection within another TCollectionItem. Adding the GetOwner override fixed that problem. Can I expect these changes to work properly having a TOwnedCollection inside another TCollectionItem? – Jerry Dodge Nov 14 '11 at 09:36
  • 1
    I used `reintroduce` because you are changing the signature of existing methods that are not virtual, so you should hide them as you cannot override them. As for `TOwnedCollection`, it just automates what you were previously doing manually. What you had before in regards to `Owner` pointers would have worked fine. And yes, you do have to override `GetOwner()` (which is what `TOwnedCollection` does) in order to nest collections properly. – Remy Lebeau Nov 14 '11 at 19:10
  • 1
    The core reason your original code would not work is because you were not overriding the `TCollectionItem` constructor correctly or declaring the matching defaults in the property declarations. – Remy Lebeau Nov 14 '11 at 19:10