Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Is it sensible to use a Tobjectlist as a property? 1

Status
Not open for further replies.

enonod

Programmer
Jan 7, 2005
22
GB
I have a TObjectlist of objects (A)s that in turn have a set of related objects (B)(the number of (B)s varies for each A).
I know that I can use a property array for the (B)s but would prefer to use the built in methods etc. of TObjectlist to control the varying lists of (B)s.
Is there any disadvantage?
Can I obtain a (B) object (not the Blist) from the (A)object by using a property of the (A) object to do the indexing and then type cast internally, then present the (B) object as the result. Like two jobs in one.
At present I can only use read to return the complete (B) list as a result and then externally index and type cast to obtain the individual (B) object.
i.e.
TBlist: Class (Tobjectlist)
The following is an object in list (A)
Private
FMyList: TBlist
published
property MyList[index: integer]: TStrip read GetMyList write SetMyList
The message I get is that I cannot declare the property as an array, because I am trying to get one item from the list instead of the whole list as follows (which does work);
property Mylist: TAlist read FMyList write SetMyList;

I hope this is clear!!
Thanks
 
You can certainly have a TObjectList as a memeber of an object which is stored in another TObjectList. No problem at all.

If you wish, you can declare your own object as a descendent of TObjectList. This allows you to add additional properties (such as another list) and methods and/or override existing properties and methods. Also that way you do not need to use code to cast your objects to the type you need.

So, for example, if you drop three buttons on a new form and put this code in the unit you should be able to see how to do it (Be sure to add contnrs to the uses clause):
Code:
TListA = class(TObjectList)
private
  FNameA:string;
  FListB:TObjectList;
public
  constructor Create(Name:string);
  destructor Destroy;  override;
  property NameA:string      read FNameA  write FNameA;
  property ListB:TObjectList read FListB  write FListB;
end;
:
:
implementation
:
:
constructor TListA.Create(Name:string);
begin
  inherited;
  FListB := TObjectList.Create;
end;

destructor TListA.Destroy;
begin
  ListB.Free;
  inherited;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  ListA:TListA;
  sName1:string;
  sName2:string;
