I discovered today that a routine that I've long used to detect a state change in a content control check box, while is would detect any state changes, it would also typically misfire when a state change did not actually occur.
These misfires were never an issue or only an annoyance, because if I had any message boxes in my change event code, they would be displayed twice.
The problem, I think, is related to the fact that a content control check box automatically changes state when you click to enter the CC with the mouse. My procedure would detect this change and fire and then begin monitor the CC with the focus for additional state changes.
Now if the CC I was in was checked and I tabbed out of it into the next CC checkbox, if it was false, the Event was misfiring and reporting a state change. I was monitoring a checked box and then a new check box (a different box) became the monitored box with a different state. This was resulting in a misfire.
I've spent most of the day experimenting and pulling my hair out trying to stop this behavior. I think I have it, but I'm so scatter brained right now that I'm not sure if there is a simply solution masked by the Rube Goldberg process I've created.
To illustrate just enter four or five CC check boxes and add this code the ThisDocument Module:
Option Explicit
Private m_oCCEntered As ContentControl
Private m_oCCInMonitor As ContentControl
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
Private Enum GotFocusHow
TabKey = 1
ShiftTabKeys = 4
LeftMouseBtn = 3
AltKey = 2
End Enum
Private Sub Document_ContentControlOnEnter(ByVal oCC_Entered As ContentControl)
'A built-in document event. Fires when a CC is entered.
Dim bTabEntry As Boolean
bTabEntry = fcn_CCTabEntry
'Using a module level variable here because I can't pass (or can't figure out how to pass a argument with _
'the Application.OnTime method.
Set m_oCCEntered = oCC_Entered
'A click entry (via mouse) in a checkbox CC triggers an automatic state change while a tab entry from another CC does not.
If Not bTabEntry Then
'The user clicked the control and it changed state on entry. Pass the object CC to the ContentControlOnChange Event.
Document_ContentControlOnChange m_oCCEntered
End If
'Call the CCMonitor procedure asynchrounously. This allows this procedure to run to termination.
Application.OnTime Now + TimeSerial(0, 0, 0.1), "CCMonitor"
lbl_Exit:
Exit Sub
End Sub
Private Sub Document_ContentControlOnExit(ByVal ContentControl As ContentControl, Cancel As Boolean)
Set m_oCCInMonitor = Nothing
End Sub
Sub CCMonitor()
'Intiated via the CC OnEnter event, this procedure continously monitors the CC for a state change _
and triggers a "custom" OnChange event when a change occurs.
Dim bCC_State As Boolean
'Leaving the control will terminate the reference to the monitored CC and raise an error.
On Error GoTo Err_Handler
'Set the intial state to compare.
Set m_oCCInMonitor = m_oCCEntered
bCC_State = m_oCCInMonitor.Checked
Do While bCC_State = m_oCCInMonitor.Checked
DoEvents
'Detect changes.
If bCC_State <> m_oCCInMonitor.Checked Then
'Pass the CC_Changed to the custom OnChange event procedure.
Document_ContentControlOnChange m_oCCInMonitor
'Set new state to compare
bCC_State = m_oCCInMonitor.Checked
End If
Loop
Err_ReEntry:
Exit Sub
'Handle expected error 91
Err_Handler:
'Debug.Print Err.Number & " " & Err.Description & "."
'An exit event has terminated the reference to the monitored CC.
Resume Err_ReEntry
End Sub
Private Sub Document_ContentControlOnChange(ByVal oCC As ContentControl)
MsgBox "The selected CC had a state change the value of the CC after the state change is: " & oCC.Checked
End Sub
Private Function fcn_CCTabEntry() As Boolean
Dim lngRet As GotFocusHow
If GetKeyState(vbKeyTab) < 0 Then
If (GetKeyState(vbKeyShift) < 0) Then
lngRet = ShiftTabKeys
Else
lngRet = TabKey
End If
ElseIf GetKeyState(vbKeyMenu) < 0 Then
lngRet = AltKey
ElseIf GetKeyState(vbKeyLButton) < 0 Then
lngRet = LeftMouseBtn
End If
Select Case lngRet
Case TabKey, ShiftTabKeys
fcn_CCTabEntry = True
Case Else
fcn_CCTabEntry = False
End Select
End Function
Questions:
1. Does anyone see away to avoid the CC_OnExit procedure?
2. Does anyone see a way to minimize the number of module level variables?
3. Does anyone have any other ideas to streamline or make this process better?
Thanks.