Delphi根据字符串中的键值对tcxtreeList进行检查和取消检查节点循环

问题描述 投票:1回答:1

我对delphi开发非常陌生,我有一个自定义的CheckTreeList组件,该组件继承自cxTreeList Devexpress组件。当我检查列表中的某些节点时,这些值以如下所示的格式存储到字符串中String Format for selected nodes as image问题是我无法通过遍历字符串中的节点和值来检查清单树的节点。我尝试了下面的代码来保存和加载选中和未选中的节点。将已检查的节点键值保存为字符串是可行的,但是无法加载节点并对其进行检查。以下是组件源代码

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;

有人可以在这个问题上检查并帮助我吗?

delphi devexpress vcl delphi-10.2-tokyo
1个回答
1
投票

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;

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

© www.soinside.com 2019 - 2024. All rights reserved.