Home » excel » excel vba – Avoiding .Activate with ActiveX Form Control

excel vba – Avoiding .Activate with ActiveX Form Control

Posted by: admin April 23, 2020 Leave a comment

Questions:

I’m updating a macro that’s used in lots of spreadsheets, and it’s rather slow. While looking to speed it up, I noticed that at one point it goes through this loop:

For each wsLoop in ThisWorkbook.Worksheets
        wsLoop.Activate
        With ActiveSheet.Status_Text
            If bStatus = True Then
                .ForeColor = &HC000&
                .Caption = "ONLINE"
            Else
                .ForeColor = &HFF&
                .Caption = "OFFLINE"
            End If
        End With
Next wsLoop

Where wsLoop is a worksheet, bStatus is a boolean and Status_Text is the name of an ActiveX label form control on each worksheet.
I know using .Activate is bad practice and can slow things down, so I dropped the wsLoop.Activate and changed the next line to With wsLoop.Status_Text, but now I get a “Method or data member not found” error message.
What’s the proper way to do this?

How to&Answers:

Interesting question which seems to touch on some poorly-documented features of Excel VBA. It seems that maybe the expression ActiveSheet.Status_Text is operating as the name of the control, with the dot acting as a namespace qualifier, but that in wsLoop.Status_Text VBA is interpreting the dot as the method/property access operator and is correctly giving the error message that no such method or property exists. To reproduce the problem, I created a label named Status_Text on each sheet and then ran

Sub test1()
    Dim ws As Worksheet
    For Each ws In Worksheets
        Debug.Print ws.Status_Text.Caption 'fails
    Next ws
End Sub

It crashes with the error that you show. One workaround (although just why it works is mysterious) is to change the loop index from being a Worksheet to a Variant:

Sub test2()
    Dim ws As Variant
    For Each ws In Worksheets
        Debug.Print ws.Status_Text.Caption 'succeeds
    Next ws
End Sub

The odd thing about this last example is if you add the line Debug.Print TypeName(ws) in the for-each loop it prints Worksheet so ws.Status_Text works if ws is a variant which holds a worksheet but not if ws is actually a worksheet. The mystery is in some sense deepened but in another sense lessened when you step through this sub in the debugger, looking at the locals window. The type of ws in the loop is described as Variant/Object/Sheet1 (in the first pass through the loop). The specific sheet seems to be part of the current subtype of the variable.

Another workaround is to use a For-Next loop rather than a For-Each:

Sub test3()
    Dim i As Long
    For i = 1 To Worksheets.Count
        Debug.Print Worksheets(i).Status_Text.Caption 'succeeds
    Next i
End Sub

You could use either of these approaches to get a reference to the label without having to activate the sheet.