VBA code simplification - vba

I have the following macro
Private Sub ComboBox1_Change()
If ComboBox1 = "Berlin" Then
Range("C20").Activate
Else
If ComboBox1 = "Paris" Then
Range("C100").Activate
Else
If ComboBox1 = "London" Then
Range("C150").Activate
End If
End If
End If
End Sub
This macro takes the value from a dropdown menu and goes to the cell, where the value is. First question is:
How can I take the values from the cells and not write them specifically in the code?
Second question is:
How can I simplify the procedure and not write for every value an IF?

First, you probably don't actually want to Activate the range! See this answer:
How to avoid using Select in Excel VBA macros
Secondly, your code...
Your Code
Private Sub ComboBox1_Change()
If ComboBox1 = "Berlin" Then
Range("C20").Activate
Else
If ComboBox1 = "Paris" Then
Range("C100").Activate
Else
If ComboBox1 = "London" Then
Range("C150").Activate
End If
End If
End If
End Sub
Using ElseIf
Private Sub ComboBox1_Change()
If ComboBox1 = "Berlin" Then
Range("C20").Activate
ElseIf ComboBox1 = "Paris" Then
Range("C100").Activate
ElseIf ComboBox1 = "London" Then
Range("C150").Activate
End If
End Sub
See documentation: https://msdn.microsoft.com/en-us/library/office/gg251599.aspx
Not hard-coding the values
Private Sub ComboBox1_Change()
Dim rng as Range
Set rng = Nothing
Set rng = ActiveSheet.Range("C:C").Find(ComboBox1.Text)
If Not rng Is Nothing Then
' As I say, you probably don't actually want to use Activate!
rng.Activate
End If
End Sub
See more about the Range object here:
https://msdn.microsoft.com/en-us/library/office/ff838238.aspx
It has useful methods like Address or Value for common use in VBA. The Find function returns a Range object or Nothing if the given value isn't found in the given range.

How can I simplify the procedure and not write for every value an IF?
If you need to test your ComboBox repeatedly (like your If-ElseIf structure), you can use the SELECT CASE to simplify your code:
Private Sub ComboBox1_Change()
Select Case ComboBox1
Case Is = "Berlin"
Range("C20").Activate
Case Is = "Paris"
Range("C100").Activate
Case Is = "London"
Range("C150").Activate
Case Else
Range("C1").Activate
End Select
End Sub
This looks at the value of ComboBox1 and picks the appropriate section to run. For example, if ComboBox1 = "Paris", it skips to the "Paris" case and only runs that section (Range("C100").Activate).
This makes it much easier to add more items to your options, and it reduces the clutter of lots of If-ElseIf-Else lines.
Edit: As mentioned by Wujaszkun, adding the Case Else section handles a ComboBox1 value that was not one of the specified cases.

Related

Save combobox value as global variable and select row of selected value

