多次快速单击时 CheckBox 无法正常工作

CheckBox not work properly when clicked on quickly multiple times

此代码的作用:

  1. 如果父节点是checked/unchecked,也check/uncheck所有子节点。

  2. 如果只检查一个子节点,还要检查父节点。

    Private Sub TreeView1_AfterCheck(sender As Object, e As TreeViewEventArgs) Handles TreeView1.AfterCheck
        If updatingTreeView Then Return
        updatingTreeView = True
        CheckNode(e.Node, e.Node.选中)
        已检查子节点 = 0
        updatingTreeView = 假
    结束子</p>
    
    <p>Private Sub CheckNode(node As TreeNode, isChecked As Boolean)
        如果 node.Parent IsNot Nothing 那么
            HasCheckedNode(node.Parent)
            If Not isChecked And HasCheckedChildNode > 0 Then Return
            node.Parent.Checked = isChecked
        ElseIf node.Parent 那么什么都不是
            For Each cn As TreeNode In node.Nodes
                cn.Checked = 已检查
            下一个
        万一
    结束子</p>
    
    <p>Private Sub HasCheckedNode(node As TreeNode)
        For Each cn As TreeNode In node.Nodes
            如果 cn.Checked = True 那么
                HasCheckedChildNode += 1
            ElseIf cn.Checked = False 然后
                HasCheckedChildNode -= 0
            万一
        下一个
    结束子
    

这段代码工作正常。

问题:

当我快速点击时,有些复选框被选中,有些则没有 例如。有时我检查了父节点,但所有子节点仍未选中。有时父节点未选中,但其子节点仍处于选中状态。

请检查示例图片:

如何解决这个问题,这是我电脑的问题吗?

我想你必须在双击事件被触发时处理它,我怀疑你的代码看起来与你的单击事件非常相似(但谁知道呢)

发生这种情况是因为 TreeView 默认情况下不会在鼠标双击复选框区域时切换 TreeNode 对象的 Check 属性。需要拦截WM_LBUTTONDBLCLK消息,获取双击点的TreeViewHitTestInfo,如果双击点在复选框上方,则切换Check属性

这是一个自定义的 TreeView,它也解决了主要问题,checking/unchecking 分支的父节点和子节点,只需启用 AutoCheckParents and/or AutoCheckChildren 属性。

Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

<DesignerCategory("Code")>
Public Class DoubleClickCheckTreeView
    Inherits TreeView

#Region "Properties"

    <Category("Behavior"),
        DefaultValue(False)>
    Public Property AutoCheckParents As Boolean = False

    <Category("Behavior"),
        DefaultValue(False)>
    Public Property AutoCheckChildren As Boolean = False

#End Region

#Region "Overrides"

    'Enable DoubleBuffered to reduce the flickering.
    Protected Overrides Sub OnHandleCreated(e As EventArgs)
        SendMessage(Handle,
                    TVM_SETEXTENDEDSTYLE,
                    IntPtr.op_Explicit(TVS_EX_DOUBLEBUFFER),
                    IntPtr.op_Explicit(TVS_EX_DOUBLEBUFFER))
        MyBase.OnHandleCreated(e)
    End Sub

    Protected Overrides Sub WndProc(ByRef m As Message)
        If m.Msg = WM_LBUTTONDBLCLK AndAlso CheckBoxes Then
            Dim x As Integer = m.LParam.ToInt32() And &HFFFF
            Dim y As Integer = (m.LParam.ToInt32 >> 16) And &HFFFF
            Dim ht As TreeViewHitTestInfo = HitTest(x, y)

            If ht.Node IsNot Nothing AndAlso
                ht.Location = TreeViewHitTestLocations.StateImage Then
                OnBeforeCheck(New TreeViewCancelEventArgs(ht.Node,
                                                          False,
                                                          TreeViewAction.ByMouse))
                ht.Node.Checked = Not ht.Node.Checked
                OnAfterCheck(New TreeViewEventArgs(ht.Node, TreeViewAction.ByMouse))
                m.Result = IntPtr.Zero
                Return
            End If
        End If
        MyBase.WndProc(m)
    End Sub

    Protected Overrides Sub OnAfterCheck(e As TreeViewEventArgs)
        MyBase.OnAfterCheck(e)

        If e.Action = TreeViewAction.Unknown OrElse
            Not CheckBoxes Then Return

        If AutoCheckParents Then
            Dim p = e.Node.Parent

            While p IsNot Nothing
                p.Checked = p.Nodes.Cast(Of TreeNode).Any(Function(x) x.Checked)
                p = p.Parent
            End While
        End If

        If AutoCheckChildren Then
            For Each tn As TreeNode In GetNodes(e.Node)
                tn.Checked = e.Node.Checked
            Next
        End If
    End Sub

#End Region

#Region "Private Methods"

    Private Iterator Function GetNodes(node As TreeNode) As IEnumerable(Of TreeNode)
        For Each n As TreeNode In node.Nodes
            Yield n
            For Each c As TreeNode In GetNodes(n)
                Yield c
            Next
        Next
    End Function

#End Region

#Region "API"

    Private Const TVM_SETEXTENDEDSTYLE As Integer = &H1100 + 44
    Private Const TVM_GETEXTENDEDSTYLE As Integer = &H1100 + 45
    Private Const TVS_EX_DOUBLEBUFFER As Integer = &H4
    Private Const WM_LBUTTONDBLCLK As Integer = &H203

    <DllImport("user32.dll")>
    Private Shared Function SendMessage(ByVal hWnd As IntPtr,
                                       ByVal msg As Integer,
                                       ByVal wp As IntPtr,
                                       ByVal lp As IntPtr) As IntPtr
    End Function

#End Region

End Class
  • 向您的项目添加一个新的 class 并粘贴此代码。
  • 重建。
  • 删除 DoubleClickCheckTreeView 的实例或更改设计器中现有默认 TreeView 的类型。


Related