Solved

TreeView add item

Posted on 2006-07-05
10
1,272 Views
Last Modified: 2010-04-05
Hello,

I want to use TreeView to create somekind of a very simple "document browser".

The TreeView starts with an empty "Root".

If user RIGHT-CLICK to a node, a "popup menu" will appear with this menu items :

- Add FOLDER
- Add FILE
- Delete

If user choose Add FOLDER, it can have child nodes  FILEs , but not FOLDER
If user choose ADD FILE, it can not have child node

At the end the structure will be something like this :

Root
|-------- FILE
|-------- FOLDER
                 |------ FILE
                 |------ FILE

So, this structure below is not allowed because FOLDER may not have FOLDER as child node:

Root
|-------- FILE
|-------- FOLDER
                 |------ FOLDER

One more thing, how can I add "compound items" as node value ? For example: a FILE/FOLDER item is constructed from 2 values :
1. Node LABEL (example: Price List)
2. File (or folder) path (example: C:\DATA\Price_List.xls)

The TreeView will display only the "Node LABEL"

If user choose Delete from the popup menu, the node and all childs will be deleted.

Please if you can show me some codes how to make this scenario.

Thanks in advance.
0
Comment
Question by:nova2002
  • 3
  • 3
  • 2
  • +2
10 Comments
 
LVL 21

Expert Comment

by:ziolko
Comment Utility
here's part of my old code:

procedure TCustomInventoryForm.AddSubCategory(Idx: Integer;const Caption: string);
var tn: TTreeNode;
    sc: TSubCategory;
begin
  sc := TSubCategory.Create(Idx, Caption);
  FSubCategories.Add(sc);
  tn := TreeView.Items.AddChild(nil ,Caption);
  tn.Data := sc;
  tn.ImageIndex := Idx;
  tn.SelectedIndex := Idx;
end;

>> AddChild(nil ,Caption); adds child of the "root"

if You want add child nodes:

tn := TreeView.Items.AddChild(nil ,Caption);
tn2 := TreeView.Items.AddChild(tn ,Caption2);
tn3 := TreeView.Items.AddChild(tn2 ,Caption3);

and so on.

ziolko.
0
 

Author Comment

by:nova2002
Comment Utility
ziolko, thats not what I expected from my questions above.
0
 
LVL 10

Expert Comment

by:atul_parmar
Comment Utility
I suggest to go with Tvirtualstringtree, It best fits your requirement. see http://www.soft-gems.net/VirtualTreeview/

See the demos available with and if you feel any doubt ask me.

Or If you want to use treeview, it has Data property where you can store other data.

To give you rough idea on your structure.
1. When user right clicks, exmine the selected item
    - If it is FILE, disable Add FILE menu item
    - If it is FOLDER, disable Add FOLDER menu item
0
 

Author Comment

by:nova2002
Comment Utility
atul, it looks interesting. But I am not that familiar with the licensing. They have Mozilla MPL1.1 and GNU Lesser GPL. Does it means I can use it for a commercial product for free ? Which one should I choose.

I will try to give it a go and will ask if I have problem or questions with it.
0
 
LVL 10

Expert Comment

by:atul_parmar
Comment Utility
Can you tell me what Delphi version you are using the same is also available with Delphi 7
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 10

Expert Comment

by:atul_parmar
Comment Utility
as long as the licensing is concerned you can use and distribute is freely. see the licensing document.
0
 
LVL 17

Accepted Solution

by:
TheRealLoki earned 450 total points
Comment Utility
Just wrote this example for you on how I'd do it using a standard TTreeview

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, (*Variants,*) Classes, Graphics, Controls, Forms, // uncomment variants if using delphi7+
  Dialogs, ComCtrls, ActnList, Menus, StdCtrls;

type TTreeDataObject = class(TObject) // every object we add to a node is of this type, so we can easily work with it
  public
  end;

type TTreeRootObject = class(TTreeDataObject) // our own root node
  public
  end;

type TTreeFolderObject = class(TTreeDataObject) // a folder
  public
    DisplayName: string;
    constructor Create(DisplayName_: string);
  end;

type  TTreeFileObject = class(TTreeDataObject) // a file
  public
      DisplayName: string;
      Filename: string;
      constructor Create(Filename_, DisplayName_: string);
  end;


