![]() |
TCollectionItem mit einer Collection - Wie?
Hallo,
ich habe bei einer Komponente eine Collection als Published-Property definiert. Im Objekt-Inspector lässt sich die Collection auch öffnen und bearbeiten. Nun brauche ich in dem CollectionItem dieser Collection wiederrum eine Collection (selbe Collection). Im Objekt-Inspector sehe ich bei dem CollectionItem auch die published Eigenschaft Childs. Aber wenn ich drauf klicke geht kein Editor auf. Ich kann diese Collection nicht via IDE bearbeiten. Ich habe aber schon bei anderen Komponente gesehen das dies möglich ist. z. B. hat TDataSet eine Property FieldDefs (Typ: TFieldDefs = TOwnedCollection). Die Items dieser Collection (TFieldDef) enthalten eine Property ChildDefs (TFieldDefs). Der Code dieser Komponente habe ich auch als "Vorlage" für folgenden Code verwendet.
Delphi-Quellcode:
Leider kann ich die Childs nicht via OI ändern. Wisst ihr was ich falsch mache? Wie bekomme ich das hin?
unit Component1;
interface uses SysUtils, Classes, ContNrs, WideStrings; type TTestCollectionItem = class; TTestCollection = class(TOwnedCollection) private FParentItem: TTestCollectionItem; function GetTestItem(Index: Integer): TTestCollectionItem; procedure SetTestItem(Index: Integer; Value: TTestCollectionItem); protected procedure SetItemName(AItem: TCollectionItem); override; public constructor Create(AOwner: TPersistent); virtual; procedure GetItemNames(List: Tstrings); overload; procedure GetItemNames(List: TWideStrings); overload; property TestItems[Index: Integer]: TTestCollectionItem read GetTestItem write SetTestItem; default; property ParentItem: TTestCollectionItem read FParentItem; end; TTestCollectionItem = class(TCollectionItem) private FName: String; FText: String; FChilds: TTestCollection; procedure SetChilds(Value: TTestCollection); protected function GetDisplayName: string; override; procedure SetDisplayName(const Value: string); reintroduce; public constructor Create(Owner: TTestCollection; const Name: string); reintroduce; overload; virtual; destructor Destroy; override; procedure Assign(Source: TPersistent); override; function HasChilds: Boolean; published property Text: String read FText write FText; property Childs: TTestCollection read FChilds write SetChilds stored HasChilds; property Name: string read FName write SetDisplayName; end; TComponent1 = class(TComponent) private FRoot: TTestCollection; protected procedure SetRoot(Value: TTestCollection); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure AssignTo(Dest: TPersistent); override; published property Root: TTestCollection read FRoot write SetRoot; end; procedure Register; implementation procedure Register; begin RegisterComponents('Samples', [TComponent1]); end; constructor TTestCollection.Create(AOwner: TPersistent); begin FParentItem := nil; if AOwner is TTestCollectionItem then FParentItem := TTestCollectionItem(AOwner); inherited Create(AOwner, TTestCollectionItem); end; procedure TTestCollection.GetItemNames(List: TWideStrings); var I: Integer; begin List.BeginUpdate; try List.Clear; for I := 0 to Count - 1 do with TTestCollectionItem(Items[I]) do if Name <> '' then List.Add(Name); finally List.EndUpdate; end; end; procedure TTestCollection.GetItemNames(List: TStrings); var wList: TWIdeStringList; begin wList := TWIdeStringList.Create; try GetItemNames(wList); List.Assign(wList); finally wList.Free; end; end; function TTestCollection.GetTestItem(Index: Integer): TTestCollectionItem; begin Result := TTestCollectionItem(inherited Items[Index]); end; procedure TTestCollection.SetTestItem(Index: Integer; Value: TTestCollectionItem); begin inherited Items[Index] := Value; end; procedure TTestCollection.SetItemName(AItem: TCollectionItem); begin inherited SetItemName(AItem); // if Value is TTestCollectionItem then // begin // if TTestCollectionItem(AItem).Name = '' then // TTestCollectionItem(AItem).Name := Copy(ClassName, 2, 5) + IntToStr(ID + 1); // end; end; // ----------- constructor TTestCollectionItem.Create(Owner: TTestCollection; const Name: string); begin FName := Name; inherited Create(Owner); FChilds := TTestCollection.Create(Self); end; destructor TTestCollectionItem.Destroy; begin inherited Destroy; FChilds.Free; end; function TTestCollectionItem.HasChilds: Boolean; begin Result := (FChilds <> nil) and (FChilds.Count > 0); end; procedure TTestCollectionItem.Assign(Source: TPersistent); var I: Integer; S: TTestCollectionItem; begin if Source is TTestCollectionItem then begin if Collection <> nil then Collection.BeginUpdate; try S := TTestCollectionItem(Source); Name := S.Name; Text := S.Text; if HasChilds then Childs.Clear; if S.HasChilds then for I := 0 to S.Childs.Count - 1 do Childs.Add.Assign(S.Childs[I]); finally if Collection <> nil then Collection.EndUpdate; end; end else inherited; end; function TTestCollectionItem.GetDisplayName: string; begin Result := FName; end; procedure TTestCollectionItem.SetDisplayName(const Value: string); begin FName := Value; inherited SetDisplayName(Value); end; procedure TTestCollectionItem.SetChilds(Value: TTestCollection); begin FChilds.Assign(Value); end; // ------------------ constructor TComponent1.Create(AOwner: TComponent); begin inherited Create(AOwner); FRoot := TTestCollection.Create(Self); end; destructor TComponent1.Destroy; begin Freeandnil(FRoot); inherited Destroy; end; procedure TComponent1.AssignTo(Dest: TPersistent); begin if Dest is TComponent1 then begin TComponent1(Dest).FRoot.Assign(FRoot); end else inherited AssignTo(Dest); end; procedure TComponent1.SetRoot(Value: TTestCollection); begin FRoot.Assign(Value); end; end. |
AW: TCollectionItem mit einer Collection - Wie?
Ein paar Kleinigkeiten vorweg:
So, nun aber zu deinem Problem, das auch mit dem 3. Punkt zusammen hängt: Der Design Editor erzeugt Collection-Items immer über den virtuellen Konstruktor
Delphi-Quellcode:
.
constructor Create(Collection: TCollection);
Wenn du diesen überschreibst, dann weiß die Collection immer, wie sie ein neues Item erzeugen kann (nämlich mit genau jenem Konstruktor). Wenn du ihn allerdings nicht überschreibst (
Delphi-Quellcode:
), sondern neu einführst (
override
Delphi-Quellcode:
oder nichts explizites) oder überlädst (
reintroduce
Delphi-Quellcode:
), dann kann die Collection diesen ja nicht aufrufen, weil sie ihn (und seine Parameter) nicht kennt. Somit wird er nicht aufgerufen, was dazu führt, dass die Collection für
overload
Delphi-Quellcode:
niemals erzeugt wird und somit auch im OI nicht zur Verfügung steht. Du musst die Methode
Childs
Delphi-Quellcode:
also überschreiben. Deshalb darfst du ihre Signatur nicht ändern, sie muss also folgendermaßen deklariert werden:
TTestCollectionItem.Create
Delphi-Quellcode:
.
constructor Create(Collection: TCollection); override;
Wenn du das unter keinen Umständen willst, musst du die Collectionklasse so abändern, dass sie den neuen Konstruktor anstelle des alten verwendet. |
AW: TCollectionItem mit einer Collection - Wie?
Hallo,
Zitat:
Delphi-Quellcode:
Ohne das
TTestCollectionItem = class(TCollectionItem)
... public constructor Create(Collection: TCollection); overload; override; constructor Create(Owner: TTestCollection; const Name: string); reintroduce; overload; virtual; ... end; ... constructor TTestCollectionItem.Create(Owner: TTestCollection; const Name: string); begin FName := Name; inherited Create(Owner); FChilds := TTestCollection.Create(Self); end; constructor TTestCollectionItem.Create(Collection: TCollection); begin inherited Create(Collection); FChilds := TTestCollection.Create(Self); end;
Delphi-Quellcode:
gibt es die Warnung "[dcc32 Warnung] Dp199898Unit1.pas(37): W1010 Methode 'Create' verbirgt virtuelle Methode vom Basistyp 'TCollectionItem'", welche bei mir ein Fehler darstellt (und in den Projekt-Optionen immer auf Fehler eingestellt ist). Denn wenn der Compiler die Warnung bringt, hat der Entwickler was vergessen. Mitunter, dass die Elternklasse eine virtuelle Methode mit dem Name hat. Bei der Fehlersuche kann man richtig viel Zeit versenken.
reintroduce;
Ohne
Delphi-Quellcode:
gibts direkt einen Fehler.
overload
Und das
Delphi-Quellcode:
braucht man, wenn in einer Kindklasse dieser Constructor überschrieben werden soll.
virtual
|
AW: TCollectionItem mit einer Collection - Wie?
Zitat:
Außerdem könntest Du mit gutem Gewissen <inherited;> schreiben in dem Glauben, daß auch tatsächlich etwas passiert - der Compiler akzeptiert das, aber wenn es gar keine Elternmethode gibt (oder sie dort abstrakt ist), dann wird nichts aufgerufen (auch kein Code erzeugt) und Du merkst nichts davon. |
AW: TCollectionItem mit einer Collection - Wie?
Zitat:
Zitat:
Delphi-Quellcode:
bzw.
constructor TTestCollection.Create(AOwner: TPersistent);
begin FParentItem := nil;
Delphi-Quellcode:
Auch das habe ich von TFieldDefs kopiert. Wird aber natürlich im "richtigen" Code nicht so gemacht. Ich habe einfach die TFieldDefs als Vorlage benutzt und möglichst viel "Original" belassen.
constructor TTestCollectionItem.Create(Owner: TTestCollection;
const Name: string); begin FName := Name; inherited Create(Owner); FChilds := TTestCollection.Create(Self); end; Zitat:
Vielen Dank für eure Antworten :-) Ich werde den Code überarbeiten und melde mich dann wieder. |
AW: TCollectionItem mit einer Collection - Wie?
Ich habe nun alle Änderungen durchgeführt. Leider öffnet sich der Collection-Editor bei den Childs-Properties weiterhin nicht.
Als Workaround habe ich jedem CollectionItem eine Property ChildHolder gegeben (von TComponent abgeleitet). Diese Klasse hat eine published-Eigenschaft Childs (TTestCollection). Bei dieser Eigenschaft wird der Collection-Editor angezeigt. Aber ich muss sämtlichen TTestCollectonItems, TTestCollectionChildHolder und TTestCollectons das Root (also TComponent1) übergeben. Wenn ich TTestCollectionChildHolder mit nil als Owner initialisiere, wird das Objekt nicht im Object-Inspector angezeigt. Ist ein ziemliches Gefrickel. Gefällt mir nicht wirklich. Habt Ihr noch eine Idee was ich machen könnte? |
AW: TCollectionItem mit einer Collection - Wie?
Zitat:
|
AW: TCollectionItem mit einer Collection - Wie?
Hier nochmal der Quelltext mit der notwendigen Änderung von mir vorgenommen, funktioniert einwandfrei:
Delphi-Quellcode:
unit Component1;
interface uses SysUtils, Classes, ContNrs, WideStrings; type TTestCollectionItem = class; TTestCollection = class(TOwnedCollection) private FParentItem: TTestCollectionItem; function GetTestItem(Index: Integer): TTestCollectionItem; procedure SetTestItem(Index: Integer; Value: TTestCollectionItem); protected procedure SetItemName(AItem: TCollectionItem); override; public constructor Create(AOwner: TPersistent); virtual; procedure GetItemNames(List: Tstrings); overload; procedure GetItemNames(List: TWideStrings); overload; property TestItems[Index: Integer]: TTestCollectionItem read GetTestItem write SetTestItem; default; property ParentItem: TTestCollectionItem read FParentItem; end; TTestCollectionItem = class(TCollectionItem) private FName: String; FText: String; FChilds: TTestCollection; procedure SetChilds(Value: TTestCollection); protected function GetDisplayName: string; override; procedure SetDisplayName(const Value: string); reintroduce; public constructor Create(Collection: TCollection); override; // <-- HIER destructor Destroy; override; procedure Assign(Source: TPersistent); override; function HasChilds: Boolean; published property Text: String read FText write FText; property Childs: TTestCollection read FChilds write SetChilds stored HasChilds; property Name: string read FName write SetDisplayName; end; TComponent1 = class(TComponent) private FRoot: TTestCollection; protected procedure SetRoot(Value: TTestCollection); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure AssignTo(Dest: TPersistent); override; published property Root: TTestCollection read FRoot write SetRoot; end; procedure Register; implementation procedure Register; begin RegisterComponents('Samples', [TComponent1]); end; constructor TTestCollection.Create(AOwner: TPersistent); begin FParentItem := nil; if AOwner is TTestCollectionItem then FParentItem := TTestCollectionItem(AOwner); inherited Create(AOwner, TTestCollectionItem); end; procedure TTestCollection.GetItemNames(List: TWideStrings); var I: Integer; begin List.BeginUpdate; try List.Clear; for I := 0 to Count - 1 do with TTestCollectionItem(Items[I]) do if Name <> '' then List.Add(Name); finally List.EndUpdate; end; end; procedure TTestCollection.GetItemNames(List: TStrings); var wList: TWIdeStringList; begin wList := TWIdeStringList.Create; try GetItemNames(wList); List.Assign(wList); finally wList.Free; end; end; function TTestCollection.GetTestItem(Index: Integer): TTestCollectionItem; begin Result := TTestCollectionItem(inherited Items[Index]); end; procedure TTestCollection.SetTestItem(Index: Integer; Value: TTestCollectionItem); begin inherited Items[Index] := Value; end; procedure TTestCollection.SetItemName(AItem: TCollectionItem); begin inherited SetItemName(AItem); // if Value is TTestCollectionItem then // begin // if TTestCollectionItem(AItem).Name = '' then // TTestCollectionItem(AItem).Name := Copy(ClassName, 2, 5) + IntToStr(ID + 1); // end; end; // ----------- constructor TTestCollectionItem.Create(Collection: TCollection); begin //FName := Name; inherited Create(Collection); FChilds := TTestCollection.Create(Self); end; destructor TTestCollectionItem.Destroy; begin inherited Destroy; FChilds.Free; end; function TTestCollectionItem.HasChilds: Boolean; begin Result := (FChilds <> nil) and (FChilds.Count > 0); end; procedure TTestCollectionItem.Assign(Source: TPersistent); var I: Integer; S: TTestCollectionItem; begin if Source is TTestCollectionItem then begin if Collection <> nil then Collection.BeginUpdate; try S := TTestCollectionItem(Source); Name := S.Name; Text := S.Text; if HasChilds then Childs.Clear; if S.HasChilds then for I := 0 to S.Childs.Count - 1 do Childs.Add.Assign(S.Childs[I]); finally if Collection <> nil then Collection.EndUpdate; end; end else inherited; end; function TTestCollectionItem.GetDisplayName: string; begin Result := FName; end; procedure TTestCollectionItem.SetDisplayName(const Value: string); begin FName := Value; inherited SetDisplayName(Value); end; procedure TTestCollectionItem.SetChilds(Value: TTestCollection); begin FChilds.Assign(Value); end; // ------------------ constructor TComponent1.Create(AOwner: TComponent); begin inherited Create(AOwner); FRoot := TTestCollection.Create(Self); end; destructor TComponent1.Destroy; begin Freeandnil(FRoot); inherited Destroy; end; procedure TComponent1.AssignTo(Dest: TPersistent); begin if Dest is TComponent1 then begin TComponent1(Dest).FRoot.Assign(FRoot); end else inherited AssignTo(Dest); end; procedure TComponent1.SetRoot(Value: TTestCollection); begin FRoot.Assign(Value); end; end. |
AW: TCollectionItem mit einer Collection - Wie?
Mit deinem Code funktioniert es einwandfrei. Da hab ich wohl irgendwo einen kleinen Tipp-Fehler. Vielen Dank :-)
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:40 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz