Saving TreeViews to disk

     From the presentation given to the Australian Delphi User Group on 19th April 1999
     by Andy Bulka  abulka@netspace.net.au

wpe3.jpg (21323 bytes)

The following bulleted points have been converted from the powerpoint presentation.   Quite a bit of verbal explanation is, of course, missing.

Objectives

Brute Force Approach

Natural Streaming Approach

wpe5.jpg (7267 bytes)

How to participate in streaming

3 techniques, all involve overriding:

Basic Solution

Plus we need to define our own treeview so we can override a few choice methods

Basic Treeview facts

    dotdata.gif (7407 bytes)

Surprising facts about Ttreeview

Ownership & Memory Issues

So why not:

getChildren() secrets

Lets go coding

wpe4.jpg (19652 bytes)

We are going to build the application, pictured above.  Clicking on each tree node will display data in the edit fields (see blue line).  Full source code below.   For the unit code + form dfm + executable download this.

ANDYSTREAMTREE.PAS

unit andyStreamTree;

{
     From the presentation given to AGUG on 19th April 1999
     by Andy Bulka  abulka@netspace.net.au

     This single unit / form illustrates the principles of
     persistence through component streaming, by showing you
     how to save a treeview to disk.
}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, comctrls;

type
  TForm4 = class(TForm)
    Button1CREATETREE: TButton;
    Button1FREETREE: TButton;
    Button1POPULATE: TButton;
    Button1STREAMOUT: TButton;
    Button2STREAMIN: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Button1SAVERECORD: TButton;
    procedure Button1CREATETREEClick(Sender: TObject);
    procedure Button1FREETREEClick(Sender: TObject);
    procedure Button1POPULATEClick(Sender: TObject);
    procedure Button1STREAMOUTClick(Sender: TObject);
    procedure Button2STREAMINClick(Sender: TObject);
    procedure Button1SAVERECORDClick(Sender: TObject);
  private
    procedure clickFest(sender: TObject);
    procedure canDrop(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure draggy(Sender, Source: TObject; X, Y: Integer);
    { Private declarations }
  public
    { Public declarations }
  end;

  Ttree = class(TTreeview)
  private
    Ftest1 : string;
    procedure clear;
  protected
    procedure getchildren(proc: TGetChildProc; root: Tcomponent); override;
    procedure loaded; override;
  published
    property test1: string read Ftest1 write Ftest1;
  end;

  Tinfo = class(Tcomponent)
  private
    Fs1 : string;
    Fs2 : string;
  published
    property address: string read Fs1 write Fs1;
    property phone: string read Fs2 write Fs2;
  end;

var
  Form4: TForm4;
  tree: Ttree;

implementation

{$R *.DFM}

procedure TForm4.Button1CREATETREEClick(Sender: TObject);
begin
     tree := Ttree.create(self);
     tree.parent := self;
     tree.height := tree.height * 3;
     tree.width := tree.width * 2;

     tree.OnClick := clickFest;
     tree.DragMode := dmAutomatic;
     tree.OnDragDrop := draggy;
     tree.OnDragOver := canDrop;
end;

procedure TForm4.Button1FREETREEClick(Sender: TObject);
begin
     tree.free;
     tree := nil;
end;

procedure TForm4.Button1POPULATEClick(Sender: TObject);
var
     obj: Tinfo;
begin
     obj := Tinfo.create(tree);
     obj.address := '123 street';
     obj.phone := '999335533';
     tree.items.AddChildObject(nil, 'hello',obj);

     obj := Tinfo.create(tree);
     tree.items.AddChildObject(nil, 'world',obj);

     tree.test1 := 'laughing is not clowing';
end;

procedure TForm4.Button1STREAMOUTClick(Sender: TObject);
begin
     WriteComponentResFile('c:\windows\desktop\andyout.dfm', tree);
end;

procedure TForm4.Button2STREAMINClick(Sender: TObject);
begin
     tree.clear;
     RegisterClasses([Ttree,Tinfo]);
     ReadComponentResFile('c:\windows\desktop\andyout.dfm', tree);
end;

procedure TForm4.clickFest(sender: TObject);
begin
     if (tree.selected <> nil) and (tree.selected.data <> nil) then
     begin
       edit1.text := Tinfo(tree.selected.data).address;
       edit2.text := Tinfo(tree.selected.data).phone;
     end;
end;

procedure Ttree.getchildren(proc: TGetChildProc; root: Tcomponent);
var
     i: integer;
begin
     for i := 0 to items.count-1 do
        Tinfo(items[i].data).name := '';

     for i := 0 to items.count-1 do
     begin
        Tinfo(items[i].data).name := 'ID'+inttostr(i);
        proc( items[i].data );
     end
end;

procedure Ttree.loaded;
var
     i: integer;
begin
     for i := 0 to items.count-1 do
        items[i].data := findcomponent('ID'+inttostr(i));
end;

procedure Ttree.clear;
var
  i: integer;
begin
  for i := ComponentCount - 1 downto 0 do
    if components[i].ClassName = 'Tinfo' then
         RemoveComponent(components[i]);
  for i := items.count-1 downto 0 do
     items[i].Delete;
end;

procedure TForm4.Button1SAVERECORDClick(Sender: TObject);
var
    info : Tinfo;
begin
  if tree.selected is TTreeNode then begin
    info := tree.selected.data;
    info.address := edit1.text;
    info.phone := edit2.text;
  end
  else beep;

end;

procedure TForm4.canDrop;
begin
    Accept := True;
end;

procedure TForm4.draggy;
var
  TargetNode, SourceNode: TTreeNode;
begin
  TargetNode := tree.GetNodeAt (X, Y);
  if TargetNode <> nil then
  begin
    SourceNode := tree.Selected;
    SourceNode.MoveTo (TargetNode, naAddChildFirst);
    tree.Selected := TargetNode;
  end;
end;


end.