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 gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How do I store and restore a component? 1

Status
Not open for further replies.

Griffyn

Programmer
Jul 11, 2002
1,077
AU
Hi all,

I'm writing a program that pulls data from lots of sources and assembles the results into a TComponent descendent class via published properties. I've then been using ReadComponent and WriteComponent to store the component's published properties into a blob field in a local table for caching. All of that works. Sorta.

The data itself makes up, for example, an invoice. All the invoice header details (date, invoice number, etc.) are being handled fine, but the line details of the invoice are not (eg StockCode, Quantity, etc.). I was using a TObjectList to hold each detail class instance (also a descendant of TComponent), but this was getting ignored, because there was no owner link between the TInvoiceDetail class and the TInvoice class.

I switched from TObjectList to TComponentList, but it made no difference, and I'm quickly getting confused. Only the header properties are getting to and from the blob field.

I've considered dispensing with the TComponentList container entirely, and relying on each TInvoiceDetail.Owner property (set to TInvoice) and FindComponent, etc. to search through them as required, but this seems kinda dangerous.

How should I structure things so I can use ReadComponent and WriteComponent to store and restore everything? Is there another approach I should be using? Any advice is appreciated.

 
I've played some more, and got a test project here for you to see exactly what I'm doing in case I wasn't clear.

This is a form unit that has a Write button that assembles data into a TInvoice class and uses WriteComponent to save it to a text file, a Read button that reads the text file back into a TInvoice instance, and a memo control to display the contents of the TInvoice instance after each write or read. The Edit1 box contains the filename to store the TInvoice contents.

Any comments or suggestions are much appreciated!

Code:
[navy][i]// for automatic syntax highlighting see faq102-6487 
[/i][/navy][b]unit[/b] Unit1;

[b]interface[/b]

[b]uses[/b]
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

[b]type[/b]
  TInvoiceDetail = [b]class[/b](TComponent)
  [b]private[/b]
    FQuantity: Integer;
    FStockCode: String;
  [b]published[/b]
    [b]property[/b] StockCode: String [b]read[/b] FStockCode [b]write[/b] FStockCode;
    [b]property[/b] Quantity: Integer [b]read[/b] FQuantity [b]write[/b] FQuantity;
  [b]end[/b];

  TInvoice = [b]class[/b](TComponent)
  [b]private[/b]
    FTranNum: String;
    FTranDate: TDateTime;
    [b]function[/b] GetDetail(Index: Integer): TInvoiceDetail;
  [b]public[/b]
    [b]constructor[/b] Create(AOwner: TComponent); [b]override[/b];
    [b]function[/b] CreateNewDetail: TInvoiceDetail;
    [b]property[/b] Detail[Index: Integer]: TInvoiceDetail [b]read[/b] GetDetail;
    [b]function[/b] DetailCount: Integer;
  [b]published[/b]
    [b]property[/b] TranNum: String [b]read[/b] FTranNum [b]write[/b] FTranNum;
    [b]property[/b] TranDate: TDateTime [b]read[/b] FTranDate [b]write[/b] FTranDate;
  [b]end[/b];

  TForm1 = [b]class[/b](TForm)
    WriteButton: TButton;
    ReadButton: TButton;
    Edit1: TEdit;
    Memo1: TMemo;
    [b]procedure[/b] WriteButtonClick(Sender: TObject);
    [b]procedure[/b] ReadButtonClick(Sender: TObject);
  [b]private[/b]
    [b]procedure[/b] UpdateMemo(AInvoice: TInvoice);
  [b]end[/b];

[b]var[/b]
  Form1: TForm1;

[b]implementation[/b]

[navy][i]{$R *.dfm}[/i][/navy]

[b]procedure[/b] TForm1.WriteButtonClick(Sender: TObject);
[b]var[/b]
  i : TInvoice;
  c : Integer;
