Solved

TCollection & TCollectionItem -> Help Please...

Posted on 2002-04-18
15
676 Views
Last Modified: 2010-04-04
Hi,
I havent really worked with TCollection or TCollectionItem before but Im trying to define a persistant structure using TCollection and TCollectionItem. I then write/read this "component" to/from a file using the Write/ReadComponent method.

Please find my code below.
1. It seems to work fine when Im writing it to a file, but when I try to read it back, I get a runtime error.

2. The writing and reading of the component works fine if I remove the ObjectContainer property from the TMacroItem collection item, so Im assuming that my code for the TObjectHierarcy is incorrect.


NOTE: For this to work, U need 2 buttons on the form, Link buttons onclick events to the repective onclick procedures in the code.

(************************)
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
(********************************************************************)
type
  TObjectItem = class(TCollectionItem)
    private
      FName:String;
    public
      procedure Assign(Source: TPersistent); override;
    published
      property Name: String Read FName Write FName;
  end;
  TObjectHierarcy = class(TCollection)
    private
      FOwner : TComponent;
    protected
      function GetOwner : TPersistent; override;
      function GetItem(Index: Integer): TObjectItem;
      procedure SetItem(Index: Integer; Value: TObjectItem);
    public
      constructor Create(AOwner : TComponent);
      destructor Destroy;
      function Add:TObjectItem;

      property Items[Index:integer]:TObjectItem Read GetItem Write SetItem;
    published

  end;
(*******************************)
  TMacroItem = class(TCollectionItem)
    private
      FMacroName:string;
      FWindow:TObjectHierarcy;
    public
    procedure Assign(Source: TPersistent); override;
    published
      property MacroName: String read FMacroName write FMacroName;
      Property ObjectContainer : TObjectHierarcy Read FObjectContainer write FObjectContainer;
  end;
  TMacroList = class(TCollection)
  private
    FOwner:TComponent;
  protected
      function GetOwner : TPersistent; override;
      function GetItem(Index: Integer): TMacroItem;
      procedure SetItem(Index: Integer; Value: TMacroItem);
    public
      constructor Create(AOwner : TComponent);
      destructor Destroy;
      function Add:TMacroItem;

      property Items[Index:integer]:TMacroItem Read GetItem Write SetItem;
    published

  end;

(*******************************)
  TComp = class(TComponent)
    private
      FMyList:TMacroList;
    public
       constructor Create(AOwner: TComponent);
       Procedure Free;
       destructor Destroy;
    published
      Property MyList : TMacroList Read FMyList write FMyList;
  end;
(*******************************)

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
implementation

{$R *.DFM}
(************************************************************)
(* This is the component that contains the collections *)
constructor TComp.Create(AOwner: TComponent);
Begin
  inherited Create(AOwner);
  Self.FMyList:=TMacroList.Create(Self);
End;
Procedure TComp.Free;
Begin
  Self.FMyList.Clear;
  inherited;
End;
destructor TComp.Destroy;
begin
  inherited Destroy;
End;
(************************************************************)
function TMacroList.GetOwner: TPersistent;
begin
  Result := FOwner;
end;
function TMacroList.GetItem(Index: Integer): TMacroItem;
Begin
  Result:=TMacroItem(inherited GetItem(Index));
End;
procedure TMacroList.SetItem(Index: Integer; Value: TMacroItem);
Begin
  inherited SetItem(Index,Value);
End;
constructor TMacroList.Create(AOwner: TComponent);
begin
  inherited Create(TMacroItem);
  FOwner := AOwner;
end;
destructor TMacroList.Destroy;
Begin
  Self.Clear;
  inherited destroy;
End;
function TMacroList.Add:TMacroItem;
Begin
  Result:=TMacroItem(inherited Add);
  Result.FObjectContainer:=TObjectHierarcy.Create(Self.FOwner);
End;
(************************************************************)
procedure TMacroItem.Assign(Source: TPersistent);
begin
  if Source is TMacroItem then
  Begin
    MacroName:= TMacroItem(Source).MacroName;
    Self.ObjectContainer.Assign(TMacroItem(Source).ObjectContainer); // Is this right ???
  End
  else
    inherited; //raises an exception
end;
(************************************************************)
constructor TObjectHierarcy.Create(AOwner: TComponent);
begin
  inherited Create(TObjectItem);
  FOwner := AOwner;
end;
destructor TObjectHierarcy.Destroy;
Begin
  Self.Clear;
  inherited destroy;
End;
function TObjectHierarcy.GetOwner: TPersistent;
begin
  Result := FOwner;
end;
function TObjectHierarcy.GetItem(Index: Integer): TObjectItem;
Begin
  Result:=TObjectItem(inherited GetItem(Index));
End;
procedure TObjectHierarcy.SetItem(Index: Integer; Value: TObjectItem);
Begin
  inherited SetItem(Index,Value);
