如何保持多个虚拟树视图节点的检查状态同步?

2024-02-18

我的树有 2 层节点 - 它是联系人列表风格树。

我的问题是,我想检查所有“联系人类别”中的每个联系人。这是我的联系人列表现在的屏幕截图(是的,我有权发布它)

如你所见,托德·赫希已在类别中选中测试类别,但不在所有联系人。我想要实现的目标是让联系人拥有相同的检查状态在每个类别中。

示例:我在“测试”类别中选中 Todd Hirsch - Todd Hirsch 会在“所有联系人”(以及所有其他类别)中自动选中。如果我在“所有联系人”中检查托德·赫希,他也会在“测试类别”中检查。如果我在“所有联系人”中取消选中托德·赫希,他在“测试类别”中也将取消选中。

我尝试通过 VirtualStringtree 的 OnChecking 事件来完成此操作,通过循环遍历树中每个节点的整个树,但是当联系人列表很大(2000 +)时,它非常慢,当有 5000 + 时,它甚至可能让我的程序崩溃(应用程序已停止工作)

你有什么建议?

这是我用来确保联系人只检查一次的代码。 (这不是我want现在,但这就是我现在正在使用的。)

////////////////////////////////////////////////////////////////////////////////
/// HasDuplicateChecked
////////////////////////////////////////////////////////////////////////////////
Function HasDuplicateChecked(Node: PVirtualNode): PVirtualNode;
Var
  ParentNode, ChildNode: PVirtualNode;
  I, J: Integer;
Begin

  // IHCW
  Result := Nil;

  // Get the first node of the tree..
  ParentNode := VT.GetFirst;

  // Loop thru the parent nodes.
  for I := 0 to VT.RootNodeCount - 1 do
  begin
    // Get the first child node.
    ChildNode := ParentNode.FirstChild;
    // Loop thru the children..
    for J := 0 to ParentNode.ChildCount - 1 do
    begin
      // If the ChildNode is checked...
      if NodeIsChecked(ChildNode) then
        // And it is NOT the passed node..
        if ChildNode <> Node then
          // but the data matches..
          if GetData(ChildNode).SkypeID = GetData(Node).SkypeID then
          begin
            // Then pass the Childnode as a result, and EXIT!
            Result := ChildNode;
            Exit;
          end;
      // Next child..
      ChildNode := ChildNode.NextSibling;
    end;
    // Next parent...
    ParentNode := ParentNode.NextSibling;
  end;

End;


////////////////////////////////////////////////////////////////////////////////
/// vtSkypeChecking
////////////////////////////////////////////////////////////////////////////////
procedure TSkypeListEventHandler.vtSkypeChecking(Sender: TBaseVirtualTree;
  Node: PVirtualNode; var NewState: TCheckState; var Allowed: Boolean);
Var
  Level: Integer;
  I: Integer;
  Child: PVirtualNode;
begin
  // Allow the checking..
  Allowed := True;
  // Get the Level..
  Level := Sender.GetNodeLevel(Node);

  // If the level is 0 (Category Level)
  if Level = 0 then
  begin
    // And if the Node's Childcount is more than 0
    if Node.ChildCount > 0 then
    Begin
      // Get the first child..
      Child := Node.FirstChild;
      // Loop thru the children..
      for I := 0 to Node.ChildCount - 1 do
      begin
        // Set the checkstate, and go next..
        Child.CheckState := NewState;
        Child := Child.NextSibling;
      end;
    End;
  end;


  // If the level is 1 (User Level)
  if Level = 1 then
  begin
    // and if the Node's parent is not Nil..
    if Node.Parent <> nil then
    begin
      // aaand, if the new state is Unchecked...
      if (NewState = csUncheckedNormal) or (NewState = csUncheckedPressed) then
      begin
        // .. and if the node checkstate is checked..
        if NodeIsChecked(Node) then
        Begin
          // Set the PARENT node's checkstate to Unchecked!
          Node.Parent.CheckState := csUncheckedNormal;
        End;

      end;
      // BUT, if there is a DUPLICATE of the node, screw the above, and
      // forbid the checking!
      if HasDuplicateChecked(Node) <> nil then
        Allowed := False;

    end;
  end;

  // Uncheck all the duplicates.
  UncheckDuplicates;

  // Refresh the Tree
  Sender.Refresh;

end;

First, OnChecking是要处理的错误事件。你要OnChecked. OnChecking其实只是问,“这个节点的检查状态是否允许改变?”它是not意味着去检查其他节点。使用OnChecked为了那个原因。

其次,您不需要处理类别节点的检查状态。打开toAutoTristateTracking选项和控件将自动调整所有相关子节点和父节点的状态。 (更改父级,所有子级都会更改。更改子级,父级将更改为“不确定”。)

