Results 1 to 4 of 4

Thread: Scope, Lifetime, I need a fresh set of eyes

  1. #1
    Microsoft Word MVP 2003-2009 VBAX Guru gmaxey's Avatar
    Sep 2005

    Scope, Lifetime, I need a fresh set of eyes

    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"
      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
        '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
      Exit Sub
      'Handle expected error 91
      '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
          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
    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?

    Last edited by SamT; 02-14-2020 at 04:03 PM.

    Visit my website:

  2. #2
    VBAX Expert Dave's Avatar
    Mar 2005
    EllaMina ??? WTF??? Anyways, maybe trial this where your wait and/or doevents code are. Good luck. Dave
    Dim t As Double
    t = Timer
    Do Until Timer - t > 1

  3. #3
    Moderator VBAX Wizard SamT's Avatar
    Oct 2006
    Near Columbia
    bTabEntry As Boolean is not a Module level variable. Never mind, I see that it is not used outside the procedure;

    WOW! It took me an hour to admit that I can't make heads or tails of the purpose of that code.

    I did manage to refactor one function, I hope it helps a little bit
    Private Function fcn_CCTabEntry() As Boolean
      fcn_CCTabEntry = GetKeyState(vbKeyTab) < 0
    End Function
    I always expect the student to do their homework and find all the errrors I leeve in.

    Please take the time to read the Forum FAQ

  4. #4
    Knowledge Base Approver VBAX Guru macropod's Avatar
    Jul 2008
    At the most basic level (i.e. I'm not going to analyse what your code is doing):
    Option Explicit
    Dim bState As Boolean
    Private Sub Document_ContentControlOnEnter(ByVal CCtrl As ContentControl)
    'A built-in document event. Fires when a CC is entered.
    With CCtrl
      If .Type = wdContentControlCheckBox Then bState = .Checked
    End With
    'Code for whatever happens on entry, regardless of whether it's a checkbox, goes here
    End Sub
    Private Sub Document_ContentControlOnExit(ByVal CCtrl As ContentControl, Cancel As Boolean)
    With CCtrl
      If .Type = wdContentControlCheckBox Then
        If bState = .Checked Then Exit Sub
      End If
    End With
    'Code for whatever happens if it's not a checkbox, or if the checkbox state has changed, goes here
    End Sub
    Paul Edstein
    [Fmr MS MVP - Word]

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts