【问题标题】:Slow TreeNode with CheckBoxes [large quantity]带 CheckBoxes 的慢 TreeNode [大量]
【发布时间】:2010-08-21 05:22:46
【问题描述】:

Tree View 控件的 AfterCheck 事件会检查其下方的所有子节点,并在检查时启用“运行”按钮。

1346 void TreeNode_AfterCheck(object sender, TreeViewEventArgs e) {
1347   if (!e.Node.Checked) return;
1348   foreach (TreeNode sub in e.Node.Nodes) {
1349     sub.Checked = e.Node.Checked;
1350   }
1351   RunButton.Enabled = IsANodeChecked();
1352 }

1429 static bool IsANodeChecked(TreeNode node) {
1430   if (node.Checked) return true;
1431   foreach (TreeNode sub in node.Nodes) {
1432     if (IsANodeChecked(sub)) {
1433       return true;
1434     }
1435   }
1436   return false;
1437 }

当有4881个子节点时检查根节点会挂掉GUI大约7秒。

我只需要调用一次 IsANodeChecked(在第 1351 行),但在处理完所有树节点之前我不知道如何禁用它。

而且我不想在我的表单上设置一个专门用于监控的计时器。

有人看到简单/明显的解决方案吗?

【问题讨论】:

    标签: c# winforms checkbox treenode


    【解决方案1】:

    在您的复选框上放置一个事件处理程序,以启用或禁用 RunButton,而不是让某些东西遍历整个事件以找出答案。

    在第一次选中复选框时,将复选框添加到选中复选框列表中,这样在选中复选框列表为空之前,您不会禁用 RunButton。未选中时将其从列表中删除等。

    下面是我的写法,如果我错过了什么,那就太抱歉了:

    private int _checkedCheckboxes;
    
    void AddCheckBox()
    {
        if (_checkedCheckBoxes++ == 1) RunButton.Enabled = true;
    }
    
    void RemoveCheckBox()
    {
        if (_checkedCheckBoxes-- == 0) RunButton.Enabled = false;
    }
    
    void TreeNode_AfterCheck(object sender, TreeViewEventArgs e) 
    {
        if (e.Node.Checked)
        {
            AddCheckBox();
            return;
        }
    
        RemoveCheckBox();
    }
    

    【讨论】:

    • 树节点是根据数据库中的型号(来自序列号)手动创建的。您是说为每个手动创建的节点创建一个新事件吗? (比如 5000 个事件)如果检查了根节点并且所有 5000 个子节点同时触发,这会比我现在做的更好吗?你的想法需要一些时间来设计,所以我想在承诺之前了解更多。
    【解决方案2】:

    我有时会使用Timer 来处理此类情况。添加一个计时器并设置Tick 事件处理程序以调用IsANodeChecked 并启用/禁用按钮。给它一个短的时间间隔(可能约为 100 毫秒),然后将其禁用。然后,您在AfterCheck 事件处理程序中的计时器上调用Stop,然后是Start。这将导致每次调用AfterCheck 时都会重新启动计时器,但只有在Start 调用之后经过一定时间后才会调用Tick 事件处理程序,这意味着直到之后才会调用它最后一次拨打AfterCheck。

    100 毫秒对于计算机来说是一个非常长的工作时间,但对用户来说似乎是即时的。

    您可以在 Windows 资源管理器中看到类似的行为。如果您使用键盘在文件夹树中快速导航,则包含文件夹内容的右侧窗格将不会更新,除非您在树中的文件夹上停留片刻。

    【讨论】:

    • 如果没有更好的结果,我可能不得不在这个应用程序中实现另一个计时器。不过,我宁愿不这样做。
    • @jp2code:如果应用程序已经是计时器密集型,我看到你不情愿。我也倾向于避免使用它们,并将它们留到那些能够真正帮助我的特殊场合。
    【解决方案3】:

    这些想法很有帮助,但我使用了一些不同的方法,通过添加单个布尔变量来实现:

    bool _treeNodeFirst = false;

    ...和一个 Before Checked 事件,它临时修改控件上的背景颜色以用作启动事件链的控件的标志:

    1273 void TreeNode_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
    1274   if (!_treeNodeFirst) {
    1275     _treeNodeFirst = true;
    1276     e.Node.BackColor = Color.Silver;
    1277   }
    1278 }
    
    1346 void TreeNode_AfterCheck(object sender, TreeViewEventArgs e) {
    1347   if (e.Node.Checked) {
    1348     foreach (TreeNode sub in e.Node.Nodes) {
    1349       sub.Checked = e.Node.Checked;
    1350     }
    1351   }
    1352   if (e.Node.BackColor == Color.Silver) {
    1353     e.Node.BackColor = Color.Empty;
    1354     RunButton.Enabled = IsANodeChecked();
    1355     _treeNodeFirst = false;
    1356   }
    1357 }
    
    1429 static bool IsANodeChecked(TreeNode node) {
    1430   if (node.Checked) return true;
    1431   foreach (TreeNode sub in node.Nodes) {
    1432     if (IsANodeChecked(sub)) {
    1433       return true;
    1434     }
    1435   }
    1436   return false;
    1437 }
    

    这似乎是确保IsANodeChecked(TreeNode) 仅在同时选择一组节点时运行一次的最佳方式(我现在可以看到)。

    不过,我确实非常喜欢 Jimmy Hoffa 使用计数的想法。我可能会将其添加到我的代码中。

    谢谢大家! 〜乔

    【讨论】:

      猜你喜欢
      • 2016-02-26
      • 2016-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-12
      • 2011-03-07
      • 1970-01-01
      • 2014-04-20
      相关资源
      最近更新 更多