End;
function TObjectHierarcy.Add:TObjectItem;
Begin
  Result:=TObjectItem(inherited Add);
End;
(*********************************)
procedure TObjectItem.Assign(Source: TPersistent);
begin
  if Source is TObjectItem then
  Begin
    Name := TObjectItem(Source).Name;
  End
  else
    inherited; //raises an exception
end;

(******************************)
procedure TForm1.Button1Click(Sender: TObject);
var f:TStream;
   M:TMacroItem;
   O:TObjectItem;
   j:integer;
   Comp:TComp;
begin
  Comp:=tComp.Create(nil);
  M:=Comp.MyList.Add;
    M.MacroName:='Testing 1';
    O:=M.ObjectContainer.Add;
  M:=Comp.MyList.Add;
    M.MacroName:='Testing 2';

  f:=TFileStream.Create('c:\file.txt',fmCreate);
  f.WriteComponent(Comp);
  f.free;
  showmessage('File "c:\file.txt" created'.);

end;

procedure TForm1.Button2Click(Sender: TObject);
var f:Tstream;
    C:TComp;
    M:TMacroItem;
    O:TObjectItem;
    j:Integer;
begin
  C:=TComp.create(nil);
  C.MyList.Clear;
  showmessage(inttostr(C.MyList.count));
  f:=TFileStream.Create('c:\file.txt',fmOpenRead);
  f.ReadComponent(c); // Error occurs here !!!
  For j := 0 to C.MyList.Count-1 do
     ShowMessage(C.MyList.Items[j].MacroName);
  showmessage(inttostr(C.MyList.count));
  f.free;
end;

end.

0
Comment
Question by:CyberKnight
  • 8
  • 7
15 Comments
 
LVL 9

Expert Comment

by:ITugay
ID: 6950001
Hi CyberKnight,

you call inherited for TCollectionItem.Assign, it doesn't overrided in TCollectionItem,
but take a look at implementation of TPersistent.Assign:

...........
procedure TPersistent.AssignTo(Dest: TPersistent);
begin
  Dest.AssignError(Self);
end;

procedure TPersistent.Assign(Source: TPersistent);
begin
  if Source <> nil then Source.AssignTo(Self) else AssignError(nil);
end;
...........


You can see that it produced error every time you call it, right?
Do not call Inherited assign.

------
Igor.
0
 

Author Comment

by:CyberKnight
ID: 6950088
Hi Igor, so what you are saying is that I shouldnt call the inherited Assign in my assign method.

Ok, Ive tried this and is still doesnt seem to work, I still get an error when I try to read it from the file ?

Can U maybe correct the code for me ?

By the way.... I found a syntax error in the code that I posted, sorry....
(*This is the correct TMacroItem*)

TMacroItem = class(TCollectionItem)
   private
     FMacroName:string;
//     FWindow:TObjectHierarcy; // this line is needs to be removed.
     FObjectContainer:TObjectHierarcy;

   public
   procedure Assign(Source: TPersistent); override;
   published
     property MacroName: String read FMacroName write FMacroName;
     Property ObjectContainer : TObjectHierarcy Read FObjectContainer write FObjectContainer;
 end;
0
 
LVL 9

Expert Comment

by:ITugay
ID: 6950098
>> Hi Igor, so what you are saying is that I shouldnt call the inherited Assign in my assign method.

because it will raise an exception every time is called. TPersistent.Assign declared similar to "abstract" way, and it should be called when you want to inform that tehere is wrong assignment.


>> Can U maybe correct the code for me ?
OK, I will try. Just a moment.

----
Igor.

0
Master Your Team's Linux and Cloud Stack!

The average business loses $13.5M per year to ineffective training (per 1,000 employees). Keep ahead of the competition and combine in-person quality with online cost and flexibility by training with Linux Academy.

 
LVL 9

Expert Comment

by:ITugay
ID: 6950134
Exploring your code I found a few strange things:

1. Detructor not overrided.
2. Constructor not overrided, but hided with your own. Collection must know what classes is in it.

while I'll continue exploring, can you explain me what is the goal of your task? It seems that model can be reconsidered.

-------
Igor.
0
 
LVL 9

Accepted Solution

by:
ITugay earned 100 total points
ID: 6950227
OK,
here is a "pure" working code.
You can use it as base to continue:

----
Igor


unit Unit2;

interface

uses
  Classes;

type
  TObjectItem = class(TCollectionItem)
  private
    FName: String;
  published
    property Name: String read FName write FName;
  end;

  TObjectHierarcy = class(TOwnedCollection)
  end;

  TMacroItem = class(TCollectionItem)
  private
    FMacroName: String;
    FObjectContainer: TObjectHierarcy;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
  published
    property MacroName: String read FMacroName write FMacroName;
    property ObjectContainer: TObjectHierarcy read FObjectContainer write FObjectContainer;
  end;

  TMacroList = class(TOwnedCollection)
  end;

  TComp = class(TComponent)
  private
    FMacroList: TMacroList;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property MacroList: TMacroList read FMacroList write FMacroList;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Samples', [TComp]);
