I am pretty new to delphi development, I have a custom CheckTreeList component inherited from cxTreeList Devexpress component. When i check some nodes in a list, Those values are stored to a string in a format as shown below String Format for selected nodes as image The issue is I am not able to check the nodes of the checktreelist by looping through the nodes and values in the string. I have tried the below code for saving and loading the checked and unchecked nodes. Saving the checked node key values to string is working, But Loading the nodes and checking them is not working. The below is the component source code
unit DXCheckTreelist;
interface
uses
System.Classes, cxTL, cxLookAndFeelPainters;
type
TdxUnboundTreeListNode = class(TcxUnboundTreeListNode)
protected
procedure SetCheckState(AValue: TcxCheckBoxState); override;
end;
TdxCheckTreeList = class(TcxTreeList)
Private
FEnableStdTreebehaviour : Boolean;
protected
function CreateNode: TcxTreeListNode; override;
Published
Property EnableStdTreebehaviour: Boolean read FEnableStdTreebehaviour write FEnableStdTreebehaviour default False;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('DX Components', [TdxCheckTreeList]);
end;
{ TdxCheckTreeList }
function TdxCheckTreeList.CreateNode: TcxTreeListNode;
begin
Result := TdxUnboundTreeListNode.Create(Self);
Changes := Changes + [tcStructure];
end;
{ TdxUnboundTreeListNode }
procedure TdxUnboundTreeListNode.SetCheckState(AValue: TcxCheckBoxState);
var
ParentNode : TdxUnboundTreeListNode;
PrevCheckState: TcxCheckBoxState;
const
AState: array[TcxCheckBoxState] of TcxTreeListNodeCheckInfos = ([], [nciChecked], [nciGrayed]);
AParentCheckState: array[Boolean] of TcxCheckBoxState = (cbsGrayed, cbsChecked);
begin
if TdxCheckTreeList(TreeList).FEnableStdTreebehaviour then
begin
inherited;
Exit;
end;
if not CanChecked then
begin
State := State - [nsCheckStateInvalid];
Exit;
end;
PrevCheckState := CheckState;
CheckInfo := CheckInfo - [nciChecked, nciGrayed] + AState[AValue] + [nciChangeCheck];
try
if (CheckState in [cbsChecked, cbsUnchecked]) and HasChildren then
begin
LoadChildren;
if AValue = cbsUnchecked then
SetChildrenCheckState(CheckState, nil);
end;
ParentNode := TdxUnboundTreeListNode(Parent);
if ParentNode <> nil then
begin
if ParentNode.IsRadioGroup and Checked then
ParentNode.SetChildrenCheckState(cbsUnchecked, Self);
if not (nciChangeCheck in ParentNode.CheckInfo) and (ParentNode <> Root) then
ParentNode.CheckState := cbsChecked;
end;
finally
CheckInfo := CheckInfo - [nciChangeCheck];
State := State - [nsCheckStateInvalid];
if CanChecked then
Repaint(True);
if (PrevCheckState <> CheckState) and Assigned(TcxTreeList(TreeList).OnNodeCheckChanged) then
TcxTreeList(TreeList).OnNodeCheckChanged(TreeList, Self, CheckState);
end;
end;
end.
In my case the property EnableStdTreebehaviour
is set to true.
Code for saving selected node key values is
procedure TfrmTreeList.btnSaveDataClick(Sender: TObject);
var
I, J: Integer;
node, cnode: TcxTreeListNode;
Result: String;
begin
result:= '';
for i := 0 to ctvMandatory.Count - 1 do
begin
node := TcxTreeListNode(ctvMandatory.Items[i]);
if ctvMandatory.Items[i].CheckState in [cbsChecked, cbsGrayed] then
begin
if node.Level = 0 then Result:= Result + '[' + node.Values[1] + ']' + ',';
for J := 0 to ctvMandatory.Items[i].Count - 1 do
begin
cnode := ctvMandatory.Items[i].Items[J];
if (cnode.Checked) and (cnode.Level = 1) then
begin
Result:= Result + cnode.Values[2] + ',';
end;
end;
end;
end;
if (Result <> '') and (Result[Length(result)] = ',') then
result:= Copy(Result, 1, length(Result) -1 );
Memo.Clear;
if result <> '' then
begin
Memo.Lines.Add(Trim(Result));
csv := result;
end;
for i := 0 to ctvMandatory.count - 1 do
begin
node := TcxTreeListNode(ctvMandatory.Items[i]);
ctvMandatory.Items[i].Checked := False;
end;
end;
Code i tried for loading and check node depend on keyvalue from string is
procedure TfrmTreeList.btnLoadDataClick(Sender: TObject);
var
i, j, X: integer;
node, cnode: TcxTreeListNode;
sl,s2: TStringList;
str: string;
key, value, val: string;
begin
chbAll.Checked:= csv = 'All';
ctvMandatory.BeginUpdate;
if chbAll.Checked then
begin
for i:= 0 to ctvMandatory.AbsoluteCount - 1 do
ctvMandatory.Items[I].Checked := True;
ctvMandatory.EndUpdate;
SetMandatoryText;
Exit;
end;
for i:= 0 to ctvMandatory.Count - 1 do
ctvMandatory.Items[I].Checked := False;
if csv = 'All' then
begin
for i:= 0 to ctvMandatory.AbsoluteCount - 1 do
ctvMandatory.Items[I].Checked := True;
end
else
if (length(csv) > 0) and (Pos(']', csv) = 0) then
begin
for i := 0 to ctvMandatory.Count - 1 do
begin
node:= TcxTreeListNode(ctvMandatory.Items[i]);
if node.Level = 0 then
ctvMandatory.Items[i].Checked:= True
else
if (node.Level = 1) and IsValueInCSV(csv, node.Values[1]) then
begin
ctvMandatory.Items[i].Checked := True;
end;
end;
end
else
begin
sl:= TStringList.Create;
sl.Delimiter:= ',';
sl.DelimitedText:= csv;
node:= nil;
s2:= TStringList.Create;
s2.Delimiter:= ',';
for str in sl do
begin
if (pos('[', str) > 0) then
begin
if (value <> '') and (value[Length(value)] = ',') then
value := Copy(value, 1, length(value) -1);
s2.DelimitedText:= value;
if (node <> nil) and (value <> '') and (node.HasChildren) then
begin
for I := 0 to ctvMandatory.Count - 1 do
begin
while Node <> Nil do
begin
node:= TcxTreeListNode(ctvMandatory.Items[I]);
node:= node.getFirstChild;
if not node.Checked then
begin
val := '';
for val in s2 do
begin
node.Checked := true;
node.getNextSibling;
end;
end;
s2.Clear;
end;
end;
end;
value:= '';
val := '';
key:= ReplaceStr(str, '[', '');
key:= ReplaceStr(key, ']', '');
for I := 0 to ctvMandatory.Count - 1 do
begin
if (TcxTreeListNode(ctvMandatory.Items[i]).Values[1] = key) and ((ctvMandatory.Items[i]).Level = 0) then
begin
node:= TcxTreeListNode(ctvMandatory.Items[i]);
Break;
end;
end;
end
else
begin
value:= value + str + ',';
end;
end;
if (value <> '') and (value[Length(value)] = ',') then
value := Copy(value, 1, length(value) -1);
s2.DelimitedText:= value;
if (node <> nil) and (value <> '') and (node.HasChildren) then
begin
for I := 0 to ctvMandatory.Count - 1 do
begin
while Node <> Nil do
begin
node:= TcxTreeListNode(ctvMandatory.Items[I]);
node:= node.getFirstChild;
if not node.Checked then
begin
val := '';
for val in s2 do
begin
node.Checked := true;
node.getNextSibling;
end;
end;
s2.Clear;
end;
end;
end;
sl.Free;
s2.Free;
end;
ctvMandatory.EndUpdate;
SetMandatoryText;
end;
function TfrmTreeList.IsValueInCSV(const CSV, Value: string): Boolean;
begin
Result := IsValueInCSV(CSV, Value, False);
end;
function TfrmTreeList.IsValueInCSV(const CSV, Value: string; ResultIfBothEmpty: Boolean): Boolean;
begin
if Trim(CSV) = Trim(Value) then
begin
if Trim(Value) = '' then
Result := ResultIfBothEmpty
else
Result := True;
end
else
Result := MatchStr(Value, SplitString(CSV, ','));
end;
Can some please check and help me on this issue?
Update I've updated this answer to provide a complete & self-contained example of saving the check marks of the TcxTreeList to a string (or TStringList) and then re-load them, both using the string format in the Q's screenshot. I've ignored the code in the Q and written it all from scratch, because that was easier than trying to guess what exactly you are intending to do in your code - if I was doing this myself, I wouldn't use the Q's method but instead save the tree nodes' states to a TClientDataSet or the Devex equivalent.
The example requires only a few utility routines to do its job and these all take a TcxTreeList or TcxTreeListNode as an input parameter so these could be moved to another unit and re-used by other forms.
These routines are as follows:
function RootNodeToString(RootNode : TcxTreeListNode) : String;
// This saves a Root node and its subkeys in the format show in the Q's screenshot
// Note: This does NOT save the RootNode's checked state because the q did not define
// whether it should
function TreeListNodesToString(TreeList : TcxTreeList) : String;
// This saves all the TreeList's Root nodes and their subkeys
// in the format show in the Q's screenshot
function RootNodeFromName(TreeList : TcxTreeList; AName : String) : TcxTreeListNode;
// Finds the RootNode having a given name or NIL if not found
function ChildNodeFromName(RootNode : TcxTreeListNode; const AName : String) : TcxTreeListNode;
// Finds the ChildNode (of a RootNode) having a given name or NIL if not found
function TreeListNodesToString(TreeList : TcxTreeList) : String;
// This saves all the TreeList's Root nodes and their subkeys
// in the format show in the Q's screenshot
function RootNodeFromName(TreeList : TcxTreeList; AName : String) : TcxTreeListNode;
// Finds the RootNode having a given name or NIL if not found
function ChildNodeFromName(RootNode : TcxTreeListNode; const AName : String) : TcxTreeListNode;
// Finds the ChildNode (of a RootNode) having a given name or NIL if not found
procedure ClearChecks(TreeList : TcxTreeList; ClearChildren : Boolean);
// Clears all the checkmark in a cxTreeList
Hopefully, these are all self-explanatory. The Implementation section of the example is
const
iCheckCol = 0; // the number of the checkbox column
iNameCol = 1; // the number of the name column
function RootNodeToString(RootNode : TcxTreeListNode) : String;
// This saves a Root node and its subkeys in the format show in the Q's screenshot
// Note: This does NOT save the RootNode's checked state because the q did not define
// whether it should
var
j : Integer;
ANode : TcxTreeListNode;
begin
Result := '[' + RootNode.Values[iNameCol] + ']';
for j := 0 to RootNode.Count - 1 do begin
ANode := RootNode.Items[j];
if ANode.Values[iCheckCol] then
Result := Result + ',' + ANode.Values[iNameCol];
end;
end;
function TreeListNodesToString(TreeList : TcxTreeList) : String;
// This saves all the TreeList's Root nodes and their subkeys
// in the format show in the Q's screenshot
var
i : Integer;
begin
Result := '';
for i := 0 to TreeList.Count - 1 do begin
if Result <> '' then
Result := Result + ',';
Result := Result + RootNodeToString(TreeList.Items[i]);
end;
end;
function RootNodeFromName(TreeList : TcxTreeList; AName : String) : TcxTreeListNode;
// Finds the RootNode having a given name or NIL if not found
var
i : Integer;
begin
// First remove the square brackets, if any
if AName[1] = '[' then
Delete(AName, 1, 1);
if AName[Length(AName)] = ']' then
Delete(AName, Length(AName), 1);
// Next, look for AName in TreeList
for i := 0 to TreeList.Count - 1 do begin
Result := TreeList.Items[i];
if CompareText(Result.Values[iNameCol], AName) = 0 then exit; //CompareText is case-insensitive
end;
Result := Nil; // if we get to here, we didn't find it
end;
function ChildNodeFromName(RootNode : TcxTreeListNode; const AName : String) : TcxTreeListNode;
// Finds the ChildNode (of a RootNode) having a given name or NIL if not found
var
i : Integer;
begin
for i := 0 to RootNode.Count - 1 do begin
Result := RootNode.Items[i];
if CompareText(Result.Values[iNameCol], AName) = 0 then exit; //CompareText is case-insensitive
end;
Result := Nil; // if we get to here, we didn't find it
end;
procedure ClearChecks(TreeList : TcxTreeList; ClearChildren : Boolean);
// Clears all the checkmark in a cxTreeList
var
i,
j : Integer;
RootNode,
ANode : TcxTreeListNode;
begin
// This clears the checkmarks from all the Root nodes and, optionally,
// their children
TreeList.BeginUpdate;
try
for i := 0 to TreeList.Count - 1 do begin
RootNode := TreeList.Items[i];
RootNode.Values[iCheckCol] := False;
for j := 0 to RootNode.Count - 1 do begin
ANode := RootNode.Items[j];
ANode.Values[iCheckCol] := False;
end;
end;
finally
TreeList.EndUpdate;
end;
end;
procedure LoadTreeListChecksFromString(TreeList : TcxTreeList; const Input : String);
// This clears the TreeList's checkmarks and then sets the checkmarks
// from the Input string.
var
RootKey,
SubKey : String;
RootNode,
ChildNode : TcxTreeListNode;
TL : TStringList;
i : Integer;
begin
TreeList.BeginUpdate;
try
// First, clear the treelist's checkmarks
ClearChecks(TreeList, True);
// Next load the Input string into a TStringList to split it into a series
// of Root keys and Child keys
TL := TStringList.Create;
try
TL.CommaText := Input;
// The i variable will be used to iterate through the contents of the StringList
i := 0;
while i <= TL.Count - 1 do begin
// The first string in TL should be Root key
RootKey := TL[i];
RootNode := RootNodeFromName(TreeList, RootKey);
Assert(RootNode <> Nil); // will raise exception if RootNode not found
// The question does not say what should happen about the checkmark on the root nodes
Inc(i);
// Now, scan down the entries below the Root key and process retrive each if its sub-keys;
// stop when we get to the next Root key or reach the end of the Stringlist
while (i <= TL.Count - 1) and (Pos('[', TL[i]) <> 1) do begin
SubKey := TL[i];
ChildNode := ChildNodeFromName(RootNode, SubKey);
ChildNode.Values[iCheckCol] := True;
Inc(i);
end;
end;
finally
TL.Free;
end;
finally
TreeList.EndUpdate;
end;
end;
procedure TForm1.SetUpTreeList;
// This sets up the form' cxTreeList with some Root nodes and Child nodes
// Some of the ChildNode's checkmarks are set to save having to click around
// to set things up manually
var
i,
j : Integer;
RootNode,
ANode : TcxTreeListNode;
begin
for i := 0 to 3 do begin
RootNode := cxTreeList1.Add;
RootNode.AssignValues([Odd(i), 'RT' + IntToStr(i + 1)]);
for j := 0 to 4 do begin
ANode := RootNode.AddChild;
ANode.AssignValues([Odd(i + j), Char(j + Ord('A'))]);
end;
RootNode.Expand(True);
end;
edSavedKeys.Text := TreeListNodesToString(cxTreeList1);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
SetUpTreeList;
end;
procedure TForm1.btnClearClick(Sender: TObject);
begin
ClearChecks(cxTreeList1, True);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SetNodeChecked(cxTreeList1.FocusedNode, not cxTreeList1.FocusedNode.Values[iCheckCol]);
end;
procedure TForm1.SetNodeChecked(Node : TcxTreeListNode; Value : Boolean);
begin
if Node = Nil then exit; // do nothing
Node.Values[iCheckCol] := Value;
end;
procedure TForm1.btnLoadClick(Sender: TObject);
begin
ClearChecks(cxTreeList1, True);
LoadTreeListChecksFromString(cxTreeList1, edSavedKeys.Text);
end;
end.
Original answer
The easiest way afaik to set the checkbox column of an unbound cxTreeList is simply to set the value in that column to True or False. So, assuming the CheckBox column of your cxTreeList is column 0, you can simply do this
procedure TForm1.SetNodeChecked(Node : TcxTreeListNode; Value : Boolean);
begin
if Node = Nil then exit; // do nothing
Node.Values[0] := Value;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// toggle the checkbox of the focused node using code
SetNodeChecked(cxTreeList1.FocusedNode, not cxTreeList1.FocusedNode.Values[0]);
end;
I assume you can weave this into your existing code. I haven't really studied it, but suspect that you could simplify it quite a lot.