Search code examples
delphidevexpressvcldelphi-10.2-tokyo

Delphi cxtreelist looping through nodes


I have a tcxTreeList with 3 columns the first column has set property of Node.CheckGroupType := ncgCheckGroup;, and 2nd column contains Key of the First root node and third column contains the keys of all child nodes under the root node. How can i get the key of root node whether it is checked or not and keys of all checked child nodes into 2 variables to insert into db and Load back items from keys we get from DB enter image description here

My code for adding items is as below

 APNode := tv.Add;
 APNode.CheckGroupType := ncgCheckGroup;
 APNode.Values[0] := define;
 ApNode.Values[1] := dCode;

define - contains Text like Created, Released,In-Active etc.. all Root nodes dcode - contains key value of each root node like E,F,I,P,R

This is how i added child nodes for each root node

procedure TF_Form1.addPStatsToTreeList(tl: TcxTreeList; const dcode, define: string);
  function AddTreeListNode(TreeList: TcxTreeList; APNode: TcxTreeListNode; const AValues: Array of Variant; AImageIndex: Integer): TcxTreeListNode;
  begin
    Result := TreeList.AddChild(APNode);
    Result.AssignValues(AValues);
    Result.Imageindex := AImageIndex;
  end;
var
  grpnode,chNode, ANode: TcxTreeListNode;
  icnt : integer;
begin
  icnt := tl.Count;
  if Assigned(tl) then
  begin
    ANode := tl.TopNode;
    while ANode <> nil do
    begin
      ANode :=  AddTreeListNode(tl, ANode, [define, '', dcode], 0);
      ANode := TcxTreeListNode(ANode.GetNext);
    end;
  end;
end;

UPDATE

dcode is a single letter and Definition is a string, Dcode is the key for string in definition, Like a key-value pair Dcode is key and definition is value. In the tree list root nodes like Created, Released, In Active, Checked and Reactivated have different Key which is in column1 and key for subnode values like Sample, Development, Production etc.. is in 3rd column. Only Key values are saved to DB, root node keys are saved directly whether they are Checked or not. Each root node key will be saved to different rows and coming to subnodes only Checked nodes keys are saved to DB with comma separated. For Example . If Root node Created and items under it like Sample , Development are checked then root node key E is one column[DEFCODE(Primarykey)] and subnodes keys M,E (DEFINITION) with coma separated are in other column.