end;

constructor TMacroItem.Create(Collection: TCollection);
begin
  Inherited;
  FObjectContainer := TObjectHierarcy.Create(Self, TObjectItem);
end;

destructor TMacroItem.Destroy;
begin
  FObjectContainer.Free;
  Inherited;
end;


constructor TComp.Create(AOwner: TComponent);
begin
  Inherited;
  FMacroList := TMacroList.Create(Self, TMacroItem);
end;

destructor TComp.Destroy;
begin
  FMacroList.Free;
  Inherited;
end;

end.
0
 

Author Comment

by:CyberKnight
ID: 6950297
Ok, Thank you Igor !!! I will look at your code.

Maybe my model can be reconsidered....???
It all started when I needed to write the following variable to a file. (Look at the variable "MacroList" below).

Someone suggested that in order to write a variable of that type to a file, I would need to use collections, and Read/writeComponent methods, so that is what Im trying to do.... Can you think of a better solution..??

If you can help, I dont mind increasing the points....really ! :-)

(*****************)
Type
ObjectItem = Record
   Name: String;
   Somedata:Integer;
end;

ObjectH = Record
  ObjectInfo : Array of ObjectItem;
End;

Macro=Record
  MacroName: String;
  MacroObject: ObjectH;
End;
Var MacroList: array of Macro;
(***************)

0
 
LVL 9

Expert Comment

by:ITugay
ID: 6950306
Seems my code exactly what you need. Without any additional points :-)

-------
Igor.

0
 

Author Comment

by:CyberKnight
ID: 6950477
Thanks Igor, I will try it out. If I have any further questions I will post them here....

CK.
0
 

Author Comment

by:CyberKnight
ID: 6951179
Hi Igor...

I need a little assistance please,
Using your code, how do I add a value to the
1. Name property of TObjectItem Object.
2. MacroName property of TObjectHieraracy Object.

Thank you....
CK.

(**************)
procedure TForm1.Button1Click(Sender: TObject);
Var X:tComp;
    F:TStream;
begin
  X:=TComp.Create(Form1);
// (X.MacroList.Add). ; ??? How do I add a value to the name property of the TObjectItem.
// Also how do I add a value to the MacroName property of the TObjectHierarcy


  f:=TFileStream.Create('c:\file.txt',fmCreate);
  f.WriteComponent(Comp1);
  f.free;

  X.Free;

end;
0
 

Author Comment

by:CyberKnight
ID: 6951186
Oh, sorry 4th last line should read
   f.WriteComponent(X);
instead of
   f.WriteComponent(Comp1);
0
 

Author Comment

by:CyberKnight
ID: 6951223
Oh, sorry 4th last line should read
   f.WriteComponent(X);
instead of
   f.WriteComponent(Comp1);
0
 
LVL 9

Expert Comment

by:ITugay
ID: 6952544
hi CK,

here is a sample:

// Comp1 - TComp placed on Form1.

procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  S: TFileStream;
  MI: TMacroItem;
  OI: TObjectItem;
begin
  // add first macro item
  MI := TMacroItem(Comp1.MacroList.Add);
  MI.MacroName := 'Macro1';

  // add second macro item
  MI := TMacroItem(Comp1.MacroList.Add);
  MI.MacroName := 'Macro1';

  // add object item into second macro item
  OI := TObjectItem(MI.ObjectContainer.Add);
  OI.Name := 'ObjectItem1';

  S := TFileStream.Create('C:\COMP.DAT', fmCreate);
  S.WriteComponent(Comp1);
  S.Free;
end;

------
Igor.
0
 

Author Comment

by:CyberKnight
ID: 6952727
Hi !
Igor, thanks for all your help,that was great ! I have got it to write and read the data structure to and from a file.

BTW, those DB components on your site are amazing !

Thanx again..

CK
0
 

Author Comment

by:CyberKnight
ID: 6952728
Great, thank you !
0
 
LVL 9

Expert Comment

by:ITugay
ID: 6952784
thanx :-)
0

Featured Post

ScreenConnect 6.0 Free Trial

Discover new time-saving features in one game-changing release, ScreenConnect 6.0, based on partner feedback. New features include a redesigned UI, app configurations and chat acknowledgement to improve customer engagement!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Nobody understands Phishing better than an anti-spam company. That’s why we are providing Phishing Awareness Training to our customers. According to a report by Verizon, only 3% of targeted users report malicious emails to management. With compan…
A short tutorial showing how to set up an email signature in Outlook on the Web (previously known as OWA). For free email signatures designs, visit https://www.mail-signatures.com/articles/signature-templates/?sts=6651 If you want to manage em…

803 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question