Simplify VBA code with repetitive operations - excel

I created a userform with a large number of textboxes and tick boxes. When the userform is initialised, data is loaded into the textboxes and the tick box values are defined by values specified in one of the workbook tabs.
I have written code with lots of repetition. For example, I include the code below for every textbox that is populated by a number.
Controls("ll_f_m_prior").Value = Format(Range("ll_f_m_prior_p"), "standard")
If Controls("ll_f_m_prior").Value = "n/a" Or Controls("ll_f_m_prior").Value = "" Then
Controls("ll_f_m_prior").Enabled = False
cb1.Enabled = False
End If
I have 25+ textboxes where the code is repeated. I am looking for a way to call a function using two dynamic variables. In the above example, the two variables are ll_f_m_prior and cb1.
I tried this code
Sub error1(var1 As String, var2 As String)
Controls(var1).Value = Format(Range(var1), "standard")
If Controls(var1).Value = "n/a" Or Controls(var1).Value = "" Then
Controls(var1).Enabled = False
x = var2 & ".Enabled"
x = False
End If
End Sub
and calling the subroutine using the function:
Call error1("ll_f_m_prior_p", cb1)

When you call the sub with Call error1("ll_f_m_prior_p", cb1) cb1isn't a string.
Yet the sub expects a string.
So if you change the second variable to act as an object instead, that should make things easier.
Sub error1(var1 As String, var2 As Object)
Controls(var1).Value = Format(Range(var1), "standard")
If Controls(var1).Value = "n/a" Or Controls(var1).Value = "" Then
Controls(var1).Enabled = False
var2.Enabled = False
End If
End Sub

Related

Cycle for to populate word template - data type error

I am trying to do a simple code to populate a word file.
If i run the script with the For cycle below, VBA return me a runtime -13 error: data type not matched correctly about this code line If CB & i = True Then.
Any idea how I could solve to get CB1-CB2-CB3 and so on correctly to claim the different variables in the cycle?
Many thanks
Dim CB1 as integer
Dim CB2 as integer
Dim CB3 as integer
Dim CB4 as integer
If Data_entry.CheckBoxTomato.Value = True Then
CB1 = 0
else
CB1 = 1
end if
'the code repeats for CB2,CB3 and CB4
Dim i As Integer
For i = 1 To 4
If CB & i = True Then
.Text = "MARK" & i
.Replacement.Text = "X"
.Execute Replace:=wdReplaceAll
'other sustitution
else
'other substitution
end if
next i
VBA (and most other languages) don't work like this. When you write If CB & i = True Then, VBA will do the following:
Look for a variable CB. If it doesn't exist and you don´t have Option Explicit set, VBA will create it on the fly as an empty variable.
Concatenate the content of the variable with i. Assuming that you didn't declare CB, the result will be a string, eg the string 1 (which is not the number 1).
Now it will try to compare that string with the value True. One is a String, one is a Boolean, and you can't compare those - you will get a type mismatch error.
Your idea is to have 4 variables and you want to check those variables in your loop. Best to do this is not to declare them as 4 single variables but to use an Array. You can access the members of the variable using the index (the "number"), with is added to the variable name in parentheses.
Dim ch(1 to 4) As Boolean ' Consider to choose a more meaningfull name!
Now you can use them like this:
If Data_entry.CheckBoxTomato.Value = True Then
ch(1) = True
Else
ch(1) = False
End IF
However, that can be written shorter, no need for the If:
ch(1) = Data_entry.CheckBoxTomato.Value
And when you check the content, you would use
For i = 1 To 4
If cb(i) Then ' use the i´th member of the array.
' Note: Some prefer to write If cb(i) = True Then
.Text = "MARK" & i
.Replacement.Text = "X"
.Execute Replace:=wdReplaceAll
End If
Next i

How to validate several userform textboxes?