I want to save selected combobox value as global variable and select row of selected value.
I have an Excel file, where I want to make calculations based on inputs in sheet1 and data on sheet2.
Inputs are provided by combobox1 (list of names from column A in sheet 2), combobox2 (case yes/no) and combobox3 (values 1,2,3).
After I select value in combobox1 (for example: ABC which is value A7 from sheet2), I want to calculate from data in row 7 in sheet2:
B7 (sheet2) + C7 (sheet2) * combobox3 value + D7 (sheet2) * (combobox2(yes = 2 / no = 0).
Can anyone help me with that?
Public SelectedComboBox1 As String
Public SelectedComboBox2 As String
Public SelectedComboBox3 As integer
Public calculate2 As Integer
Private Sub ComboBox1_DropButtonClick()
Sheet1.ComboBox1.List = Sheet2.Range("A3:A46").Value
SelectedComboBox1 = Me.ComboBox1.Value
End Sub
Private Sub ComboBox2_DropButton()
With Me.ComboBox2
.AddItem "YES"
.AddItem "NO"
End With
SelectedComboBox2 = Me.ComboBox2.Value
End Sub
Private Sub ComboBox3_DropButton()
With Me.ComboBox3
.AddItem "1"
.AddItem "2"
.AddItem "3"
End With
SelectedComboBox3 = Me.ComboBox3.Value
End Sub
Public Sub Calculate2_click()
calculate2 = Sheet2.Range("B7") * Sheet2.Range("C7") * SelectedComboBox3+Sheet2.Range("D7")*??
Sheet1.Range("H10").Value = calculate2
End Sub
It seems like you added a ActiveX combobox. You might want to use a form-combobox instead for Excel Sheets. Nevertheless: in the Editor you can add varioous actions to events in the combobox. What you did is add a reaction to the button-down-click Event. There you told excel to fill the list and set the SelectedCombobox variable to the - as of yet undefined - value of the combobox.
What you might want is one sub to fill the list as you did and another sub reacting to the change event of the combobox. That sub will be called as soon as someone changes the value of the box:
Private Sub ComboBox1_DropButtonClick()
Sheet1.ComboBox1.List = Sheet2.Range("A3:A46").Value
End Sub
Private Sub ComboBox1_Change()
SelectedComboBox1 = Me.ComboBox1.Value
End Sub
This should get you a good start. There are plenty of resources out-there that teach you how to write effective macros in Excel.
If you just need a result that uses the value of these three comboboxes, you could also connect a simple form-comboboxes with respective cells and claculate the result out of those cells.
But if you still want to use vba, think about using just one button to trigger a sub and access the values immediately:
Private Sub Button1_click
cells("A1").value=Me.ComboBox1.Value * Me.ComboBox2.Value...
End Sub

Run time error 424 Object Required working with UserForm

I'm trying to link a user form I built in VBA editor for MS Excel 2010 to the data in an excel worksheet, but I'm getting a
run-time error 424: Object required.
I referred to the active worksheet explicitly in my code to try and remedy this, but it still comes up with the same error. My code is:
Private Sub GetData()
Dim r As Long
r = ActiveSheet.Range("B2").Value
If IsNumeric(RowNumber.Text) Then
r = CLng(RowNumber.Text)
Else
ClearData
MsgBox "Invalid row number"
Exit Sub
End If
If r > 1 And r <= LastRow Then
cboFilterResultId.Text = FormatNumber(Cells(r, 1), 0)
txtFolderPaths.Text = Cells(r, 2)
txtFileName.Text = Cells(r, 3)
txtDeletedDate.Text = Cells(r, 4)
txtReason.Text = Cells(r, 5)
txtcboAdd.Text = Cells(r, 6)
txtcboView.Text = Cells(r, 7)
txtcboChange.Text = Cells(r, 8)
DisableSave
ElseIf r = 1 Then
ClearData
Else
ClearData
MsgBox "Invalid row number"
End If
End Sub
Where RowNumber is a textbox where the user can enter the row number for the data they want.
Please help!
I rarely use ActiveSheet just in case that isn't the sheet I'm after. Generally better to be explicit which sheet you're referring to:
r=ThisWorkbook.WorkSheets("Sheet1").Range("B2")
Right, pulling data from a worksheet to a userform... as you haven't said which line your error occurs on and you haven't given us the code for ClearData or DisableSave I'll start from scratch.
Example Form Design
I create a blank userform and add three text boxes and a spin button to it:
txtRowNumber holds the row number that the data is pulled from.
TextBox1 and TextBox2 will hold my sample values.
In the Tag property of TextBox1 I enter 1 and in the Tag property of TextBox2 I enter 2. These are the column numbers that the data will be pulled from.
In reality I usually add extra stuff, for example, 8;CPER;REQD. I'd then use some code to pull it apart so it pastes in column 8, must have a percentage and is a required entry.
spnButton is used to quickly move up or down a row.
We'll need two procedures to populate the form from the given row number and to clear all controls on the form (ready for the next row to be brought in).
Any textbox or combobox that has something in it's Tag property is cleared:
Private Sub ClearForm()
Dim frmCtrl As Control
For Each frmCtrl In Me.Controls
If frmCtrl.Tag <> "" Then
Select Case TypeName(frmCtrl)
Case "TextBox", "ComboBox"
frmCtrl.Value = Null
Case Else
'Do nothing.
End Select
End If
Next frmCtrl
End Sub
Any control that has a Tag value (it's assumed the value is correct) is populated from the specified RowNumber and column (from the Tag value). The value is always taken from the sheet called MyDataSheet in the workbook containing the VBA code (ThisWorkbook) no matter which is currently active:
Private Sub PopulateForm(RowNumber As Long)
Dim frmCtrl As Control
For Each frmCtrl In Me.Controls
With frmCtrl
If .Tag <> "" Then
.Value = ThisWorkbook.Worksheets("MyDataSheet").Cells(RowNumber, CLng(.Tag))
End If
End With
Next frmCtrl
End Sub
Whenever txtRowNumber changes the form should update with values from the indicated row. To do this we'll need to clear the form of current data and then repopulate it:
Private Sub txtRowNumber_Change()
ClearForm
PopulateForm CLng(Me.txtRowNumber)
End Sub
The spin button should increase/decrease the value in .txtRowNumber. I've added checks that it doesn't go below 1. You should also add checks that it doesn't go higher than the last populated row.
Private Sub spnButton_SpinDown()
With Me
.txtRowNumber = CLng(.txtRowNumber) + 1
End With
End Sub
Private Sub spnButton_SpinUp()
With Me
If CLng(.txtRowNumber) > 1 Then
.txtRowNumber = CLng(.txtRowNumber) - 1
End If
End With
End Sub
Finally, the form should be populated when it is first opened:
Private Sub UserForm_Initialize()
With Me
.txtRowNumber = 2
.spnButton = .txtRowNumber
PopulateForm .txtRowNumber
End With
End Sub

If statement to paste values in VBA

I am NEW to VBA and need some help. I am trying to write something that will look in my sheet named 'ROLLUP' and if cell H5 contains the letters "jan", then I would like to paste the values from sheet named 'Project" cells I97:I102 in sheet named "Detail" cells H38:H43. The following is my code and nothing happens. Any help?
Option Explicit
Private Sub ETC_pop()
month = Worksheets("ROLLUP").Range("H5")
If ws.Name = "Detail" Then
If InStr(month, ("Jan")) > 0 Then
Worksheets("Detail").Range("H38:H43").Value = Worksheets("Project").Range("I97:I102").Value
End If
End If
End Sub
You can make this one If-statement with two parts
Private Sub ETC_pop()
If Sheets("ROLLUP").Range("H5").Text="Jan" Then
Sheets("Detail").Range("H38:H43").Copy
Sheets("Project").Range("I97:I102").PasteSpecial xlPasteValues
End If
End Sub
Edit:
To account for H5 containing a date, we want to use MONTH() function such as:
Private Sub ETC_pop()
If Month(Sheets("ROLLUP").Range("H5"))=1 Then
Sheets("Detail").Range("H38:H43").Copy
Sheets("Project").Range("I97:I102").PasteSpecial xlPasteValues
End If
End Sub
It's better not to use a variable Month which is similar to the function, but to use a distinct name, like myMonth.
Other notes are inside the code (as comments).
Option Explicit
Private Sub ETC_pop()
Dim myMonth As String
myMonth = Worksheets("ROLLUP").Range("H5")
'If ws.Name = "Detail" Then '< -- not needed according to your post
' another option >> use if Case In-sensitive
' If UCASE(myMonth) Like "*JAN*" Then
If myMonth Like "*jan*" Then
Worksheets("Detail").Range("H38:H43").Value = Worksheets("Project").Range("I97:I102").Value
End If
'End If
End Sub

How to create a kind of variable Checkbox in vba excel

I have the next excel sheet with many checkboxs.
The problem is when I am coding I have to do some functions with Valor1 and Valor2 depending of if the checkbox is activate.
Well I have the code.
Option Explicit
Sub Casilladeverificación1_Haga_clic_en()
Range("c12").Activate
Do
If CheckBox1.Value Then
Call fucntion1
'Works for the first row, but for the second row int shoul be check CheckBox12 ,a next CheckBox23 ...
If CheckBox2.Value Then
Call fucntion1
If CheckBox2.Value Then
Call fucntion3
....
ActiveCell.Offset(1, 0).Activate
While Not IsEmpty(ActiveCell.Value2)
End Sub
But you can notice I dont want to made all the case with all the checkbox, there is a solve for this like checkbox[i]
I would put all of your functions into one big function and the functionality would separated by a Select Case block.
Private Sub functionRouter(checkAction as integer)
Select Case checkAction
Case 1
'Code for function one
Case 2
'Code for function two
''Etc.
End Select
End Sub
You're going to want to loop over all your check boxes. This is going to depend on what checkbox you are using.
Sub test()
Dim chkBox As CheckBox
Dim chkBox2 As OLEObject
'Regular
For Each chkBox In Sheets("Sheet1").CheckBoxes
Debug.Print chkBox.Caption
Next chkBox
'ActiveX
For Each chkBox2 In Sheets("Sheet1").OLEObjects
If TypeName(chkBox2.Object) = "CheckBox" Then
Debug.Print chkBox2.Object.Value
End If
Next chkBox2
You could do a few different things with all of your checkboxes. You could use the tagproperty (you would need to set all of them, but this allows for duplicates). Then call functionRouter(chkBox.tag) Or you could parse something from the name functionRouter Right(chkBox.name, 1)
You can iterate checkboxes on worksheet by using this loop:
For Each chk In ActiveSheet.CheckBoxes
MsgBox chk.Name
Next
It won't work if you use ActiveX controls though.

Populate Combobox2 Based on Previous Combobox1 Selection

I have two combobox.The first combobox has three items as:{One, Tow, Three} now I would like to load the second combobox based on what user select in combobox1.
For example if user select One in the from combobox1 then namebox1 will populate to combox2
and if if user select Two in the from combobox2 then namebox2 will populate to combox2 and so on..
Can you please let me know how I can do this in VBA?
Thanks
Here is the Update Code:
Private Sub ComboBox1_Change()
Me.ComboBox2.Clear
Select Case Me.ComboBox1.Value
Case "One"
With Me.ComboBox2
.RowSource = "nameBox1"
End With
Case "Two"
With Me.ComboBox2
.RowSource = "nameBox1"
End With
Case "Three"
With Me.ComboBox2
.RowSource = "nameBox1"
End With
End Select
End Sub
Please be advise that I didn't use .addItem to populate the ComboBox1. It hasbeen populate by same method .RowSource which is usinf excel collection selection box
The easiest way to do this would be using a Select Case statement.
Suppose you had
Private Sub ComboBox1_Change()
Me.ComboBox2.Clear
Select Case Me.ComboBox1.Value
Case "One"
' If your data was information you wanted to put in yourself:
With Me.ComboBox2
.AddItem "Adam"
.AddItem "Allen"
.AddItem "Andy"
End With
Case "Two"
' If your data existed in a worksheet range
ComboBox2.RowSource = MyRange.Address
End With
End Select
End Sub
I hope this explains enough to get you going - Otherwise, I'm a bit confused - Can you explain what you mean by NameBox in your question