How to dynamically count UBound() values? - vba

I got something like this:
For i = 1 to 4
cur_tab = "tab_event" & i
For e = 1 to Ubound(cur_tab)
' do something
Next
Next
I need to dynamically change the cur_tab , but it's not working due to Ubound(cur_tab) is returning a String(e.g.) *Ubound("tab_event1")*
*I've declared all vars and of course I have 'i' tables
"Subscript out of range"
Any ideas ?

What you are trying to achieve is called Variable Variables. It doesn't work like that in VBA. If you have a common procedure then use a separate procedure and use that again and again.
For example, your code can be written as
Sub Sample()
'
'~~> Rest Of Your Code
'
For e = 1 To UBound(tab_event1)
MySub
Next
For e = 1 To UBound(tab_event2)
MySub
Next
For e = 1 To UBound(tab_event3)
MySub
Next
For e = 1 To UBound(tab_event4)
MySub
Next
'
'~~> Rest Of Your Code
'
End Sub
Sub MySub()
MsgBox "Hello World!"
End Sub

Related

VBA: Why can't I use "If x is null then...", but "dim q = isNull(x) / if q then..." works fine?

I'm very new to VBA so forgive my structure if it's inefficient. I'm trying to differentiate between a new record being added to a table in Access versus a blank field of an existing record being updated. My code below works as I intend it to.
Private Sub x_BeforeUpdate(Cancel As Integer)
Dim Q, W As Boolean
Q = IsNull(x.OldValue)
W = IsNull(x)
If Me.NewRecord Then
...
Exit Sub
ElseIf Q And Not W Then
...
Exit Sub
ElseIf W And Not Q Then
...
Exit Sub
End If
...
End Sub
My question is mostly about the fact that when it looked like the next code block, I would always get an error on the first line for "x.OldValue is Null".
Private Sub x_BeforeUpdate(Cancel As Integer)
If Me.NewRecord Then
...
Exit Sub
ElseIf x.OldValue is Null And x is Not Null Then
...
Exit Sub
ElseIf x.OldValue is Not Null and x is Null Then
...
Exit Sub
End If
...
End Sub
What is the big difference between these two blocks of code? Why does declaring a variable work but using "x is Null" does not?

Time Loop in VBA - Looping through Columns

There are two sheets (sheet1 and sheet2).
Need to refresh sheet1 every 4 minutes as long as the workbook is open.
Then I Need to copy values from Column C in sheet1 to column C in sheet2 every 4 minutes.
Since new values would come in every 4 mins in sheet1, I want the values to be copied to a new column in sheet2 everytime.
I'm using the following code. The problem with my code is that the variable i is getting initiated fresh everytime and I am not able to initiate it to a value outside the module using Public i as long.
Sub copyvalues()
Dim i As Long
i = 3
Sheets(2).Columns(i).Value = Sheets(1).Range("C11:C90").Value
i = i + 1
Application.OnTime Now + TimeValue("00:04:00"), "copyvalues"
End Sub
Although you mention you can't declare variable i as Public, this should work:
For example, the following works without issues:
Public i As Long
Sub test()
Debug.Print i
Application.OnTime Now + TimeValue("00:00:01"), "Module1.test"
i = i + 1
End Sub
Try the following:
Public i As Long
Public SubIsRunning As Boolean
Sub initiatesubs()
If Not SubIsRunning = True Then
i = 3
Call copyvalues
SubIsRunning = True
End If
End Sub
Sub copyvalues()
Workbooks(REF).Sheets(2).Columns(i).Value = Workbooks(REF).Sheets(1).Range("C11:C90").Value
i = i + 1
Application.OnTime Now + TimeValue("00:04:00"), "Module1.copyvalues" 'assuming the sub is in Module1
End Sub

VBA Range Function/Variable Object error

I can't figure out why I keep getting an object error in VBA when calling a function with a desired range/variable. I want to call the SumSameCells function from a sub with the B8, B9, B10 as desired/destination cells (rng As Range)
Any help would be greatly appreciated. Thanks
Sub MySums()
Call SumSameCells(B8)
Call SumSameCells(B9)
Call SumSameCells(B10)
End Sub
Function SumSameCells(rng As Range)
x = 0
For i = 2 To 3
x = x + Sheets(i).Range(" & rng.Address & ")
Next i
Sheet1.Range(" & rng.Address & ") = x
End Function
This:
Call SumSameCells(B8)
isn't passing the Range B8 but an undeclared variable named B8
Using Option Explicit would warn you about this type of error.
This would be simpler:
Sub MySums()
SumSameCells "B8"
SumSameCells "B9"
SumSameCells "B10"
End Sub
Function SumSameCells(addr As String)
x = 0
For i = 2 To 3
x = x + Sheets(i).Range(addr).Value
Next i
Sheet1.Range(addr) = x
End Function
Variation on the already given answer.
Functions return something. You are using a return value so make it a sub.
No need for a loop in this case. You can just sum direct.
Declare the appropriate type for x. Option Explicit has already been mentioned.
Use the worksheets collection to avoid trying to work with a chart sheet.
Drop the call keyword as is obsolete.
Code:
Option Explicit
Public Sub MySums()
SumSameCells "B8"
SumSameCells "B9"
SumSameCells "B10"
End Sub
Public Sub SumSameCells(ByVal addr As String)
Dim x As Double '<== Use whatever appropriate type is
x = Application.WorksheetFunction.Sum(Worksheets(2).Range(addr), Worksheets(3).Range(addr))
Worksheets("Sheet1").Range(addr) = x
End Sub

Object required error when looping through cells in selection

