If statement to paste values in VBA - 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

Related

Automatically change sheet names

The following code adds/deletes sheets and inserts a list of all the sheet names while making them hyperlinks:
https://stackoverflow.com/a/48159499/9102830
The following code changes the sheet name depending on several cell names:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Target.Address(0, 0) = "C13" Then
Sh.Name = Sh.Range("B33").Value + "_" + Sh.Range("C13").Value + "_" + Sh.Range("C22").Value + "_N01"
End If
End Sub
The questions are:
Instead of changing when the cell input changes, can this be done automatically?
Could it be a part of the "Sub add_sheet" code? Like, if I add a sheet, it should be named depending on the cells, and since it is copied and the cells are the same, the name will obviously be taken, in which case the name should be "N02" instead of "N01" (instead of the default "(2)" that Excel does).
Finally, when/if the cells are changed, so that the sheet name is not equal to a previous sheet name, could it automatically go back to "N01"?
Thanks in advance!
There are a few ways you can handle that. One way is to combine Worksheet_Activate with Worksheet_Calculate, and throw in a public variable.
When you first open up your worksheet, you will automatically set the variable, and the variable will set with every change as well.
Option Explicit
Public myVar As Variant
Private Sub Worksheet_Activate()
'Set the public variable
myVar = Range("B33").Value
End Sub
Private Sub Worksheet_Calculate()
If myVar <> Range("B33").Value Then
sh.Name = ...
myVar = Range("B33")
End If
End Sub
You can also set the variable when you open the workbook with Workbook_Open as well. Really just depends on your particular needs.

VBA macro to change filters in pivot table

I'm trying do automate a daily report and therefore I want to create two buttons which change the filters of three pivot tables. In detail the buttons shall change the day which is shown. The first filters on yesterday the second one is a reset button do clear all filters and show all days.
The "Resest"-Button is working but the "Yesterday"-Button not.
At the moment the macro looks like that:
Private Sub CommandButton2_Click()
MsgBox ActiveSheet.Range("B1")
With ActiveSheet.PivotTables("Detail_Digital").PivotFields("Tag").CurrentPage = _
ACtiveSheet.Range("B1").Value
End With
End Sub
I've also tried PivotFilters.Add _ , Type:=xlDateYesterday but that isn't working either.
Any suggestions?
Try the code below, it should work, unless your "Date" is formatted differently between the Pivot's data source and Range("B1").
Note: try to avoid using ActiveSheet, instead use referenced objects. In the case below, replace Worksheets("Sheet1") with your sheet's name.
Code
Option Explicit
Private Sub CommandButton2_Click()
Dim PvtTbl As PivotTable
Dim PvtItm As PivotItem
' set the Pivot Table
Set PvtTbl = Worksheets("Sheet1").PivotTables("Detail_Digital")
With PvtTbl
.PivotFields("Tag").ClearAllFilters ' <-- clear all filters to "Tag"
'Debug.Print Worksheets("Sheet1").Range("B1").Value
For Each PvtItm In .PivotFields("Tag").PivotItems
If PvtItm.Name = Worksheets("Sheet1").Range("B1").Value Then
PvtItm.Visible = True
Else
PvtItm.Visible = False
End If
Next PvtItm
End With
End Sub

VBA code simplification

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.

User Choice and loops vba

