TreeView help

Posted by Jennifer on June 18, 2001

Hello all! Ok, here is what's given:

I have a parent node and child node(s) which looks like this:

 + 1 Joe
  | 
  |___1 Monitor
  |
  |___4 Floppy

which means that Joe(CustomerID = 1) ordered 1 monitor and 4 floppy drives.

I have a Table called Orders with Fields (in order):
OrderNumber, CustomerID, ItemNumber, MyDate

Now, problem here is how would I be able to delete an item (the monitor or floppy) from the Orders Table when the user clicks on the DeleteItem Pop-up Menu? What I mean is how would I extract the OrderNumber for that particular selected item or child node, so that I can delete it from the Table? What I need is a function... Thanks a lot!!!
J.D.

 Re: webmaster Guido on June 27

You could either add OrderNumber to each item-node, or reorganize the treeview and have an extra level for the orders.

1. If you stick to the 2 level treeview, the easiest method is: add OrderNumber to the text of each item-node at the moment that you create the node. For example:

- 1 Joe 
|__ 1 Monitor - Order: 1234
|__ 4 Floppy  - Order: 1234

To set the text of an item node, if OrderNumber is an integer:

Node.Text := ...; // what you do now to set the text
Node.Text := Node.Text + ' - Order: ' + IntToStr(OrderNumber);

Or, if OrderNumber is a string, we'd better call it OrderID in that case:

Node.Text := ...; // what you do now to set the text
Node.Text := Node.Text + ' - Order: ' + OrderID;

To extract OrderID from the selected item-node:

OrderID := IDFromNodeText(Treeview1.Selected.Text);

The function:

function TForm1.IDFromNodeText(S: string): string;
var
  P: integer;
begin

  P := LastDelimiter(' ', ItemText); // position of last space 
  // Extract ID 
  Result := copy(S, P + 1, Length(S) - P);
end;

2. Better would be to work with three levels instead of two, and to have separate tables for orders and order-items, because in the first example you can have only one order per customer. Also note that I put CustomerID in the last part of the customer-node text.

Joe 1
 |_ Order 285
 |   |_ 1 Monitor    
 |   |_ 4 Floppy
 |
 |_ Order 286
     |_ 2 Keyboard
     |_ 2 Mouse

When an item-node is selected, you can extract OrderID from the order-node, which is the parent of the selected item-node:

OrderID := IDFromNodeText(Treeview1.Selected.Parent.Text);

The code for the function itself is the same as in the first example. If your treeview is organized in this way, you can use the same function to extract all ID's, only the parameter will change.

If an item-node is selected:

OrderID := IDFromNodeText(Treeview1.Selected.Parent.Text);
CustomerID := IDFromNodeText(Treeview1.Selected.Parent.Parent.Text);

If an order-node is selected:

OrderID := IDFromNodeText(Treeview1.Selected.Text);
CustomerID := IDFromNodeText(Treeview1.Selected.Parent.Text);

If a customer-node is selected:

CustomerID := IDFromNodeText(Treeview1.Selected.Text);

There are other ways to add ID's in an invisible way to the nodes, such as using the node's Data property to store a pointer, or even maintain a list (in memory, such as a TStringlist) with details of each node. But I think that it's a lot easier for the user when she/he actually sees the ID's of customers, and orders.
And of course, you can easily extend this code to item-ID's.

Re: Pavement on June 27, 2001

I dont know the answer to your question, but i was working with Treeviews a little while back and didn't recieve any help, it seems nobody knows!... so all i can do is post my source code.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  FormEx, ComCtrls, StdCtrls, TFlatTabControlUnit, TFlatComboBoxUnit,
  Buttons, Menus;
type
  TMainForm = class(TForm)
  TreeView: TTreeView;
  mView: TMemo;
  BtnDel: TButton;
  lblName: TLabel;
  BtnPAdd: TBitBtn;
  MainMenu1: TMainMenu;
  procedure OnNodeChange(Sender: TObject; Node: TTreeNode);
  procedure DelSelectedNode(Sender: TObject);
  procedure AddNew(Sender: TObject);
  procedure FormCreate(Sender: TObject);
  procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
  { Private declarations }
public
  { Public declarations }
end;

type
  PData = ^TData;
  TData = record
    Name : string;
    Comments : string;
  end;

var
  MainForm: TMainForm;
  TreeStream, DataStream: TFileStream;

implementation
uses unit2;
{$R *.DFM}

procedure TMainForm.OnNodeChange(Sender: TObject; Node: TTreeNode);
begin
  if treeview.selected.level <> 0 then
  begin
    mView.Text := PData(TreeView.Selected.Data)^.Comments;
    lblName.Caption := PData(TreeView.Selected.Data)^.Name;
  end
end;

procedure TMainForm.DelSelectedNode(Sender: TObject);
begin
  if treeview.selected.level <> 0 then
    treeview.selected.free;
end;

procedure TMainForm.AddNew(Sender: TObject);
var
  DataPtr: PData;
begin
  AddNode := TAddNode.Create(Self);
  AddNode.ShowModal;
  AddNode.Free;
  if status = 1 then
  begin
    New(DataPtr);
    DataPtr^.Name := NewName;
    DataPtr^.Comments := NewComments;
    with treeview do
      if selected.level <> 0 then
        selected := selected.parent;
    treeview.items.AddchildObjectfirst(treeview.selected, NewName, DataPtr);
    treeview.fullexpand;
  end
  else
  begin
    mView.Text := '';
    lblName.Caption := '';
  end;
end;

procedure TMainForm.FormCreate(Sender: TObject);
var
  F:TFileStream;
begin
  F:=TFileStream.Create('TreeView.txt',fmOpenRead or fmShareDenyWrite);
  F.ReadComponent(TreeView);
  F.Free;
end;

procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
var
  F: TFileStream;
begin
  F:=TFileStream.Create('TreeView.txt',fmCreate or fmShareCompat);
  F.WriteComponent(TreeView);
  F.Free;
end;

end.
----------------------------------

unit Unit2;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, TFlatComboBoxUnit;
type
  TAddNode = class(TForm)
  BtnOk: TBitBtn;
  BtnCan: TBitBtn;
  mComments: TMemo;
  edName: TEdit;
  FlatComboBox1: TFlatComboBox;
  procedure BtnOkClick(Sender: TObject);
  procedure BtnCanClick(Sender: TObject);
private
  { Private declarations }
public
  { Public declarations }
end;

var
  AddNode: TAddNode;
  NewName, NewComments: string;
  Status: integer;

implementation
uses Unit1;
{$R *.DFM}

procedure TAddNode.BtnOkClick(Sender: TObject);
begin
  Status := 1;
  If (mComments <> Nil) and (edName <> Nil) then
  begin
    NewName := edName.text;
    NewComments := mComments.text;
    AddNode.Close;
  end
  else
  begin
    NewName := 'New';
    NewComments := 'No Comments';
    AddNode.Close;
  end
end;

procedure TAddNode.BtnCanClick(Sender: TObject);
begin
  AddNode.Close;
  Status := 2
end;

end.