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#)