[b]begin[/b]
  i := TInvoice.Create([b]nil[/b]);
  [b]try[/b]
    i.TranNum := [teal]'123458'[/teal];
    i.TranDate := Date - [purple]1[/purple];
    [b]with[/b] i.CreateNewDetail [b]do[/b]
    [b]begin[/b]
      StockCode := [teal]'HCSTRIP'[/teal];
      Quantity := [purple]5[/purple];
    [b]end[/b];
    [b]with[/b] i.CreateNewDetail [b]do[/b]
    [b]begin[/b]
      StockCode := [teal]'ZFREIGHT'[/teal];
      Quantity := [purple]1[/purple];
    [b]end[/b];
    DeleteFile(Edit1.Text);
    [navy][i]// write i to a text file
[/i][/navy]    [b]with[/b] TFileStream.Create(Edit1.Text, fmCreate) [b]do[/b]
      [b]try[/b]
        WriteComponent(i);
      [b]finally[/b]
        Free;
      [b]end[/b];
    [navy][i]// display contents of i to verify internal handling works
[/i][/navy]    UpdateMemo(i);
  [b]finally[/b]
    i.Free;
  [b]end[/b];
[b]end[/b];

[b]procedure[/b] TForm1.ReadButtonClick(Sender: TObject);
[b]var[/b]
  i : TInvoice;
  c : Integer;
[b]begin[/b]
  i := TInvoice.Create([b]nil[/b]);
  [b]try[/b]
    [b]with[/b] TFileStream.Create(Edit1.Text, fmOpenRead) [b]do[/b]
      [b]try[/b]
        ReadComponent(i);
      [b]finally[/b]
        Free;
      [b]end[/b];
    UpdateMemo(i);
  [b]finally[/b]
    i.Free;
  [b]end[/b];
[b]end[/b];

[navy][i]{ Displays contents of AInvoice, and subcomponents }[/i][/navy]
[b]procedure[/b] TForm1.UpdateMemo(AInvoice: TInvoice);
[b]var[/b]
  c : Integer;
[b]begin[/b]
  Memo1.Clear;
  Memo1.Lines.Add([teal]'TranNum='[/teal] + AInvoice.TranNum);
  Memo1.Lines.Add([teal]'TranDate='[/teal] + DateToStr(AInvoice.TranDate));
  [b]for[/b] c := [purple]0[/purple] [b]to[/b] AInvoice.DetailCount - [purple]1[/purple] [b]do[/b]
  [b]begin[/b]
    Memo1.Lines.Add(Format([teal]'Detail%d.StockCode=%s'[/teal], [c, AInvoice.Detail[c].StockCode]));
    Memo1.Lines.Add(Format([teal]'Detail%d.Quantity=%d'[/teal], [c, AInvoice.Detail[c].Quantity]));
  [b]end[/b];
[b]end[/b];

[navy][i]{ TInvoice }[/i][/navy]

[b]constructor[/b] TInvoice.Create(AOwner: TComponent);
[b]begin[/b]
  [b]inherited[/b];
[b]end[/b];

[b]function[/b] TInvoice.CreateNewDetail: TInvoiceDetail;
[b]begin[/b]
  Result := TInvoiceDetail.Create(Self);
  [navy][i]// the next line doesn't seem to make any difference
[/i][/navy]  Result.SetSubComponent(True);
[b]end[/b];

[navy][i]// return number of TInvoiceDetail children
[/i][/navy][b]function[/b] TInvoice.DetailCount: Integer;
[b]var[/b]
  c : Integer;
[b]begin[/b]
  Result := [purple]0[/purple];
  [b]for[/b] c := [purple]0[/purple] [b]to[/b] ComponentCount - [purple]1[/purple] [b]do[/b]
    [b]if[/b] Components[c] [b]is[/b] TInvoiceDetail [b]then[/b]
      Inc(Result);
[b]end[/b];

[navy][i]// return an indexed TInvoiceDetail child
[/i][/navy][b]function[/b] TInvoice.GetDetail(Index: Integer): TInvoiceDetail;
[b]var[/b]
  c, d : Integer;