type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    pmTree: TPopupMenu;
    ActionList1: TActionList;
    aAddFolder: TAction;
    aAddFile: TAction;
    AddFolder1: TMenuItem;
    AddFile1: TMenuItem;
    aDelete: TAction;
    OpenDialog1: TOpenDialog;
    Delete1: TMenuItem;
    Label1: TLabel;
    Label2: TLabel;
    procedure aAddFolderExecute(Sender: TObject);
    procedure aAddFileExecute(Sender: TObject);
    procedure pmTreePopup(Sender: TObject);
    procedure aDeleteExecute(Sender: TObject);
    procedure TreeView1Deletion(Sender: TObject; Node: TTreeNode);
    procedure FormCreate(Sender: TObject);
    procedure TreeView1Click(Sender: TObject);
  private
    { Private declarations }
    RightClickedNode: TTreeNode;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
// add our own root note
    with TreeView1.Items.AddChild(nil, 'ROOT') do
      Data := TTreeRootObject.Create;
end;

procedure TForm1.pmTreePopup(Sender: TObject);
var
  TreeDataObject: TTreeDataObject;
begin
// when the user right clicks, we decide what menu options are available
  if TreeView1.SelectionCount = 1 then // use .SelCount if delphi version < 7
  begin //a tree node is selected. find out what type it is and show the appropriate menu items
    RightClickedNode := TreeView1.Selected;
    if assigned(RightClickedNode.Data) then
    begin
      TreeDataObject := TTreeDataObject(RightClickedNode.Data);
      if (TreeDataObject is TTreeRootObject) then
      begin // can add a file or folder from the root
        aAddFolder.Visible := True;
        aAddFile.Visible := True;
        aDelete.Visible := False;
      end
      else if (TreeDataObject is TTreeFolderObject) then
      begin // can add a file below a folder
        aAddFolder.Visible := False;
        aAddFile.Visible := True;
        aDelete.Visible := True;
      end
      else if (TreeDataObject is TTreeFileObject) then
      begin // can not add anything below a file
        aAddFolder.Visible := False;
        aAddFile.Visible := False;
        aDelete.Visible := True;
      end
      else
      begin // unhandled data type, not a folder or file type (does not happen in this example, but here in case you wish to add future support)
        aAddFolder.Visible := False;
        aAddFile.Visible := False;
        aDelete.Visible := False;
      end;
    end
    else
    begin // this does not have an object assigned to it (does not happen in this example, but here in case you wish to add future support)
        aAddFolder.Visible := False;
        aAddFile.Visible := False;
        aDelete.Visible := False;
    end;
  end
  else
  begin // do not allow adding if user has selected more than 1 node
    aAddFolder.Visible := False;
    aAddFile.Visible := False;
  end;
end;

procedure TForm1.aAddFolderExecute(Sender: TObject);
var
  newcaption: string;
begin
  newcaption := InputBox('Add Folder', 'Folder name', 'new');
  TreeView1.Items.AddChildObject(RightClickedNode, newcaption, TTreeFolderObject.Create(newcaption));
  RightClickedNode.Expand(False);
end;

procedure TForm1.aAddFileExecute(Sender: TObject);
var
  TreeDataObject: TTreeDataObject;
  newcaption, filename: string;
  newnode: TTreeNode;
begin
  if OpenDialog1.Execute then
  begin
    filename := OpenDialog1.FileName;
    newcaption := InputBox('Add File', 'Display name', ExtractFilename(OpenDialog1.FileName));
    TreeView1.Items.AddChildObject(RightClickedNode, newcaption, TTreeFileObject.Create(Filename, newcaption));
    RightClickedNode.Expand(False);
  end;
end;

procedure TForm1.aDeleteExecute(Sender: TObject);
begin
  If MessageDlg('Really delete this node and any child nodes it has?', mtConfirmation, [mbYes, mbCancel], 0) = idYes then
    RightClickedNode.Delete;
end;

procedure TForm1.TreeView1Deletion(Sender: TObject; Node: TTreeNode);
var
  TreeDataObject: TTreeDataObject;
begin
  if assigned(Node.Data) then
    TTreeDataObject(Node.Data).Free; // assume all objects are our objects and clean it up as we delete
end;

procedure TForm1.TreeView1Click(Sender: TObject);
var
  TreeDataObject: TTreeDataObject;
begin
// display the tree node details (folder or file)
  if TreeView1.SelectionCount = 1 then // use .SelCount if delphi version < 7
  begin
    if assigned(TreeView1.Selected.Data) then
    begin
      TreeDataObject := TTreeDataObject(TreeView1.Selected.Data);
      if (TreeDataObject is TTreeRootObject) then
      begin // can add a file or folder from the root
        label1.caption := 'ROOT';
        label2.caption := '';
      end
      else if (TreeDataObject is TTreeFolderObject) then
      begin // can add a file below a folder
        label1.caption := TTreeFolderObject(TreeDataObject).DisplayName;
        label2.caption := '';
      end
      else if (TreeDataObject is TTreeFileObject) then
      begin // can not add anything below a file
        label1.caption := TTreeFileObject(TreeDataObject).DisplayName;
        label2.caption := TTreeFileObject(TreeDataObject).Filename;
      end
      else
      begin
        label1.caption := '';
        label2.caption := '';
      end;
    end
    else
    begin // no data object assigned (does not happen in this example)
      label1.caption := '';
      label2.caption := '';
    end;
  end;