I'm trying to establish the logic for creating a navigation menu for a budget tracking system: it has 12 sheets for each budget line with 12 monthly tables per sheet.
The navigation menu is based on two combo boxes, one listing the sheets, and the other the names of the months - when a user selects where to go, the sheet and first cell in the chosen table activate.
What I'm looking for is a more effective way to organize this than writing 144 distinct if-then conditions accounting for every possible listindex combination the user might choose. The Select Case approach also works, but it is equally voluminous in scope...
I have been investigating using loops for the purpose - e.g. ListIndex values can be defined in a loop, but I'm coming up short on ideas for the overarching concept.
Thank you in advance!
Here I set up a workbook with 12 worksheets one for each month. Each worksheet has 12 tables on it. When the user selects a worksheet from the dropdown (cboWorkSheets) the second drop down (cboTables) list is cleared and then all the table names from the selected worksheet is added to back to the list.
When a user selects a table name from cboTables the worksheet referenced by cboWorkSheets is searched for that table. The first cell in the table's databody range is then selected.
Option Explicit
Private Sub cboTables_Change()
Dim ws As Worksheet
Dim tbl As ListObject
Set ws = Worksheets(cboWorkSheets.Value)
Set tbl = ws.ListObjects(cboTables.Value)
ws.Activate
tbl.DataBodyRange.Cells(1, 1).Select
End Sub
Private Sub cboWorkSheets_Change()
Dim ws As Worksheet
Dim tbl As ListObject
Set ws = Worksheets(cboWorkSheets.Value)
cboTables.Clear
For Each tbl In ws.ListObjects
cboTables.AddItem tbl.Name
Next
End Sub
Private Sub UserForm_Initialize()
cboWorkSheets.List = Array("Sheet1", "Sheet2", "Sheet3", "Sheet4", "Sheet5", "Sheet6", "Sheet7", "Sheet8", "Sheet9", "Sheet10", "Sheet11", "Sheet12")
End Sub
Doing the sheet selection is pretty straightforward. Just create an array that will hold the sheet name that corresponds to the ListIndex. Something like this
Dim myArray(11) As String
myArray(0) = "a"
myArray(1) = "b"
myArray(2) = "c"
...
myArray(10) = "k"
myArray(11) = "l"
Worksheets(myArray(ComboBox1.ListIndex)).Activate
If the person selects the 5th ComboBox element, sheet "e" would be activated.
Selecting the table cell is a bit more problematic since it depends on where on the sheet the tables are located. If they are spaced equidistantly apart, you can use a simple math formula. That is, if the January table starts at E7, Feb at E27, Mar at e47, then it is a simple matter of using the listindex to calculate the starting row. Eg:
Worksheets(myArray(ComboBox1.ListIndex)).Cells(7 + ComboBox2.ListIndex * 20, "E").Select
Hope this helps. :)
As general interest, this is the functional version of the code for a proof of concept file I built around #Tim's example, given above. Here goes:
In Module1:
Sub ComboBox1_Change()
Dim sheets_array(0 To 2) As Variant
sheets_array(0) = "Sheet1"
sheets_array(1) = "Sheet2"
sheets_array(2) = "Sheet3"
With UserForm1.ComboBox1
.Clear
.List = sheets_array
.Style = fmStyleDropDownCombo
End With
Call ComboBox2_Change
UserForm1.Show
End Sub
Sub ComboBox2_Change()
Dim monthsarray(0 To 3) As Variant
monthsarray(0) = "April"
monthsarray(1) = "May"
monthsarray(2) = "June"
With UserForm1.ComboBox2
.Clear
.List = monthsarray
.Style = fmStyleDropDownCombo
End With
End Sub
In the UserForm1 code window:
Private Sub ComboBox1_Change()
With UserForm1.ComboBox1
Worksheets(.List(.ListIndex)).Activate
End With
End Sub
Private Sub ComboBox2_Change()
With Worksheets(UserForm1.ComboBox1.ListIndex)
.Select
.Cells(7 + UserForm1.ComboBox2.ListIndex * 20, "E").Select
End With
End Sub
#Thomas Inzina, your solution is considerably more elegant and I hope I can think about programming at your level at some point.

Application or object defined error when trying to change background and font colors

I am trying to replace all of the conditional formatting of a spreadsheet (over 30 formatting rules). I have created a class module (called ConditionalFormatting) that has a series of subs for all the formatting rules, with one sub for each range that needs conditional formatting.
The only way I have thought to do this (open to suggestions) is by having the worksheet_change event call a sub in the ConditionalFormatting class called FormattingSubs which will call the correct sub to perform the formatting.
Here is the code for FormattingSubs:
Public Sub FormattingSubs(target As Range)
'have logic here to call the right sub based on what target.address
'is from the worksheet.change event
Select Case target.Name.Name
Case "head_pouch_lot_number"
Call HeadPouchLotNumber(target)
Case "head_consumed_pouch_lot"
Call HeadConsumedPouchLot(target)
Case "section_one_heading"
Call SectionOneHeading
End Select
End Sub
And here is the code for one of the formatting subs, HeadConsumedPouchLot: (note that the color variables are public constants defined in a separate module)
Public Sub HeadConsumedPouchLot(target As Range)
Dim head_consumed_pouch_lot As Range
Dim ws As Worksheet
Set head_consumed_pouch_lot = ActiveSheet.Range("head_consumed_pouch_lot")
Set ws = target.Worksheet
If target.address <> head_consumed_pouch_lot.address Then
Set target = head_consumed_pouch_lot
End If
With ws.Range(target.address)
If Range("section_one_heading").Value <> "" Then
.Interior.ColorIndex = red
.Font.ColorIndex = yellow
Else
.Interior.ColorIndex = lightgreen
.Font.ColorIndex = black
End If
End With
The problem is that when it goes to actually set the color, it gives me the 1004 error: "Application-defined or object-defined error."
What is wrong with my code?
The problem I figured out was that I needed to unprotect my sheet before I could make any changes to it! Thank you all for helping me look for answers.