1

I'm trying to loop thought all controls (after ignoring tab index 1 and 2) in an Excel form and use the tab index for each control to lookup a value from a sheet. I've set the tab index order via the Properties hoping that I will get loop to go in numerical order, but when I debug.print the control loop I get the following order: 2,3,20,5,6,7

All of the controls start with txt.

The Code:

For Each ctrl In PipelineUpdateForm.Controls

    If Left(ctrl.Name, 3) = "txt" And ctrl.TabIndex > 2 Then

        ColumnN = ColumnN + 1

        ctrl.Value = Application.WorksheetFunction.VLookup(LookupObject, Sheets("PipelineRawEntries").Range("B2:AS4"), ColumnN, False)

    End If

Next ctrl

It looks like the tab index is bound to something and the loop ignores the way it is set in the Properties. Where am I going wrong or how I can fix this?

1
  • VBA looping isn't tabbing (which is a user-interface action). Why should the loop order be the same as the tab order? If you want that, you could create your own custom collection. It is an interesting question. Commented May 4, 2018 at 16:24

1 Answer 1

1

A For Each loop would iterate the controls in the order they were added to the form (although, I don't think that's specified or guaranteed in any way), like it would with any other Collection type.

For Each works by pulling the collection's enumerator. If you look at MSForms.Controls in the Object Browser (F2), right-click a blank area in the object browser and check the "Show hidden members" option, you'll see a hidden _NewEnum member on the type.

_NewEnum also exists in Excel.Workbooks, Excel.Range, VBA.Collection, and everything else you can iterate with a For Each loop: if you write a custom collection class, you'll need to have a NewEnum() As IUnknown member with a special member attribute too (VB_UserMemId = -4) - this enumerator is what the VBA runtime uses to get the next object reference in the collection.

And it knows nothing of a control's TabIndex. The TabIndex determines the order controls get focus when the user hits the Tab key to iterate the controls - it has nothing to do with a For Each loop.

If you need to iterate your controls in a specific order (FWIW I can't think of a legit reason to do that), then you can specify the order in the control names themselves (e.g. txtSomethingSomething1, txtSomethingSomething2, ...), and then retrieve the controls by name, using a For...Next loop (which would be less efficient, since object collections want to be enumerated):

Dim i As Long
Dim currentTextBox As TextBox
For i = 1 To 20
    Set currentTextBox = Me.Controls("txtSomethingSomething" & i)
    currentTextBox.Value = Application.WorksheetFunction.VLookup(...)
Next

Alternatively, you could store the ColumnN index you need, in each control's Tag property - and get a much more efficient piece of logic (robustness is arguable though.. there are many ways to go about this, that's just one way):

For Each ctrl In Me.Controls
    If ctrl.Tag <> vbNullString Then
        columnN = ctrl.Tag
        ctrl.Value = Application.WorksheetFunction.VLookup(...)
    End If
Next 

Notice I used Me.Controls instead of PipelineUpdateForm.Controls; I'm assuming this code is living in the form's code-behind - if that's the case, then you should be referring to your form instance with the Me keyword, not the form's name. You'll inevitably run into fairly common issues, sooner or later, if you keep referring to a form's default instance within that form's code-behind. My UserForm1.Show article from last October covers it all in details.

Side note, you probably want to pull Sheets("PipelineRawEntries").Range("B2:AS4") into a local object variable, instead of dereferencing the same object over and over and over, at each iteration of the loop:

Dim lookupRange As Range
Set lookupRange = ActiveWorkbook.Workheets("PipelineRawEntries").Range("B2:AS4")

And then use lookupRange in the VLookup function.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! It makes sense now, I decided to you your first solution to sort out the problem and update my code to change the form name to me. Have a great day :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.