1. Computing & Technology

Discuss in my forum

TShellTreeView with CheckBoxes - Custom Delphi TShellTreeView Enhancement

Showing the check boxes; Updating when a click occurs

By , About.com Guide

As said, the TGtroCustomCheckShellTreeView component needs supplementary capabilities:

  • showing check boxes next to each node;
  • detecting where the user clicks;
  • updating the component once a user has clicked a check box; and
  • producing the list of files.

Showing the check boxes

Showing the check boxes is fairly straightforward: simply override the CreateWnd() method of the ancestor as follows:
 procedure TGtroCustomCheckShellTreeView.CreateWnd;
 var
   Style: Integer;
 begin
   inherited CreateWnd;
   Style:= GetWindowLong(Handle, GWL_STYLE) ;
   Style:= Style or TVS_CHECKBOXES;
   SetWindowLong(Handle, GWL_STYLE, Style) ;
 end;
 

Detecting where the click occurred

Next, I had to detect the clicks on the check boxes and at the same time retain the basic capabilities of the ancestor when the user expands or collapses nodes. This is done using the WMLButtonDown() protected method which captures the WM_LBUTTONDOWN Windows message and probes where the click occurred on the screen.
 procedure TGtroCustomCheckShellTreeView.WMLButtonDown(var Msg: TWMLButtonDown) ;
 var
   HitTests: THitTests;
   Node: TTreeNode;
   OldState: Boolean;
 begin
   LockWindowUpdate(Handle) ; // prevents flicker
   try
     Node:= GetNodeAt(Msg.XPos, Msg.YPos) ; // reference of the node that was clicked
     OldState:= IsNodeChecked(Node) ; // Check the status of the node
     HitTests:= GetHitTestInfoAt(Msg.XPos, Msg.YPos) ; // where was the node clicked
     if htOnStateIcon in HitTests then // Click on the checkbox
       CheckIt(Node, not Node.Expanded, not OldState) ; // produce the list of files
     if htOnLabel in HitTests then Select(Node) ; // Click on the label
     if htOnButton in HitTests then // Click on the button
     if Node.Expanded then Node.Collapse(False)
       else
     Node.Expand(False) ;
   finally
     LockWindowUpdate(0) ;
   end; // try...finally
 end;
 
Lets see what it does. The call to LockWindowUpdate() prevents flicker of the component during the process. Then,after the identification of the node on which the click was observed, the following questions are raised: was its check box previously checked or unchecked? Was the click on the check box, on the label, on the + or - button? The answers to these questions receives specific answer in the method, in particular when the check box is clicked.

Updating when a click occurs

Now, let's see what happens when a check box is clicked. As shown above in the highlighted statement in WMLButtonDown(), the private method CheckIt() is called.
 procedure TGtroCustomCheckShellTreeView.CheckIt(const Node: TTreeNode; IncludeSubs: Boolean; State: Boolean) ;
 var
   Pattern: string;
   ANode: TTreeNode;
   Level: Integer;
   List: TStringList;
 begin
   Items.BeginUpdate;
   try
     SetNodeChecked(Node, State) ; // Check/uncheck the node
     EnumFiles(Node, State, FList) ;
     Pattern:= GetNodePath(Node) + '\'; // Get the path of the node (pattern)
     if IncludeSubs then
     begin
       FPatterns.Add(Pattern + '/s') ; // Load the pattern in the FPatterns list
       Level:= Node.Level;
       Node.Expand(True) ; // absolutely necessary as GetNext sees only visible nodes
       try
         ANode:= Node.getFirstChild;
         while (ANode <> nil) and (Level < ANode.Level) do
         begin
           if State <> IsNodeChecked(ANode) then
           begin
             SetNodeChecked(ANode, State) ;
             EnumFiles(ANode, State, FList) ;
           end;
           ANode:= ANode.GetNext;
         end;
       finally
         Node.Collapse(True) ;
       end;
     end
     else FPatterns.Add(Pattern) ; // Load the pattern in the FPatterns list
   finally
     Items.EndUpdate;
   end; // try...finally
   if Assigned(FOnCheckChanged) then FOnCheckChanged(Self, Node, State, FList) ;
 end;
 
Its first role is to update the user interface by showing check marks on the check box that was clicked as well as on the check boxes of all its sub-nodes if the node that was clicked was collapsed. The SetNodeCheck() method set the state of the node to checked or not checked depending on the value of State whereas EnumFiles() enumerates the files contained in the folder and add them to the list of files is State is true. Otherwise, the enumerated files are removed from the list of files.

If IncludeSubs is true (the node was collapsed), the node is expanded temporarily so that its sub-nodes are created and made available to the GetFirstChild() and GetNext() methods of the base component. All the sub-nodes are then scanned and those nodes which are in a state different from the value of State are handled once again by the SetNodeChecked() and the EnumFiles() methods. The scan is terminated when the list of nodes is exhausted or when the process meets a node at the same level as the starting node.

©2012 About.com. All rights reserved.

A part of The New York Times Company.