end;

{ TTreeFolderObject }

constructor TTreeFolderObject.Create(DisplayName_: string);
begin
  inherited Create;
  DisplayName := DisplayName_;
end;

{ TTreeFileObject }

constructor TTreeFileObject.Create(Filename_, DisplayName_: string);
begin
  inherited Create;
  Filename := Filename_;
  DisplayName := DisplayName_;
end;

end.


***********************************
***** FORM FOLLOWS *****************
***********************************

object Form1: TForm1
  Left = 186
  Top = 107
  Width = 651
  Height = 455
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 24
    Top = 8
    Width = 32
    Height = 13
    Caption = 'Label1'
  end
  object Label2: TLabel
    Left = 24
    Top = 32
    Width = 32
    Height = 13
    Caption = 'Label2'
  end
  object TreeView1: TTreeView
    Left = 16
    Top = 56
    Width = 361
    Height = 297
    HideSelection = False
    Indent = 19
    PopupMenu = pmTree
    ReadOnly = True
    RightClickSelect = True
    RowSelect = True
    ShowRoot = False
    TabOrder = 0
    OnClick = TreeView1Click
    OnDeletion = TreeView1Deletion
  end
  object pmTree: TPopupMenu
    OnPopup = pmTreePopup
    Left = 224
    Top = 128
    object AddFolder1: TMenuItem
      Action = aAddFolder
    end
    object AddFile1: TMenuItem
      Action = aAddFile
    end
    object Delete1: TMenuItem
      Action = aDelete
    end
  end
  object ActionList1: TActionList
    Left = 224
    Top = 176
    object aAddFolder: TAction
      Caption = 'Add Folder'
      Hint = 'Add Folder'
      OnExecute = aAddFolderExecute
    end
    object aAddFile: TAction
      Caption = 'Add File'
      Hint = 'Add File'
      OnExecute = aAddFileExecute
    end
    object aDelete: TAction
      Caption = 'Delete'
      Hint = 'Delete'
      OnExecute = aDeleteExecute
    end
  end
  object OpenDialog1: TOpenDialog
    Left = 224
    Top = 216
  end
end
0
 
LVL 1

Expert Comment

by:tammoz
Comment Utility
hi
  the best solution is to create your own class and inherite it from the (TTreeNode) class
so you can add any property and save any value you need in it, follow these steps

1) add this code after the (uses) part of the interface of your unit

Type Node_Folder = Class(TTreeNode)
  private
    FolderPrice : float;
    FolderPath : string;
end;

type Node_File = Class(TTreeNode)
  private
    FilePrice: float;
    FilePath : String;
end;
----------------------------------------------------------------------------------------------

2) then identify a global variable like this:
  Current_Class : TTreeNodeClass;
----------------------------------------------------------------------------------------------

3) go to the event (OnCreateNodeClass) of your treeview and write down the following:

procedure TChoseVisitFm.TreeView1CreateNodeClass(Sender: TCustomTreeView;
  var NodeClass: TTreeNodeClass);
begin
  NodeClass := Current_Class;
end;

to tell delphi that you want to type cast the treenode:
----------------------------------------------------------------------------------------------

4) now to add a new file you have to do the following:

procedure Add_New_File(FName, FPath : string, FPrice : float, FParent : TTreeNode);
var NFile : TTreeNode;
begin
     Current_Class := Node_File;
     NFile := TreeView1.Items.AddChild(FParent, FName);
     (NFile as Node_File).FilePrice := FPrice;
     (NFile as Node_File).FilePath  := FPath;
end;

and to add a new folder

procedure Add_New_Folder(FName, FPath : string, FPrice : float);
var NFolder : TTreeNode;
begin
     Current_Class := Node_Folder;
     NFolder := TreeView1.Items.AddChild(Nill, FName);
     (NFolder as Node_Folder).FolderPrice := FPrice;
     (NFolder as Node_Folder).FolderPath  := FPath;
end;

last note
       in your case you can use one class for both file and folder but for me I preffer to separate them for future development and also it will be easer to specify an image from image index for every class of them

Best regards
0
 

Author Comment

by:nova2002
Comment Utility
TheRealLoki, thanks for your example. That is very nice for learning purposes.

Your quality of answer is always excellent !
0
 
LVL 17

Expert Comment

by:TheRealLoki
Comment Utility
you're welcome :-)
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.

763 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

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now