Windows 表单中 Treeview 中的 Aftercheck 和 AfterSelect 事件

Aftercheck and AfterSelect events in Treeview in Windows Forms

我正在设置一个新窗体,我在检查和取消检查子节点时遇到了一些问题。在这个 short clip

中更容易看出问题

通常它工作正常但有时会卡住(我认为与选择有冲突但我不确定)并且方法应用不正确。

我有这个方法来检查和取消检查孩子:

private void Treeview_AfterCheck(object sender, TreeViewEventArgs e)
{
    if (e.Action != TreeViewAction.Unknown)
        if (e.Node.Checked)
        {
            CheckAll(e.Node.Nodes);
        }
        if (e.Node.Checked == false)
        {
            Uncheckall(e.Node.Nodes);
        }
}

public void Uncheckall(TreeNodeCollection nodes)
{
    foreach (TreeNode node in nodes)
    {
        node.Checked = false;
        foreach (TreeNode node1 in node.Nodes)
        {
            node1.Checked = false;
            foreach (TreeNode node2 in node1.Nodes)
            {
                node2.Checked = false;
            }
        }
    }
}


public void CheckAll(TreeNodeCollection nodes)
{
    foreach (TreeNode node in nodes)
    {
        node.Checked = true;
        foreach (TreeNode node1 in node.Nodes)
        {
            node1.Checked = true;
            foreach (TreeNode node2 in node1.Nodes)
            {
                node2.Checked = true;
            }
        }
    }
}

而且我已尝试将选择设为空:

private void TreeView_Select(object sender, TreeViewEventArgs e)
{
    TreeView.SelectedNode = null;
}

但问题依旧。有任何想法吗?谢谢

我在评论中提到的 question 的答案向我们展示了如何遍历 TreeView 节点的不同方式。为此,您需要一个递归函数,它是一个调用自身的函数

现在,回到您的代码。您不需要创建两个函数来检查和取消检查节点,也不需要为每个节点、子节点和子节点的子节点使用 foreach 等。请尝试以下操作:

private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
    if (e.Action == TreeViewAction.Unknown) { return; }

    foreach (TreeNode tn in GetNodes(e.Node))
        tn.Checked = e.Node.Checked;
}

private static IEnumerable<TreeNode> GetNodes(TreeNode parentNode)
{
    foreach (TreeNode tn in parentNode.Nodes)
    {
        yield return tn;

        foreach (TreeNode child in GetNodes(tn))
        {
            yield return child;
        }
    }
}

这样,您可以使用此迭代器对您的节点执行其他操作,而不仅仅是 check/uncheck 它们。

编辑

You can see this strange behaviour in the seconds 7, 10, 15.

我明白你的意思了。

当您在节点上鼠标单击速度过快时会出现该行为,因此您实际上是在执行鼠标单击、鼠标双击序列。默认情况下,树视图控件不会通过鼠标双击切换节点的检查状态,除非您告诉它这样做。如何? PhilP in this 问题已回答。

  • 创建一个新的class继承树视图控件并覆盖WndProc事件如下:
class TreeViewEx : TreeView
{

    public TreeViewEx()
    { }

    #region This extra to reduce the flickering

    private const int TVM_SETEXTENDEDSTYLE = 0x1100 + 44;
    private const int TVM_GETEXTENDEDSTYLE = 0x1100 + 45;
    private const int TVS_EX_DOUBLEBUFFER = 0x4;

    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);

    protected override void OnHandleCreated(EventArgs e)
    {
        SendMessage(Handle, TVM_SETEXTENDEDSTYLE, (IntPtr)TVS_EX_DOUBLEBUFFER, (IntPtr)TVS_EX_DOUBLEBUFFER);
        base.OnHandleCreated(e);
    }

    #endregion

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == 0x203 && CheckBoxes)
        {
            int x = m.LParam.ToInt32() & 0xffff;
            int y = (m.LParam.ToInt32() >> 16) & 0xffff;
            TreeViewHitTestInfo hitTestInfo = HitTest(x, y);

            if (hitTestInfo.Node != null && hitTestInfo.Location == TreeViewHitTestLocations.StateImage)
            {
                OnBeforeCheck(new TreeViewCancelEventArgs(hitTestInfo.Node, false, TreeViewAction.ByMouse));
                hitTestInfo.Node.Checked = !hitTestInfo.Node.Checked;
                OnAfterCheck(new TreeViewEventArgs(hitTestInfo.Node, TreeViewAction.ByMouse));
                m.Result = IntPtr.Zero;
                return;
            }
        }

        base.WndProc(ref m);
    }
}
  • 在包含您的 TreeView 的表单设计器中,将 TreeView 的类型更改为我们刚刚创建的扩展类型。

  • 使用相同的代码切换检查状态。

  • 重建您的项目。

就这些了。

这是一个快速演示。我疯狂地点击鼠标和双击。但是,它可以正常工作。希望。


Related