【问题标题】:CheckBox not work properly when clicked on quickly multiple times多次快速单击时复选框无法正常工作
【发布时间】:2020-04-19 03:12:40
【问题描述】:

这段代码的作用:

  1. 如果选中/取消选中父节点,还选中/取消选中所有子节点。

  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.Checked)
        HasCheckedChildNode = 0
        updatingTreeView = False
    End Sub
    
    

    Private Sub CheckNode(node As TreeNode, isChecked As Boolean) If node.Parent IsNot Nothing Then HasCheckedNode(node.Parent) If Not isChecked And HasCheckedChildNode > 0 Then Return node.Parent.Checked = isChecked ElseIf node.Parent Is Nothing Then For Each cn As TreeNode In node.Nodes cn.Checked = isChecked Next End If End Sub

    Private Sub HasCheckedNode(node As TreeNode) For Each cn As TreeNode In node.Nodes If cn.Checked = True Then HasCheckedChildNode += 1 ElseIf cn.Checked = False Then HasCheckedChildNode -= 0 End If Next End Sub

这段代码运行良好。

问题:

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

请查看示例图片:

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

【问题讨论】:

    标签: vb.net visual-studio-2015 treeview


    【解决方案1】:

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

    这是一个自定义的 TreeView,它也解决了主要问题,选中/取消选中分支的父节点和子节点,只需为此启用 AutoCheckParents 和/或 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
    
    • 向您的项目添加一个新类并粘贴此代码。
    • 重建。
    • 删除 DoubleClickCheckTreeView 的实例或更改设计器中现有默认 TreeView 的类型。


    相关


    AfterCheck and AfterSelect events in TreeView, Windows Forms (c#)


    【讨论】:

    • 非常感谢您,先生,@JQSOFT。你的代码就像一个魅力!虽然我不完全理解你的代码,但我想知道更多。能否请您推荐我应该在哪里阅读。
    • @SRN 欢迎好友。只要问自己一个简单的问题。 它是如何工作的?,搜索答案,阅读书籍,编码,犯错误,从中学习。祝你好运。
    • @SRN 谢谢。刚刚添加了一个新属性来分开切换父节点和子节点,因为每个节点都有应用程序。
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-04
      • 1970-01-01
      • 2021-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多