begin
  ListA := TListA.Create( 'test' );
  try
    ListA.ListB.Add(Form1.Button1);
    ListA.ListB.Add(Form1.Button2);
    sName1 := TButton(ListA.ListB[0]).Caption;
    sName2 := TButton(ListA.ListB[1]).Caption;
    ShowMessage( 'B1 is ' + sName1 + #13 + 'B2 is ' + sName2);
  finally
    FListA.Free;
  end;    
end;
 
Thank you Zathras for your reply
FListA.Free caught me out, should be ListA.free

You have answered the first part of my question thank you.

The second part I probably didn't explain very well. Also the objects in TListB are always the same class and known. I want ListA object to return a ListB object ready made.
Using your code, to explain further:
Your ListB property returns the List to the Button3Click routine from which the Button3Click extracts the objects (Button1 -2).
What I am asking is, can the button3Click code be internal to TListA as the GetObjectList to internally extract the requested object by an index sent in (as you would with an array property), then return the object (from ListB) from ListA property rather than return the ListB itself for external processing.
I tried this with something like:

property ListB[index: integer]: TButton read GetListB write SetListB;

function TListA.GetListB(index: integer): TButton;
begin
result:=(FListB[index] as TButton);
end;
I got an error that said that ListB was not an array so I could not use index.

Thank you for your continued help?
 
Ok. Play around with this code:
Code:
TCustomObject = class(TObject)
private
  FString1:string;
  FString2:string;
  FInteger1:integer;
  FInteger2:integer;
public
  constructor Create( S1,S2:string; I1,I2:integer );
  property String1:string   read FString1  write FString1;
  property String2:string   read FString2  write FString2;
  property Integer1:integer read FInteger1 write FInteger1;
  property Integer2:integer read FInteger2 write FInteger2;
end;


TListB = class(TObjectList)
private
  function GetItem(Index: integer): TCustomObject;
public
  property Items[Index:integer]:TCustomObject read GetItem; default;
end;

TListA = class(TObjectList)
private
  FNameA:string;
  FListB:TListB;
public
  constructor Create(Name:string);
  destructor Destroy;  override;
  property NameA:string  read FNameA  write FNameA;
  property ListB:TListB  read FListB  write FListB;
end;
:
:
implementation
:
:
constructor TCustomObject.Create(S1, S2: string; I1, I2: integer);
begin
  FString1  := S1;
  FString2  := S2;
  FInteger1 := I1;
  FInteger2 := I2;
end;

constructor TListA.Create(Name:string);
begin
  inherited;
  FListB := TListB.Create;
end;

destructor TListA.Destroy;
begin
  FListB.Free;
  inherited;
end;

function TListB.GetItem(Index: integer): TCustomObject;
begin
  Result := TCustomObject(inherited Items[Index]);
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  ListA:TListA;
  oCustomObject:TCustomObject;
  sData:string;
  i:integer;
begin
  ListA := TListA.Create( 'test' );
  try
    // Note: Because oCustomObjects are being added to
    //       a TObjectList they will be freed automatically.
    oCustomObject := TCustomObject.Create('aa','bb',11,22);
    ListA.ListB.Add( oCustomObject );
    oCustomObject := TCustomObject.Create('cc','dd',33,44);
    ListA.ListB.Add( oCustomObject );

    for i := 0 to ListA.ListB.Count - 1 do
      with ListA.ListB.Items[i] do
        sData := sData
                   + String1 + ' '
                   + String2 + ' '
                   + IntToStr(Integer1) + ' '
                   + IntToStr(Integer2) + #13#10;
    ShowMessage( sData );
  finally
    ListA.Free;
  end;
end;
 
Zathras
Many thanks for your time.
Your suggestion was absolutely spot on, despite my poor description.

My regards
 
Hello Zathras
I hope you get this, as it appeared we had completed.
Although the code works beautifully, it is too graceful for me to understand fully the syntax.
The aim is for me to learn from this and it is not written in a way I am familiar with. Would you be so kind as to explain a few points, and I hope there are not too many.

I do not understand the TListB. It appears that calling items[index] in turn calls GetItem(index) but no index appears to be passed to this second stage. I have not seen inherited in this way either and don't understand.

Presumably the casting is done in the declaration of the function by the class it is asking to be returned.

Could ListA be a list of TComponent and ListB a property (as you have shown) of each item in ListA rather than of ListA itself? i.e. listA.iteminListA.ListB.items[x] and is there a way to have ListA internally obtain the same information from ListB as Button3 did so that I could use something like ListA.iteminListA[y,x] y being the listA item and x being the the listB item in the listA item.

Finally, how do you put your code in the code window on this forum.

Thank you again
 
Ok, I'll try.

TListB is descended from TObjectList. That means that it has all of the properties and methods of a TObjectList. In this example, there are no new properties or methods introduced, but the Items property is re-declared to change its behavior. The only change I made is to cause Items to be a list of TCustomObject instead of TObject. Since the way to get an item is to use the "real" function contained in TObjectList, the keyword "inherited" is used to tell the compiler that the "real" GetItem function of TObjectList is to be used to get the pointer. (Try it without "inherited" and you will get an error because the GetItem function will call itself repeatedly until you run out of stack space.

Since ListA is descended from TObjectList and TComponent is descended from TObject, the list could certainly contain TComponents.

If you click the link "ProcessTGML" you will see the way to mark text for special effects. In particular the tags [ignore]
Code:
 and
[/ignore] delimit what goes in a "code window" in a post.

Here is another small appliation to demonstrate the procedures:
Code:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, contnrs, Grids;

type

  TCustomObject = class(TObject)
  private
    FString1:string;
    FString2:string;
    FInteger1:integer;
    FInteger2:integer;
  public
    constructor Create( S1,S2:string; I1,I2:integer );
    property String1:string   read FString1  write FString1;
    property String2:string   read FString2  write FString2;
    property Integer1:integer read FInteger1 write FInteger1;
    property Integer2:integer read FInteger2 write FInteger2;
  end;

  TListB = class(TObjectList)
  private
    function GetItem(Index: integer): TCustomObject;
  public
    property Items[Index:integer]:TCustomObject read GetItem; default;
  end;

  TListA = class(TObjectList)
  private
    FNameA:string;
    FListB:TListB;
      function GetItem(Index: integer): TListB;
  public
    constructor Create(Name:string);
    destructor Destroy;  override;
    property NameA:string  read FNameA  write FNameA;
    property Items[Index:integer]:TListB read GetItem; default;
    procedure AddToListB( Index:integer; ACustomObject:TCustomObject );
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    StringGrid1: TStringGrid;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    MainList:TListA;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
var
  i:integer;
begin
  // Create MainList and add two entries each with 3 sub-entries.
  MainList := TListA.Create('MyList');
  i := MainList.Add( TListB.Create );
  MainList.AddToListB(i,TCustomObject.Create('111','112',113,114));
  MainList.AddToListB(i,TCustomObject.Create('111','112',113,114));
  MainList.AddToListB(i,TCustomObject.Create('111','112',113,114));
  i := MainList.Add( TListB.Create );
  MainList.AddToListB(i,TCustomObject.Create('211','212',213,214));
  MainList.AddToListB(i,TCustomObject.Create('211','212',213,214));
  MainList.AddToListB(i,TCustomObject.Create('211','212',213,214));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  MainList.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  // Dump contents of MainList to string grid.
  with StringGrid1 do
    begin
      Cells[1,1] := MainList[0].Items[0].String1;
      Cells[2,1] := MainList[0].Items[0].String2;
      Cells[3,1] := IntToStr(MainList[0].Items[0].Integer1);
      Cells[4,1] := IntToStr(MainList[0].Items[0].Integer2);
      Cells[1,2] := MainList[0].Items[1].String1;
      Cells[2,2] := MainList[0].Items[1].String2;
      Cells[3,2] := IntToStr(MainList[1].Items[0].Integer1);
      Cells[4,2] := IntToStr(MainList[1].Items[0].Integer2);
      Cells[1,3] := MainList[1].Items[0].String1;
      Cells[2,3] := MainList[1].Items[0].String2;
      Cells[3,3] := IntToStr(MainList[1].Items[0].Integer1);
      Cells[4,3] := IntToStr(MainList[1].Items[0].Integer2);
      Cells[1,4] := MainList[1].Items[1].String1;
      Cells[2,4] := MainList[1].Items[1].String2;
      Cells[3,4] := IntToStr(MainList[1].Items[0].Integer1);
      Cells[4,4] := IntToStr(MainList[1].Items[0].Integer2);
    end;
end;

{ TCustomObject }

constructor TCustomObject.Create(S1, S2: string; I1, I2: integer);
begin
  FString1  := S1;
  FString2  := S2;
  FInteger1 := I1;
  FInteger2 := I2;
end;

{ TListA }

constructor TListA.Create(Name:string);
begin
  inherited;
  FListB := TListB.Create;
end;

destructor TListA.Destroy;
begin
  FListB.Free;
  inherited;
end;

procedure TListA.AddToListB(Index:integer; ACustomObject: TCustomObject);
var
  oListB:TListB;
begin
  oListB := TListB( inherited Items[Index] );
  oListB.Add(ACustomObject);
end;

function TListA.GetItem(Index: integer): TListB;
begin
  Result := TListB(inherited Items[Index]);
end;

{ TListB }

function TListB.GetItem(Index: integer): TCustomObject;
begin
  Result := TCustomObject(inherited Items[Index]);
end;

end.
 
Thank you again
Your explanation is perfectly clear and it has opened other possibilities in my mind as a result. I will study the final code submission and hopefully learn more.
You have been a great help.

Regards
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top