I am looping through some cells, in a vertical selection, in Excel, and then passing that cell as a parameter to a procedure.
I have done it this way, so I don't have the contents of ProcessCells twice, in the code, once for the while loop, and the second time in the For loop.
If I try and get the value of the cell written out, in the for loop, it works.
If I put the contents of the ProcessCells procedure in the for loop, it also works.
But if I try to pass it as a parameter, into ProcessCells, I am getting an error
'Object Required'
Here is the code, if you want to check it out:
Sub loopThroughCells()
Dim c As Range
Dim autoSelect As String
Dim X, Y As Integer
autoSelect = Cells(3, 2).Value
If StrComp(autoSelect, "Y") = 0 Then
Y = 5
X = 4
While Not IsEmpty(Cells(Y, X).Value)
ProcessCells (Cells(Y, X))
Y = Y + 1
Wend
Else
For Each c In Selection
ProcessCells (c)
Next c
End If
End Sub
Sub ProcessCells(ce As Range)
End Sub
How is
Cells(n,m)
different from
c In Selection
?
The error happens in the For loop, but it doesn't happen in the while loop.
Here is how you should do it:
Option Explicit
Sub TestMe()
Dim c As Range
For Each c In Selection
Call ProcessCells(c)
Next c
End Sub
Sub ProcessCells(ce As Range)
End Sub
You should refer with call, because you have an argument in parenthesis.
Or like this, if you do not like the call:
Option Explicit
Sub TestMe()
Dim c As Range
For Each c In Selection
ProcessCells c
Next c
End Sub
Sub ProcessCells(ce As Range)
End Sub
Plus a small edition of your code. Make your declarations like this:
Dim X as Long, Y As long
In your code X is declared as a variant, and integer is slower and smaller than long - Why Use Integer Instead of Long?
Here is some good explanation when to put the argument in parenthesis and when to use the call - How do I call a VBA Function into a Sub Procedure

Using Excel VBA commands with variables

I'm trying reuse some code and thus make controlls in call a function to get different results. The problem seem to be to get references to the different controlls I want to update.
Function populate(num1, num2)
Dim index As Integer
index = ComboBox1.ListIndex
ComboBox2.Clear
Select Case index
Case Is = 0
With ComboBox2
ComboBox2.List = Worksheets("Sheet1").Range("A1:A10").Value
End With
Case Is = 1
With ComboBox2
ComboBox2.List = Worksheets("Sheet2").Range("A1:A10").Value
End With
Case Is = 2
With ComboBox2
ComboBox2.List = Worksheets("Sheet3").Range("A1:A10").Value
End With
End Select
End Function
Private Sub ComboBox1_Change()
Call populate(1, 2)
End Sub
Private Sub ComboBox3_Change()
Call populate(3, 4)
End Sub
Private Sub ComboBox5_Change()
Call populate(5, 6)
End Sub
Naturally, for every combobox I call ( odd numbers ), it should update the values of the 2nd combobox ( even numbers ). But instead of copying the code to each and every odd numbered combobox, I
d like to have some sort of code that actually gets reused by function calls as shown above.
Unfortunatly, I'm unsure of how to make that work in VBA. Needless to say, I'd like it to be something like ( which does not work ) :
Function populate(num1, num2)
Dim index As Integer
index = ComboBox + num1 + .ListIndex
ComboBox + num1 + .Clear
Select Case index
Case Is = 0
With ComboBox + num2
ComboBox + num2 + .List = Worksheets("Sheet1").Range("A1:A10").Value
End With
Case Is = 1
With ComboBox + num2
ComboBox + num2 + .List = Worksheets("Sheet2").Range("A1:A10").Value
End With
Case Is = 2
With ComboBox + num2
ComboBox + num2 + .List = Worksheets("Sheet3").Range("A1:A10").Value
End With
End Select
End Function
Also had a look at the possible duplicated question, tough that did not solve my problem. In fact, even if the subject seems dupicate, I don't think the question is. I'm trying to combine a partial command and a variable in order to create the command I want to use, whilst the possible duplicate seems to want something slightly different ... I might be wrong, but that's what it seemed like to me.
Instead of passing a number to your populate subroutine, pass the ComboBoxes that you wish to process:
Sub populate(cb1 As ComboBox, cb2 As ComboBox)
Dim index As Integer
index = cb1.ListIndex
With cb2
.Clear
Select Case index
Case 0
.List = Worksheets("Sheet1").Range("A1:A10").Value
Case 1
.List = Worksheets("Sheet2").Range("A1:A10").Value
Case 2
.List = Worksheets("Sheet3").Range("A1:A10").Value
End Select
End With
End Sub
Private Sub ComboBox1_Change()
populate ComboBox1, ComboBox2
End Sub
Private Sub ComboBox3_Change()
populate ComboBox3, ComboBox4
End Sub
Private Sub ComboBox5_Change()
populate ComboBox5, ComboBox6
End Sub
You can reference your comboboxes and other controls using the Sheet.Drawingobjects collection, as:
ActiveSheet.DrawingObjects("ComboBox" & num2)
Assuming you have userform comboboxes then your code can shorten down to:
Option Explicit
Sub populate(num1, num2)
Dim shtName As String
shtName = "Sheet" & (Me.Controls("Combobox" & num1).ListIndex + 1)
With Me.Controls("Combobox" & num2)
.Clear
.List = Worksheets(shtName).Range("A1:A10").Value
End With
End Sub
Otherwise, if you have ActiveX Excel comboboxes then it slightly changes to:
Option Explicit
Sub populate(num1, num2)
Dim shtName As String
shtName = "Sheet" & (ActiveSheet.DrawingObjects("ComboBox" & num1).Object.ListIndex + 1)
With ActiveSheet.DrawingObjects("Combobox" & num2).Object
.Clear
.List = Worksheets(shtName).Range("A1:A10").Value
End With
End Sub