CheckBox not work properly when clicked on quickly multiple times

穿精又带淫゛_ 提交于 2020-05-13 18:53:06

问题


What this code do:

  1. If parent node is checked/unchecked, also check/uncheck all child nodes.

  2. If just one child node is checked, also check parent node.

    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

This code works fine.

Problem:

When I clicks quickly some of the checkboxes are checked and some no E.g. Sometimes I checked the parent node but all child nodes still remain unchecked. Sometimes the parent node is unchecked but its child nodes still checked.

Please check the example image:

this

How to solve this, is this a problem with my PC?


回答1:


That happens because the TreeView by default doesn't toggle the Check property of the TreeNode objects on mouse double click over the check box area. You need to intercept the WM_LBUTTONDBLCLK messages, get TreeViewHitTestInfo of the double clicked point, and toggle the Check property if the double clicked point is over the check box.

Here's a custom TreeView for that, also it solves the main issue, checking/unchecking the parent and child nodes of the branch, just enable the AutoCheckParents and/or AutoCheckChildren properties for that.

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
  • Add a new class to your project and paste this code.
  • Rebuild.
  • Drop an instance of the DoubleClickCheckTreeView or change the type of the existing default TreeView in the Designer.


Related


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





回答2:


I would imagine you would have to handle your double click event when its fired, and I suspect your code for that is going to look very similar to your single click event (but who knows)



来源:https://stackoverflow.com/questions/61299089/checkbox-not-work-properly-when-clicked-on-quickly-multiple-times

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!