I have a workbook with userforms to write to several numeric and date fields. I need to validate the textbox control for proper numbers and dates.
Rather than replicate the validation for each textbox, I thought I would call a common subprocedure within the BeforeUpdae event of each textbox.
I have two problems.
If I execute the form and test using text in tbAmount box, it seems the ContolValidate procedure is not called.
If I run it in break mode with a breakpoint on Call ContolValidate(What, CurrentControl), it will step through that procedure.
Even though it steps through the procedure, the Cancel = True does not seem to work.
If I paste the ContolValidate code directly in the BeforeUpdate, the Cancel = True does work.
This code is all on the userform.
Private Sub tbAmount1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
Dim What As String
Dim CurrentControl As Control
What = "NumericField"
Set CurrentControl = Me.ActiveControl
Call ContolValidate(What, CurrentControl)
End Sub
Private Sub ContolValidate(What, CurrentControl)
If Not IsNumeric(CurrentControl.Value) Then
ErrorLabel.Caption = "Please correct this entry to be numeric."
Cancel = True
CurrentControl.BackColor = rgbPink
CurrentControl.SelStart = 0
CurrentControl.SelLength = Len(CurrentControl.Value)
Else
If CurrentControl.Value < 0 Then
ErrorLabel.Caption = "This number cannot be negative."
Cancel = True
CurrentControl.BackColor = rgbPink
CurrentControl.SelStart = 0
CurrentControl.SelLength = Len(CurrentControl.Value)
End If
End If
End Sub
Private Sub tbAmount1_AfterUpdate()
ErrorLabel.Visible = False
tbAmount1.BackColor = Me.BackColor
End Sub
(1) When your control is named tbAmount1 and the code is in the code-behind module of the form, the trigger should fire.
(2) As #shahkalpesh mentioned in his comment, Cancel is not known in your validate-routine. Putting Option Explicit at the top of you code would show you that.
I would suggest to convert the routine to a function. In the code below, I return True if the content is okay and False if not (so you need to put a Not to the result to set the Cancel-parameter)
Private Sub tbAmount1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
Cancel = Not ControlValidate("NumericField", Me.ActiveControl)
End Sub
Private Function ControlValidate(What, CurrentControl) As Boolean
ControlValidate = False
If Not IsNumeric(CurrentControl.Value) Then
errorlabel.Caption = "Please correct this entry to be numeric."
ElseIf CurrentControl.Value < 0 Then
errorlabel.Caption = "This number cannot be negative."
Else
ControlValidate = True ' Input is okay.
End If
If ControlValidate Then
CurrentControl.BackColor = vbWhite
Else
CurrentControl.BackColor = rgbPink
CurrentControl.SelStart = 0
CurrentControl.SelLength = Len(CurrentControl.Value)
End If
End Function
P.S.: I changed the name to ControlValidate - "contol" seems wrong to me...

Why do I get run-time error -2147417848 (80010108) in excel 2013 most of the time I run UserForm?

