Setting Checked On NodeCheck Event

2020-05-06 11:17发布

问题:

Setting the checked property during the NodeCheck event has been causing it to revert to its previous state.

For example:The node is checked, and the event below gets fired. It finds that the node is checked and sets it to be false. If I walk through the code with a break, the node will reflect this in the user interface. Although, as soon as the code hits the end sub, the checkbox will jump back to being set to true.

Private Sub treeviewExample_NodeCheck(ByVal Node As Object)
     If Node.Checked = True Then
            Node.Checked = False
     ElseIf Node.Checked = False Then
            Node.Checked = True
     End If
end sub

How do I set the checked property during the NodeCheck event?

I have tried the solution here that sets the node to a local or global variable and then sets it, and it does the same thing.

回答1:

You can leave the Checkboxes property False and use the Windows API to set the checkboxes property. Then use the NodeClick event to choose whether to check or uncheck the node.

Option Explicit

Private Const TVS_CHECKBOXES As Long = &H100
Private Const GWL_STYLE As Long = (-16)
Private Const TVS_HASLINES As Long = 2
Private Const TV_FIRST As Long = &H1100
Private Const TVM_SETBKCOLOR As Long = (TV_FIRST + 29)

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Private Sub Form_Load()

    SetTVCheckboxStyle TreeView1

End Sub

Private Sub SetTVCheckboxStyle(pobjTV As TreeView)

    Dim lngCurStyle As Long
    Dim lngResult   As Long

    ' === Set the Checkbox style of the TreeView ===
    ' As advised by Microsoft, due to a bug in the TreeView control,
    ' set the Checkbox style of the TreeView by using the following
    ' API calls, rather than simply setting the "Checkboxes" property
    ' to True ...
    lngCurStyle = GetWindowLong(pobjTV.hwnd, GWL_STYLE)
    lngResult = SetWindowLong(pobjTV.hwnd, GWL_STYLE, _
                              lngCurStyle Or TVS_CHECKBOXES)

End Sub

As you add your node set some property of the nodes you want disabled so you can check the property later. I chose to use the ForeColor property so the disabled nodes would have a disabled appearance. Then use the NodeClick event to check, clear, or ignore the user clicks.

Private Sub TreeView1_NodeClick(ByVal Node As MSComctlLib.Node)

    If Node.ForeColor <> vbGrayText Then
        Node.Checked = Not Node.Checked
    End If

End Sub


回答2:

I've encountered this issue, and discovered a workaround.

You can't set the 'checked-ness' of a treeview's checkbox in its own NodeCheck event.

However, assuming you're capturing the user clicking the checkbox, the Treeview_Click event fires immediately afterwards (as they had to click to change it), and you can set checkboxes in that event.

So all you need to do is store a reference to the checkbox you need to be changed and set it in the Click event, before clearing the reference.

Private WithEvents tv As TreeView

Private checkBoxToSet_Node As Node
Private checkBoxToSet_value As Boolean


Private Sub tv_NodeCheck(ByVal Node As MSComctlLib.Node)

    Dim prompt As VbMsgBoxResult

    prompt = MsgBox("Do you REALLY want to set this checkbox?", vbQuestion + vbYesNoCancel)

    ' can't set the checked-ness here, so we store it

    If (prompt <> vbYes) Then
        Set checkBoxToSet_Node = Node
        checkBoxToSet_value = Not Node.checked
    End If

End Sub


Private Sub tv_Click()

    ' check if we had previously set a node to have its checkbox changed

    If (Not checkBoxToSet_Node Is Nothing) Then
        tv.Nodes(checkBoxToSet_Node.key).checked = checkBoxToSet_value

        Set checkBoxToSet_Node = Nothing
    End If

End Sub