Solution

  • The code below shows how to save to a TClientDataSet the Checked state of a root node along with its key and a comma-separated list of the keys of its children which are checked using the method SaveTreeState.

    The method LoadTreeState clears the Checked state of each node in the ctTreeList and then re-loads the saved Checked states from the CDS.

    To use this code you need to add a TClientDataSet named CDS1 to your project, and a TDataSource and TDBGrid wired up in the usual way. Then add persistent fields to the CDS as shown in the code, compile and run.

    I hope I've understood how your cxTreeList is set up correctly, but if not, hopefully the code should be pretty straightforward to adapt to what you are actually doing.

    type
      TForm1 = class(TForm)
        cxTreeList1: TcxTreeList;
        CDS1: TClientDataSet;
        btnLoad: TButton;
        cxTreeList1Column1: TcxTreeListColumn;
        cxTreeList1Column2: TcxTreeListColumn;
        cxTreeList1Column3: TcxTreeListColumn;
        DataSource1: TDataSource;
        DBGrid1: TDBGrid;
        CDS1RootKey: TStringField;    // String[1]
        CDS1CheckedChildKeys: TStringField;  //  String[20]
        CDS1RootNodeChecked: TBooleanField;
        btnClear: TButton;
        procedure btnClearClick(Sender: TObject);
        procedure btnLoadClick(Sender: TObject);
        procedure btnSaveClick(Sender: TObject);
        procedure FormCreate(Sender: TObject);
      private
        procedure SaveTreeState;
        procedure ClearChecks;
        procedure LoadTreeState;
      end;
    
    [...]
    procedure TForm1.ClearChecks;
    var
      i,
      j : Integer;
    begin
      for i := 0 to cxTreeList1.Count - 1 do begin
        cxTreeList1.Items[i].Checked := False;
        for j := 0 to cxTreeList1.Items[i].Count - 1 do begin
          cxTreeList1.Items[i].Items[j].Checked := False;
        end;
      end;
    end;
    
    procedure TForm1.SaveTreeState;
    var
      i,
      j : Integer;
      RootNode,
      ChildNode : TcxTreeListNode;
      RootKey,
      ChildKeys : String;
    begin
      CDS1.DisableControls;
      try
        CDS1.EmptyDataSet;
    
        for i := 0 to cxTreeList1.Count - 1 do begin
          RootNode := cxTreeList1.Items[i];
          RootKey := RootNode.Values[1];
          ChildKeys := '';
          for j := 0 to RootNode.Count - 1 do begin
            ChildNode := RootNode.Items[j];
            if ChildNode.Checked then begin
              if ChildKeys <> '' then
                ChildKeys := ChildKeys + ',';
              ChildKeys := ChildKeys + ChildNode.Values[2];
            end;
          end;
          CDS1.InsertRecord([RootKey, ChildKeys, RootNode.Checked]);
        end;
      finally
        CDS1.EnableControls;
      end;
    end;
    
    procedure TForm1.LoadTreeState;
    var
      RootKey,
      ChildKey : String;
      ChildKeys : TStringList;
      i : Integer;
      RootNode,
      ChildNode : TcxTreeListNode;
    
      function FindRootNodeByKey(const Key : String) : TcxTreeListNode;
      var
        i : Integer;
      begin
        for i := 0 to cxTreeList1.Count - 1 do begin
          Result := cxTreeList1.Items[i];
          if CompareText(Result.Values[1], Key) = 0 then
            Exit;
        end;
        Result := Nil;
      end;
    
      function FindChildNodeByKey(ParentNode : TcxTreeListNode; const Key : String) : TcxTreeListNode;
      var
        i : Integer;
      begin
        for i := 0 to ParentNode.Count - 1 do begin
          Result := ParentNode.Items[i];
          if CompareText(Result.Values[2], Key) = 0 then
            Exit;
        end;
        Result := Nil;
      end;
    
    begin
      cxTreeList1.BeginUpdate; // prevent treelist from updating while we load its state
      ClearChecks;
      try
        //  ChildKeys is a TStringList that we'll use to parse the list of child keys into individual values
        ChildKeys := TStringList.Create;
        try
          CDS1.DisableControls;
          try
            CDS1.First;
            while not CDS1.Eof do begin
              RootKey := CDS1RootKey.AsString;
              RootNode := FindRootNodeByKey(RootKey);
              Assert(RootNode <> Nil);  // check that we found the root node
              RootNode.Checked := CDS1RootNodeChecked.AsBoolean;
              ChildKeys.CommaText := Trim(CDS1CheckedChildKeys.AsString);  // Loading ChildKeys 
              //  by this assignment is what causes it to be parsed into individual keys
              for i := 0 to ChildKeys.Count - 1 do begin
                ChildKey := ChildKeys[i];
                ChildNode := FindChildNodeByKey(RootNode, ChildKeys[i]);
                Assert(ChildNode <> Nil);
                ChildNode.Checked := True;
              end;
              CDS1.Next;
            end;
          finally
            CDS1.EnableControls;
          end;
        finally
          ChildKeys.Free;
        end;
      finally
        cxTreeList1.EndUpdate;
      end;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      CDS1.CreateDataSet;
      ClearChecks;
    end;
    
    procedure TForm1.btnClearClick(Sender: TObject);
    begin
      ClearChecks;
    end;
    
    procedure TForm1.btnLoadClick(Sender: TObject);
    begin
      LoadTreeState;
    end;
    
    procedure TForm1.btnSaveClick(Sender: TObject);
    begin
      SaveTreeState;
    end;
    
    end.