不过,您的代码似乎走在正确的轨道上。当子节点发生变化时,需要查找所有子节点other树的其余部分中该节点的副本,并更改​​其检查状态以匹配刚刚更改的节点的新状态。执行该操作所需的时间应该与树中节点的数量成线性关系——节点数量加倍,并且大约需要两倍的时间来查找所有重复项。但即使有几千个节点,它也应该在眨眼之间完成。如果需要更长的时间,则还有一些其他耗时的操作未在此处显示。尝试使用分析器来发现瓶颈。

下面的代码将遍历树中的所有节点。它暂时禁用OnChecked事件处理程序,因为否则,每次更改重复项之一的状态时,该事件都会再次运行。如果新的检查状态与当前的检查状态相同,则该事件不会运行,因此不存在无限递归的危险,但禁用该事件确实可以防止它在树中进行大量冗余遍历。

procedure PropagateCheckState(Tree: TVirtualStringTree; Node: PVirtualNode);
var
  Data: PNodeData;
  TargetID: string;
  Parent: PVirtualNode;
  FoundOne: Boolean;
begin
  Data := Tree.GetNodeData(Node);
  TargetID := Data.SkypeID;

  Parent := Tree.GetFirst;
  while Assigned(Parent) do begin
    // Assume no user appears twice in the same category
    if Parent <> Tree.NodeParent[Node] then begin
      FoundOne := False;
      Child := Tree.GetFirstChild(Parent);
      while Assigned(Child) and not FoundOne do begin
        Data := Tree.GetNodeData(Child);
        if Data.SkypeID = TargetID then begin
          // Found a duplicate. Sync it with Node.
          Tree.CheckState[Child] := Tree.CheckState[Node];
          FoundOne := True;
        end;
        Child := Tree.GetNextSibling(Child);
      end;
    end;
    Parent := Tree.GetNextSibling(Parent);
  end;
end;