Task:
I work in Excel2013. I tried to write in VBA a userform to add parameters into dynamic named ranges. All named ranges are held in one sheet and were created using insert>table. I select the range, show existing values and get the new value. All went well untill I actually got to adding value to the range.
Problem:
Excel shuts down most of the time when I try to run the UserForm. Saying:
"Run-time error '-2147417848 (80010108)' Method X of object 'Range' failed"
with different methods ('_Default' last time I checked) at different stages of me breaking code down.
Symtoms:
After this line as I found I get the error:
Cells(y, x) = v
where y and x are integers and v a string I get from the userform. During the debug I checked all values are defined and have values. Moreover, Immediate window with the same numbers input manually (not as variables), works!
It mostly doesn't work, though it did follow through doing the job.
If somone could tell the reason why it breaks it would be greatly appreciated!
Some of the captions and potential values are in Unicode in case it matters, though I tried putting it all in English as well.
Private Sub UserForm_Initialize()
' Preparing all controls of UserForm
Sheet2.Activate
Me.LB_parameter.SetFocus
Me.LB_parameter.value = ""
Me.LB_elements.RowSource = ""
Me.L_element.Enabled = False
Me.TB_element.Enabled = False
Me.TB_element.Locked = True
Me.Btn_Add.Enabled = False
Me.Btn_Add.Locked = True
End Sub
Private Sub LB_parameter_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
' Filling the existing list of values for the selected parametr
If Me.LB_parameter.value <> "" Then
Me.LB_elements.RowSource = "D_" & Me.LB_parameter.value & "s"
Me.L_element.Enabled = True
Me.TB_element.Enabled = True
Me.TB_element.Locked = False
Me.TB_element.SetFocus
End If
End Sub
Private Sub TB_element_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
' Catching the event of filling out the potential new value
Me.Btn_Add.Enabled = True
Me.Btn_Add.Locked = False
Me.L_element.Enabled = False
Me.TB_element.Enabled = False
Me.TB_element.Locked = True
End Sub
Private Sub Btn_Add_Click()
If Me.TB_element.Text = "" Then
' Check if Empty
MsgBox ("Âû íå âïèñàëè çíà÷åíèå!")
' Reset the UserForm
Me.Btn_Add.Enabled = False
Me.Btn_Add.Locked = True
Me.L_element.Enabled = True
Me.TB_element.Enabled = True
Me.TB_element.Locked = False
Me.TB_element.SetFocus
Else
' check if exists
Dim str
For Each str In range("D_" & Me.LB_parameter.value & "s")
If Me.TB_element.Text = str Then
MsgBox ("Ââåäåííîå çíà÷åíèå óæå ñóùåñòâóåò!")
' reset the UserForm
Me.Btn_Add.Enabled = False
Me.Btn_Add.Locked = True
Me.L_element.Enabled = True
Me.TB_element.Enabled = True
Me.TB_element.Locked = False
Me.TB_element.SetFocus
Me.TB_element.value = ""
Exit Sub
End If
Next str
' add to the range here
Dim x As Integer, y As Integer, v As String
y = range("D_" & Me.LB_parameter.value & "s").Rows.Count + 2
x = Me.LB_parameter.ListIndex + 1
v = Me.TB_element.value
' Next line causes break down
Cells(y, x) = v
MsgBox ("Âû äîáàâèëè ýëåìåíò:'" & v & "' äëÿ ïàðàìåòðà '" & Me.LB_parameter.value & "'.")
' Reset the Userform
Me.LB_parameter.SetFocus
Me.LB_parameter.value = ""
Me.LB_elements.RowSource = ""
Me.L_element.Enabled = False
Me.TB_element.Enabled = False
Me.TB_element.Locked = True
Me.Btn_Add.Enabled = False
Me.Btn_Add.Locked = True
End If
End Sub
Sheet I add values to the parametrs and namedranges window:
The UserForm layout:
Cells(y, x) = v
This call is shorthand for this:
ActiveSheet.Cells(y, x).Value = v
I'm not sure why it's crashing on you, but the _Default property of a Range object being its Value, what I'd try here is being more explicit about what I'm trying to achieve, namely:
Exactly which Worksheet is supposed to get modified?
Exactly which Range is being referred to?
I very very very seldom work with ActiveSheet - most of the time I know exactly what object I'm working with. Try using an object. You can create a new one:
Dim target As Worksheet
Set target = ThisWorkbook.Worksheets("pl")
...Or you can give the sheet a code name in the properties toolwindow (F4):
That (Name) property defines an identifier that you can use in VBA code to access a global-scope object that represents that specific worksheet. Assuming that's Sheet1, you could do this:
Sheet1.Cells(x, y) = v
If that still fails, then you can be even more specific about the Range object you're accessing and the property you're setting:
Dim target As Range
Set target = Sheet1.Cells(x, y)
target.Value = v
Normally that wouldn't make a difference though. But I see you're making Range calls, which are also implicitly calling into the ActiveSheet.
I'd start by eliminating these, and working off an explicit object reference.
Then I'd work on getting the spreadsheet logic out of the form; that button click handler is doing way too many things - but I digress into Code Review territory - feel free to post your code there when you get it to work as intended!
Looks like the problem lies in my version of Excel. Not sure if the problem is in my copy or in the 2013 in general. In Excel 2007 on the same machine the UserForm with given suggestions worked continuously without any errors at all! Will update in comments later as I try it in different versions.

