Linking a TreeView and a ListView


Here is what the finished application will look like.

Control Caption Name
TButton Add Folder but_AddFolder
TButton Add File but_AddFile
TButton Remove but_Remove
TTreeView   tv_eg5
TListView   lv_Link



Looking at the above requirements, the most difficult is probably selecting a TreeView node when a ListView item is double clicked. This is quite easily solved by using the Node's ItemID property (see example 7).

As explained in example 5 I'll be using the ListView's data property as if it was an integer. In a real application you might want to store the ItemID value as a member of a class / record that the .Data property points to (example 5).


The way to link a ListItem to a TreeNode is to store the TreeNode's ItemID in the ListItem's data property. eg


   ListView.Data := TreeNode.ItemID;
Now when the ListItem is double clicked you get the node with the given TreeNode eg

   LinkNode := TreeView.Items.GetNode(  HTreeItem(ListView.Data)  );
And then select the TreeNode

   LinkNode.Selected := true;
As you can see its very easy. Once you get the TreeNode that the ListItem is linked to you can do whatever you like with it. Rename, delete, move etc.


Before moving on to the source, some notes on the ListView.



The source is based on example 10, so there are folder, file and root nodes.

Here is the procedure that displays the TreeNodes in the ListView.


   procedure TForm1.UpdateListView;
   var
      ChildNode : TTreeNode;
   begin
      lv_Link.Items.Clear;

         {If nothing is selected}
      if(  tv_eg5.Selected = nil  ) then
         Exit;

         {Does this node have a parent?}
      if(  tv_eg5.Selected.Parent <> nil  ) then
      begin
           {Add the "up to parent" node}
         with lv_Link.Items.Add do
         begin
            Caption := 'To Parent';
               {Set the image}
            ImageIndex := IMG_TO_PARENT;

               {Node that ListItem is linking to (ie Parent Node)}
            Data := tv_eg5.Selected.Parent.ItemId;
         end;
      end;


      ////////////////////////////////////////////////
      // Now get all the selected node's child nodes
      ////////////////////////////////////////////////
      lv_Link.Items.BeginUpdate;

         {Get first child}
      ChildNode := tv_eg5.Selected.GetFirstChild;

      while(  ChildNode <> nil  ) do
      begin
           {Add the "up to parent" node}
         with lv_Link.Items.Add do
         begin
            Caption := ChildNode.Text;
               {Set the image}
            ImageIndex := ChildNode.ImageIndex;

               {Node that ListItem is linking to}
            Data := ChildNode.ItemId;
         end;

            {Next child node}
         ChildNode := tv_eg5.Selected.GetNextChild(  ChildNode  );
      end;

      lv_Link.Items.EndUpdate;   
   end;

Nothing to complex here..

  1. Clear old items from the ListView
  2. If the node has a parent add the To Parent icon
  3. Add every child TreeNode as a ListItem, with the same ItemIndex and save the Node's ItemID in the ListItem's Data property


This procedure must be called whenever a change is made to the TreeView so add it to the TreeView's OnChange event


   procedure TForm1.tv_eg5Change(Sender: TObject; Node: TTreeNode);
   begin
      UpdateListView;
   end;

As well as to AddNode and RemoveClick



Next the ListView's OnClick event


   procedure TForm1.lv_LinkDblClick(Sender: TObject);
   var
      LinkNode : TTreeNode;
   begin
         {Make sure there is a selection}
      if(  lv_Link.Selected = nil  ) then
         Exit;

         {Find the node in the TreeView that corresponds to
           this ListView Item}
      LinkNode := tv_eg5.Items.GetNode(  HTreeItem(lv_Link.Selected.Data)  );

         {If the node was found, select it}
      if(  LinkNode <> nil  ) then
         LinkNode.Selected := true;
   end;
This too is very simple.
  1. Check that there is a selection
  2. Try locate the corresponding TreeNode
  3. If the node was found select it



Finally the ListView's OnEdited and OnEditing events


   procedure TForm1.lv_LinkEditing(Sender: TObject; Item: TListItem;  var AllowEdit: Boolean);
   begin
         {Can edit all but the "To Parent" item}
      AllowEdit := (Item.ImageIndex <> IMG_TO_PARENT);
   end;
Allow any Item except for the "To Parent" item to be renames



   procedure TForm1.lv_LinkEdited(Sender: TObject; Item: TListItem;  var S: String);
   var
      LinkNode : TTreeNode;
   begin
         {Find the node that is being renamed}
      LinkNode := tv_eg5.Items.GetNode(  HTreeItem(Item.Data)  );

         {If the node was found}
      if(  LinkNode <> nil  ) then
      begin
            {Does another node already have this name?}
         if(   IsDuplicateName(  LinkNode,  s,  false  )   ) then
         begin
            MessageBeep(  -1  );
            ShowMessage(  'Duplicate Name!'  );
         
               {Revert to original name}
            S := Item.Caption;
         end
         else begin
               {Name is valid so allow rename}
            LinkNode.Text := s;
         end;
      end
      else begin
         {For some reason the link was not found, give ListIem its
           old name}
         S := Item.Caption;
      end;
   end;

Same as a "normal" OnEdited event, except for the Node linking. See example 9 for more information on OnEditing and OnEdited events.



Thats all there is to it. Naturally there is a lot more that can be done. For instance allowing nodes to be added or deleted in the ListView. One way to do these kind of things is to have a procedure that is called by both the ListView and TreeView events. For the TreeView you'd call DoSomthing( TreeView.Selected ) for the ListView DoSomthing( LinkNode ), where LinkNode is found as above.






All information on these www pages is copyright (©) 1997 Andre .v.d. Merwe And may not be copied or mirrored without my permission.

color="#000000">



All information on these www pages is copyright (©) 1997 Andre .v.d. Merwe And may not be copied or mirrored without my permission.