procedure TSkypeListEventHandler.vtSkypeChecked(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  CheckedEvent: TVTChangeEvent;
begin
  if Sender.GetNodeLevel(Node) = 0 then
    exit; // The tree cascades changes automatically

  Assert(Sender.GetNodeLevel(Node) = 1, 'Unexpected node level');
  // We'll be accessing members that are protected in TBaseVirtualTree, but
  // they're public in TVirtualStringTree, so make sure we're still operating
  // on the same tree.
  Assert(Sender = vtSkype);

  CheckedEvent := vtSkype.OnChecked;
  vtSkype.OnChecked := nil;
  try
    PropagateCheckState(vtSkype, Node);
  finally
    vtSkype.OnChecked := CheckedEvent;
  end;
end;

如果您的数据结构具有与给定用户 ID 关联的所有节点的列表,则情况会更加简单:

procedure PropagateCheckState(Tree: TVirtualStringTree; Node: PVirtualNode);
var
  Data: PNodeData;
  i: Integer;
begin
  Data := Tree.GetNodeData(Node);

  for i := 0 to Pred(Data.User.Nodes.Count) do
    Tree.CheckState[Data.User.Nodes[i]] := Tree.CheckState[Node];
end;

即使您继续将所有数据存储在树控件本身中(您已经多次被告知这是一个坏主意),您仍然可以使用辅助数据结构来充当index对于树节点,关闭用户 ID。如果您有足够新的 Delphi 版本,您可以使用TDictionary<string, TList<PVirtualNode>>. Then PropagateCheckState可能看起来像这样:

uses Generics.Collections;

var
  UserNodes: TDictionary<string, TList<PVirtualNode>>;

procedure PropagateCheckState(Tree: TVirtualStringTree; Node: PVirtualNode);
var
  Data: PNodeData;
  Nodes: TList<PVirtualNode>;
  i: Integer;
begin
  Data := Tree.GetNodeData(Node);

  if not UserNodes.TryGetValue(Data.SkypeID, Nodes) then
    exit; // Weird. The node's ID isn't in the index at all.

  for i := 0 to Pred(Nodes.Count) do
    Tree.CheckState[Nodes[i]] := Tree.CheckState[Node];
end;

确保更新UserNodes每当您在类别中添加或删除用户时都会创建索引。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何保持多个虚拟树视图节点的检查状态同步? 的相关文章

随机推荐

  • 什么是“部署 ID”和“部署唯一名称”

    我不明白 Azure 云服务的 部署 ID 和 部署唯一名称 是什么 Azure Powershell 工具似乎会自动生成它们 但它们的用途是什么 如何使用它们 当 部署槽 不够用时 在什么意义上部署名称是 唯一的 部署ID是 Window
  • 斯坦福核心 NLP 是否支持德语词形还原?

    我发现了与斯坦福核心 NLP 兼容的德语解析和后标记模型 但是我无法使德语词形还原工作正常进行 有办法这样做吗 抱歉 据我所知 Stanford CoreNLP 不存在德语词形还原的实现
  • PHP - 如何重命名对象属性?

    我想知道如何在 PHP 中重命名对象属性 例如
  • 使用 ESM 在浏览器中动态或静态导入 json

    我运行以下示例 而 JS 之上没有捆绑器 index js async gt const mod await import index json console log mod file index json Chrome 80 无法加载
  • Django:员工装饰者

    我正在尝试为 Django 编写一个 仅限员工 的装饰器 但我似乎无法让它工作 def staff only error Only staff may view this page def dec view func def view re
  • 两个复杂度 O((2n + 1)!) 和 O(n!) 相等吗?

    这可能是一个幼稚的问题 但我对 Big O 表示法和复杂性的概念很陌生 无法找到任何答案 我正在处理一个算法 2n 1 次检查条件 我可以说问题的复杂度是 O n 还是复杂度是 O 2n 1 Use 斯特林近似 http en wikipe
  • JAVA中存储二维数组的数据结构

    我正在寻找一种数据结构来存储二维整数数组 List 是正确的数据结构还是我应该使用另一种数据结构 有人可以给我一个关于如何创建这样的数据结构以及如何添加二维数组的简短示例吗 编辑 我想要一个数据结构 在其中存储 int 11 7 数组 例如
  • MySQL 中 BLOB 到长文本的转换

    我在 MySQL 中有一个数据类型为 BLOB 的列 我正在使用 Crystal Reports 来生成一些报告 我的问题是 BLOB 列如果包含图片以外的数据 将显示空白数据 我从数据库获取的数据只是文本 所以我想将 BLOB 列转换为其
  • 无法从设备存储中删除文件android

    我正在开发一个笔记应用程序 其中列表视图在将笔记存储在由我的实用程序类控制的内部存储中后显示笔记 我刚刚在上下文菜单中实现了删除选项 删除选项可以很好地删除每个选定的列表视图项目 但是 当我刷新列表活动或添加新注释时 已删除的注释不断重新出
  • 一旦我将项目添加到 XCode 4.0.2,它就会崩溃

    它给出了这个错误 ASSERTION FAILURE in SourceCache IDEXcode3ProjectSupport IDEXcode3ProjectSupport 269 Xcode3Sources XcodeIDE Fra
  • 如何使用 jszip 库压缩文件

    我正在开发一个使用 HTML5 和 jquery 的移动离线应用程序 我想使用 jszip 从本地存储备份文件 下面是我所做的代码片段 if localStorageKeys length gt 0 for var i 0 i lt loc
  • android:覆盖来电屏幕

    我想在来电屏幕上添加一些附加信息 为此 在我的应用程序中 我正在检查 PHONE STATE 并在 RINGING 上 我正在调用一项活动 在此活动中 我设置如下文本视图 它按预期工作 文本将添加到来电屏幕 问题是 如果我在我的应用程序中
  • 使用正则表达式和 re 获取括号之间的文本

    我有一个字符串数组 我想从中提取特定内容 link description button text 我想得到以下输出 link description button text 对于数组中的每个字符串 我执行以下操作 str re finda
  • Create-React-App:从 node_module 目录包含 CSS 的最佳方法是什么

    我正在尝试在我的 create react app 项目中包含一些 CSS CSS 来自第 3 方 NPM 包 因此位于 node modules 目录中 我试过 import node modules packagename css st
  • 尽管超时很长,PHP 仍丢失 mongoDB 游标

    我正在运行一个长 mongoDB 查询 如下所示 foreach xyz gt find gt timeout 24 60 60 1000 gt maxTimeMS 24 60 60 1000 as document 但是 尽管客户端和服务
  • 更改单选按钮旁边的文本字体?

    要更改我使用的 textView 的字体 TextView tv TextView findViewById R id textview Typeface font Typeface createFromAsset getAssets SF
  • #selector' 指的是未暴露给 Objective-C swift 3 的方法

    我正在使用 Xcode 8 和 swift 3 我在 let action 行上出现以下错误 selector 指的是不暴露给Objective C的方法有什么建议吗 override func tableView tableView UI
  • 传递结构体和结构体指针有什么区别,它们不都是指针吗?

    例如 var myStructRef Vertex var myStruct Vertex myStructRef Vertex 2 3 myStruct Vertex 2 3 fmt Println myStructRef fmt Pri
  • 如何使 Tkinter 支持 PNG 透明度?

    我在 Tkinter 中放入了部分透明的 PNG 图像 我得到的就是这个 如何让右边的黑三角变清晰 就像应该的那样 顺便说一句 这是 Windows 7 上的 python 2 6 这是一个示例 PNG 文件 example png 在不同
  • 如何保持多个虚拟树视图节点的检查状态同步?

    我的树有 2 层节点 它是联系人列表风格树 我的问题是 我想检查所有 联系人类别 中的每个联系人 这是我的联系人列表现在的屏幕截图 是的 我有权发布它 如你所见 托德 赫希已在类别中选中测试类别 但不在所有联系人 我想要实现的目标是让联系人