Excel Diagramm Seriesnames in VBA

I'm programming a Makro for a Excel xy-Diagramm
The diagramm is nearly correct, but i have dublicate DataSeriesNames;
I already tried to go through all Series and Compare the Names.
There were no Errors, but it didn't work.
Code was like that:
For a=1 to Count
If ActiveChart.SeriesCollection(Count).Name = Range("A2").Value Then
Name = true
End If
a = a + 1
Next
If Name = false Then
ActiveChart.SeriesCollection.NewSeries
End If
ActiveChart.SeriesCollection(Count).Name = "='Tasks'!$D$" & i
ActiveChart.SeriesCollection(Count).XValues = "='Tasks'!$B$" & i
ActiveChart.SeriesCollection(Count).Values = "='Tasks'!$C$" & i
Mfg Robin
There are a couple of things wrong here.
First of all, you're always looking at the same series! I think you want to replace Count with a in your If statement.
If ActiveChart.SeriesCollection(a).Name
Secondly, once that is corrected, even if your Name variable gets set to True at some point, it may get reset to False later as the For...Next loop continues iterating through the remainder of the series collection. To prevent this, add an Exit For like this:
For a = 1 To Count
If ActiveChart.SeriesCollection(a).Name = Range("A2").Value Then
Name = True
Exit For
End If
a = a + 1
Next
Also, I suspect you haven't declaring Name as a Boolean variable, so by default it's a Variant so its value isn't False to start with! You should declare the variable at the top of your procedure with Dim Name as Boolean, and then if you want Name to be False by default, you should really say so explicitly: Put Name = False before the loop. Moreover, Name is a terrible name for a variable... and so is Count Argh! I think your code should look like this:
Option Explicit
Sub MySub()
Dim a As Long
Dim NameExists As Boolean
Dim SeriesCount As Long
SeriesCount = ActiveChart.SeriesCollection.Count
NameExists = False
For a = 1 To SeriesCount
If ActiveChart.SeriesCollection(a).Name = Range("A2").Value Then
NameExists = True
Exit For
End If
a = a + 1
Next
If NameExists = False Then
' Rest of your code goes here...
End Sub

Why can't I send a OptionButton a parameter in VBA

I have the following function in VBA:
Private Function Option1Checked(option1 As OptionButton) As Integer
option1.ForeColor = vbGreen
If (option1.Value = True) Then
Option1Checked = 1
End If
Option1Checked = 0
End Function
Whenever I try to call the function like this
counter = counter + Option1Checked(OptionButton1)
I get a type mismatch error at runtime. But OptionButton1 is OptionButton, so what am I doing wrong?
You're running into one of the 'features' of VBA here. If you refer to some objects, like the option button, without a property specified, VBA assumes you want the default property, not the object itself. In the case of the option button, the default property is .Value, so in your code, OptionButton1 is not the option button object, but rather TRUE or FALSE depending on whether or not the OptionButton1 is checked.
Your best bet will be to change your function to this:
Private Function Option1Checked(option1 As Boolean) As Integer
//option1.ForeColor = vbGreen
If (option1 = True) Then
Option1Checked = 1
Else
Option1Checked = 0
End If
End Function
The downside here is that you cannot change the foreground color of the option button to green without referring to it by name.
An alternative that would get you the functionality that you want would be to pass the name of the option button to your Function.
Private Function Option1Checked(ByVal option1 As String) As Integer
UserForm1.Controls(option1).ForeColor = vbGreen
If (UserForm1.Controls(option1) = True) Then
Option1Checked = 1
Else
Option1Checked = 0
End If
End Function
Sub MyCountingRoutine()
Dim str As String
str = OptionButton1.Name
counter = counter + Option1Checked(str)
End Sub
Make sure you include the Else in the If..Then statement in your function, otherwise you will always get 0 back.

Resources