Delphi循环一个tcxtreeList,根据字符串中的键值来检查和取消选中节点



我是delphi开发的新手,我有一个从cxTreeList-Devexpress组件继承的自定义CheckTreeList组件。当我检查列表中的一些节点时,这些值将以如下所示的格式存储到字符串中选定节点的字符串格式为图像问题是我无法通过循环遍历字符串中的节点和值来检查检查树列表中的节点。我已经尝试了下面的代码来保存和加载选中和未选中的节点。将选中的节点键值保存到字符串中是有效的,但加载节点并检查它们是无效的。以下是组件源代码

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.

在我的情况下,属性EnableStdTreebehaviour设置为true。

保存所选节点键值的代码为

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;

我尝试加载并检查节点的代码取决于字符串中的键值是

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;

一些人能检查一下并在这个问题上帮助我吗?

更新我已经更新了这个答案,以提供完整的&将TcxTreeList的复选标记保存到字符串(或TStringList(中,然后使用Q屏幕截图中的字符串格式重新加载它们的自包含示例。我忽略了Q中的代码,并从头开始编写,因为这比猜测你在代码中到底打算做什么更容易——如果我自己这样做,我不会使用Q的方法,而是将树节点的状态保存到TClientDataSet或等效的Devex中。

该示例只需要几个实用程序例程即可完成其工作,并且这些例程都以TcxTreeList或TcxTreeListNode作为输入参数,因此这些例程可以移动到另一个单元并由其他表单重新使用。

这些例程如下:

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

希望这些都是不言自明的。示例的实施部分是

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.

原始答案

设置未绑定cxTreeList的复选框列的最简单方法是将该列中的值设置为True或False。因此,假设您的cxTreeList是第0列,您可以简单地执行此

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;

我认为您可以将其编织到现有代码中。我还没有真正研究过,但是怀疑你可以把它简化很多。

最新更新