[b]begin[/b]
  d := -[purple]1[/purple];
  Result := [b]nil[/b];
  [b]for[/b] c := [purple]0[/purple] [b]to[/b] ComponentCount - [purple]1[/purple] [b]do[/b]
    [b]if[/b] Components[c] [b]is[/b] TInvoiceDetail [b]then[/b]
    [b]begin[/b]
      Inc(d);
      [b]if[/b] Index = d [b]then[/b]
      [b]begin[/b]
        Result := TInvoiceDetail(Components[c]);
        Break;
      [b]end[/b];
    [b]end[/b];
[b]end[/b];

[b]end[/b].
 
Found it. Tooks lots of searching, and advice from this great page
Essentially, the SubComponent setting in Delphi doesn't work (at least not in D6). While TPersistent objects are streamed, they don't allow for ownership chains, so no good to me there unless I referenced each instance of TInvoiceDetail in a published property - no good for me here. Fortunately TCollection objects are streamed, and this code works perfectly.

Code:
[b]unit[/b] Unit1;

[b]interface[/b]

[b]uses[/b]
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls;

[b]type[/b]
  TInvoiceDetail = [b]class[/b](TCollectionItem)
  [b]private[/b]
    FQuantity: Integer;
    FStockCode: String;
  [b]published[/b]
    [b]property[/b] StockCode: String [b]read[/b] FStockCode [b]write[/b] FStockCode;
    [b]property[/b] Quantity: Integer [b]read[/b] FQuantity [b]write[/b] FQuantity;
  [b]end[/b];

  TInvoiceDetailCollection = [b]class[/b](TCollection)
  [b]private[/b]
    [b]function[/b] GetItem(Index: Integer): TInvoiceDetail;
    [b]procedure[/b] SetItem(Index: Integer; [b]const[/b] Value: TInvoiceDetail);
  [b]public[/b]
    [b]property[/b] Items[Index: Integer]: TInvoiceDetail [b]read[/b] GetItem [b]write[/b] SetItem; [b]default[/b];
  [b]end[/b];

  TInvoice = [b]class[/b](TComponent)
  [b]private[/b]
    FTranNum: String;
    FTranDate: TDateTime;
    FDetails: TInvoiceDetailCollection;
  [b]public[/b]
    [b]constructor[/b] Create(AOwner: TComponent); [b]override[/b];
    [b]destructor[/b] Destroy; [b]override[/b];
    [b]function[/b] CreateNewDetail: TInvoiceDetail;
  [b]published[/b]
    [b]property[/b] TranNum: String [b]read[/b] FTranNum [b]write[/b] FTranNum;
    [b]property[/b] TranDate: TDateTime [b]read[/b] FTranDate [b]write[/b] FTranDate;
    [b]property[/b] Details: TInvoiceDetailCollection [b]read[/b] FDetails [b]write[/b] FDetails;
  [b]end[/b];

  TForm1 = [b]class[/b](TForm)
    WriteButton: TButton;
    ReadButton: TButton;
    Edit1: TEdit;
    Memo1: TMemo;
    [b]procedure[/b] WriteButtonClick(Sender: TObject);
    [b]procedure[/b] ReadButtonClick(Sender: TObject);
  [b]private[/b]
    [b]procedure[/b] UpdateMemo(AInvoice: TInvoice);
  [b]end[/b];

[b]var[/b]
  Form1: TForm1;

[b]implementation[/b]

[navy][i]{$R *.dfm}[/i][/navy]

[b]procedure[/b] TForm1.WriteButtonClick(Sender: TObject);
[b]var[/b]
  i : TInvoice;
