Delphi 循环 tcxtreeList 以根据字符串中的键值检查和取消检查节点
Delphi Loop a tcxtreeList for check and uncheck nodes depending on key values from string
我是 delphi 开发的新手,我有一个从 cxTreeList Devexpress 组件继承的自定义 CheckTreeList 组件。
当我检查列表中的某些节点时,这些值以如下所示的格式存储到字符串中
String Format for selected nodes as image
问题是我无法通过遍历字符串中的节点和值来检查 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;
有人可以检查并帮助我解决这个问题吗?
Update 我已经更新了这个答案以提供一个完整且独立的示例,将 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.
原回答
afaik 设置未绑定 cxTreeList 的复选框列的最简单方法是简单地
将该列中的值设置为 True 或 False。因此,假设您的 CheckBox 列
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;
我假设您可以将其编入您现有的代码中。我还没有真正研究过,但是
怀疑您可以大大简化它。
我是 delphi 开发的新手,我有一个从 cxTreeList Devexpress 组件继承的自定义 CheckTreeList 组件。 当我检查列表中的某些节点时,这些值以如下所示的格式存储到字符串中 String Format for selected nodes as image 问题是我无法通过遍历字符串中的节点和值来检查 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;
有人可以检查并帮助我解决这个问题吗?
Update 我已经更新了这个答案以提供一个完整且独立的示例,将 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.
原回答
afaik 设置未绑定 cxTreeList 的复选框列的最简单方法是简单地 将该列中的值设置为 True 或 False。因此,假设您的 CheckBox 列 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;
我假设您可以将其编入您现有的代码中。我还没有真正研究过,但是 怀疑您可以大大简化它。