[b]begin[/b]
  i := TInvoice.Create([b]nil[/b]);
  [b]try[/b]
    i.TranNum := [teal]'123458'[/teal];
    i.TranDate := Date - [purple]1[/purple];
    [b]with[/b] i.CreateNewDetail [b]do[/b]
    [b]begin[/b]
      StockCode := [teal]'HCSTRIP'[/teal];
      Quantity := [purple]5[/purple];
    [b]end[/b];
    [b]with[/b] i.CreateNewDetail [b]do[/b]
    [b]begin[/b]
      StockCode := [teal]'ZFREIGHT'[/teal];
      Quantity := [purple]1[/purple];
    [b]end[/b];
    DeleteFile(Edit1.Text);
    [navy][i]// write i to a text file
[/i][/navy]    [b]with[/b] TFileStream.Create(Edit1.Text, fmCreate) [b]do[/b]
      [b]try[/b]
        WriteComponent(i);
      [b]finally[/b]
        Free;
      [b]end[/b];
    [navy][i]// display contents of i to verify internal handling works
[/i][/navy]    UpdateMemo(i);
  [b]finally[/b]
    i.Free;
  [b]end[/b];
[b]end[/b];

[b]procedure[/b] TForm1.ReadButtonClick(Sender: TObject);
[b]var[/b]
  i : TInvoice;
[b]begin[/b]
  i := TInvoice.Create([b]nil[/b]);
  [b]try[/b]
    [b]with[/b] TFileStream.Create(Edit1.Text, fmOpenRead) [b]do[/b]
      [b]try[/b]
        ReadComponent(i);
      [b]finally[/b]
        Free;
      [b]end[/b];
    UpdateMemo(i);
  [b]finally[/b]
    i.Free;
  [b]end[/b];
[b]end[/b];

[navy][i]{ Displays contents of AInvoice, and subcomponents }[/i][/navy]
[b]procedure[/b] TForm1.UpdateMemo(AInvoice: TInvoice);
[b]var[/b]
  c : Integer;
[b]begin[/b]
  Memo1.Clear;
  Memo1.Lines.Add([teal]'TranNum='[/teal] + AInvoice.TranNum);
  Memo1.Lines.Add([teal]'TranDate='[/teal] + DateToStr(AInvoice.TranDate));
  [b]for[/b] c := [purple]0[/purple] [b]to[/b] AInvoice.Details.Count - [purple]1[/purple] [b]do[/b]
  [b]begin[/b]
    Memo1.Lines.Add(Format([teal]'Detail%d.StockCode=%s'[/teal], [c, AInvoice.Details[c].StockCode]));
    Memo1.Lines.Add(Format([teal]'Detail%d.Quantity=%d'[/teal], [c, AInvoice.Details[c].Quantity]));
  [b]end[/b];
[b]end[/b];

[navy][i]{ TInvoice }[/i][/navy]

[b]constructor[/b] TInvoice.Create(AOwner: TComponent);
[b]begin[/b]
  [b]inherited[/b];
  FDetails := TInvoiceDetailCollection.Create(TInvoiceDetail);
[b]end[/b];

[b]function[/b] TInvoice.CreateNewDetail: TInvoiceDetail;
[b]begin[/b]
  Result := TInvoiceDetail.Create(Details);
[b]end[/b];

[b]destructor[/b] TInvoice.Destroy;
[b]begin[/b]
  FDetails.Free;
  [b]inherited[/b];
[b]end[/b];

[navy][i]{ TInvoiceDetailCollection }[/i][/navy]

[b]function[/b] TInvoiceDetailCollection.GetItem(Index: Integer): TInvoiceDetail;
[b]begin[/b]
  Result := TInvoiceDetail([b]inherited[/b] Items[Index]);
[b]end[/b];

[b]procedure[/b] TInvoiceDetailCollection.SetItem(Index: Integer; [b]const[/b] Value: TInvoiceDetail);
[b]begin[/b]
  [b]inherited[/b] Items[Index] := Value;
[b]end[/b];

[b]end[/b].
 
This might help:

Another suggestion would be to go with XML. In this way you're protected from changes in structure to the document and you can save/load the xml file with a doctype entity. Your code can then process the doctype accordingly. XML files could be streamed into a blob field without much effort.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top