For variable = SheetName1 to SheetName2 - vba

Sub my_code_sht1()
Sheets("Sht1").select
'------------------
'------MY CODE-----
'------------------
End sub
Sub my_code_sht2()
Sheets("Sht2").select
'------------------
'------MY CODE-----
'------------------
End sub
"MY CODE" is exactly the same for my 2 sub.
Now I'd like to know if it's possible to regroup these two pieces of code like this:
Sub my_code()
For string = "Sht1" to "Sht2"
Sheets("string").select
'------------------
'------MY CODE-----
'------------------
Next string
End sub
Is something like that possible? Thanks in advance

Here is one way, not that you need to select each sheet
Sub my_code()
Dim ws As Worksheet
For Each ws In Sheets(Array("Sht1", "Sht2"))
ws.Select
'code
Next ws
End Sub

Alternatively, you code extract your code into its own subroutine and pass the sheet name as a parameter or you could use a ParamArray to pass in a variable number of sheet names.
Sub my_code_sht1()
ProcessWorkSheet "Sht1"
ProcessWorkSheet "Sht2"
ProcessWorkSheetsArray "Sht1", "Sht2"
End Sub
Sub ProcessWorkSheet(SheetName As String)
Sheets(SheetName).Select
'------------------
'------MY CODE-----
'------------------
End Sub
Sub ProcessWorkSheetsArray(ParamArray SheetNames())
Dim vName As Variant
For Each vName In SheetNames
Sheets(vName).Select
'------------------
'------MY CODE-----
'------------------
Next
End Sub

Here is another option with array and for loop. It does not use for each, but it checks the length of the array:
Sub my_code()
dim arr_names as variant
dim l_counter as long
arr_names = Array("Sht1", "Sht2", "Sht3")
for l_counter = lbound(arr_names) to ubound(arr_names)
sheets(arr_names(l_counter)).select
'code
next l_counter
end sub

If you apply the same code, then first `
Sub DoWithSheet(wks As Worksheet)
' Code for the Worksheet
Debug.Print wks.Name
' and more
End Sub
And then for two sheets only
DowithSheet Sheets("Sht1")
DoWithSheet Sheets("Sht2")

Related

I have a problem using varaiable range in combo box

This is my code:-
Public CBR As Range
Private Sub ComboBox1_Change()
Dim cbvalue As String
Set CBR = Range("b1")
Call Copy_header
End Sub
Sub Copy_header()
Workbooks("Book2").Worksheets("DropDown").Activate
ActiveSheet.Range.CBR.Select
End Sub
CBR is come as "DOS" or "NDC
I tried to put CBR as range in module also but not working
Obviously I don't know what you are doing, but this may help:
(Indented and spaced)
Option Explicit
Public CBR As String
Private Sub ComboBox1_Change()
Dim cbvalue As String
CBR = "b1"
Call Copy_header
End Sub
Sub Copy_header()
Workbooks("Book2").Worksheets("DropDown").Activate
ActiveSheet.Range(CBR).Select
End Sub
EDIT: This is closer to what you have:
Public CBR As Range
Private Sub ComboBox1_Change()
Dim cbvalue As String
Set CBR = Workbooks("Book2").Worksheets("DropDown").Range("b1")
Call Copy_header
End Sub
Sub Copy_header()
' Workbooks("Book2").Worksheets("DropDown").Activate
' ActiveSheet.Range.CBR.Select
CBR.Worksheet.Activate
CBR.Select
End Sub
EDIT 2: A lot of people don't know this: (Meaning I didn't know this)
That the range includes the workbook and the worksheet!
Option Explicit
Sub sub1()
Dim range1 As Range, range2 As Range
ThisWorkbook.Activate
Sheets("sheet1").Activate
Set range1 = Range("a1")
Set range2 = Workbooks("book2").Sheets("sheet2").Range("b2")
Debug.Print "Range1: ", range1.Worksheet.Parent.Name, range1.Worksheet.Name, range1.Address
Debug.Print "Range2: ", range2.Worksheet.Parent.Name, range2.Worksheet.Name, range2.Address
End Sub

How to remain a code active after all macros have been executed

I have written multiple macros that will be executed with a command button however I want my last macro to remain active after all before macros have been executed. I want Macro15 to remain active after. And for for Macro15 I want if any cell changes I want to highlight that cell with colorindex 3
Sub RunallMacros()
macro1
macro2
macro3
macro5
Macro12
Macro13
Macro14
Macro15
End Sub
Sub macro1()
ThisWorkbook.Sheets("Main").Activate
End Sub
Sub macro2()
Dim myvalue As Variant
myvalue = InputBox("Enter Safety Stock Days")
Range("R5").value = myvalue
End Sub
Sub macro5()
Dim answer As Integer
answer = MsgBox("Are There Any ICF Forms?", vbYesNo + vbQuestion, "Other Sales")
If answer = vbYes Then ICFUserForm.Show
End Sub
Sub macro3()
Dim MyAnswer1 As Variant
Dim MyAnswer2 As Variant
Dim MyAnswer3 As Variant
Dim MyAnswer4 As Variant
Dim MyAnswer5 As Variant
MyAnswer1 = InputBox("Enter Growth Current Month")
Range("m3").value = MyAnswer1
MyAnswer2 = InputBox("Enter Growth Current Month+1")
Range("n3").value = MyAnswer2
MyAnswer3 = InputBox("Enter Growth Current Month+2")
Range("o3").value = MyAnswer3
MyAnswer4 = InputBox("Enter Growth Current Month+3")
Range("p3").value = MyAnswer4
MyAnswer5 = InputBox("Enter Growth Current Month+4")
Range("q3").value = MyAnswer5
End Sub
Sub Macro12()
ActiveCell.FormulaR1C1 = "='raw data'!R[-5]C"
Range("A7").Select
Selection.AutoFill Destination:=Range("A7:A500"), Type:=xlFillDefault
End Sub
Sub Macro13()
Range("C7").Select
Selection.ClearContents
End Sub
Sub Macro14()
For Each ws In ActiveWorkbook.Worksheets
If ws.Name <> "Raw" And ws.Name <> "Main" And ws.Name <> "Calendar" Then
For Each c In ws.Range("A50:A300")
ws.Rows(c.Row).Hidden = c.value = 0
Next
End If
Next
End Sub
Sub Macro15()
If Not Intersect(Target, Range("A7:AH500")) Is Nothing Or _
Not Intersect(Target, Range("A7:AH500")) Is Nothing Then
Target.Interior.ColorIndex = 3
End If
End Sub
A macro that is "active" is doing something, i.e. it is executing code. While it is executing code, the user can't do anything. So either the macro is active or the user is active.
What you want is to respond to an event, in this case the Worksheet.Change event:
Private Sub Worksheet_Change(ByVal Target as Range)
Target.Interior.ColorIndex = 3
End Sub
See https://msdn.microsoft.com/en-us/library/office/ff839775.aspx
Great answer from Paul - try this to get it working;
Private Sub Worksheet_Change(ByVal Sh As Object, ByVal Target as Range)
Target.Interior.ColorIndex = 3
End Sub

VBA Passing cells/range of cells to multiple subs

Hi i have a problem defining a range of cells as a variable depending on what group of cells have changed. So far I have this but it sends up multiple errors, I have tried passing them as string and creating temp variables to hold the values and pass that but no matter what it does not seem to work.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not (Application.Intersect(Worksheets("Sheet1").Range("A:E"), Target) Is Nothing) Then
DoSort("A3:F100", "A4")
End If
If Not (Application.Intersect(Worksheets("Sheet1").Range("H:L"), Target) Is Nothing) Then
DoSort("H3:M100", "H4)
End If
End Sub
Sub DoSort(x As Range, y As Range)
With ThisWorkbook.Sheets("Sheet1")
.Range(x).Sort Key1:=.Range(y), Order1:=xlAscending, Header:=xlYes
End With
End Sub
I had it working before when i hard coded the cells in like this:
Private Sub DoSort2()
With ThisWorkbook.Sheets("Sheet1")
.Range("H3:M100").Sort Key1:=.Range("H4"), Order1:=xlAscending, Header:=xlYes
End With
End Sub
Never really worked in VBA with excel macros so am very new to this, so any help would be appreciated!
See my refactored code below. See my comments for explanation.
Private Sub Worksheet_Change(ByVal Target As Range)
'I used "Me." in place of "Worksheets("Sheet1")." assuming that the Worksheet_Change event is already on Sheet1
If Not Intersect(Me.Range("A:E"), Target) Is Nothing Then
DoSort "A3:F100", "A4"
End If
If Not Intersect(Me.Range("H:L"), Target) Is Nothing Then
DoSort "H3:M100", "H4" 'you were missing a close " here
End If
End Sub
'define x and y as String to pass the string address of the range reference
Sub DoSort(x As String, y As String)
With ThisWorkbook.Sheets("Sheet1")
.Range(x).Sort Key1:=.Range(y), Order1:=xlAscending, Header:=xlYes
End With
End Sub
You could pass the range if you want as well. That would look like this:
DoSort Me.Range("A3:F100"), Me.Range("A4")
Sub DoSort(x as Range, y as Range)
x.Sort Key1:=y, Order1:=xlAscending, Header:=xlYes
End Sub
You could pass the data as a String versus as a Range:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not (Application.Intersect(Worksheets("Sheet1").Range("A:E"), Target) Is Nothing) Then
DoSort("A3:F100", "A4")
End If
If Not (Application.Intersect(Worksheets("Sheet1").Range("H:L"), Target) Is Nothing) Then
DoSort("H3:M100", "H4")
End If
End Sub
Sub DoSort(x As String, y As String)
With ThisWorkbook.Sheets("Sheet1")
.Range(x).Sort Key1:=.Range(y), Order1:=xlAscending, Header:=xlYes
End With
End Sub

Clear cascading ComboBox on reselection

I am in the process of creating a user form in excel that uses several ComboBox'. The first ComboBox lists values from column 1 of a table and the following ComboBox' lists values from the following columns. ComboBox 2 onwards also only lists values depending on the preceding box. All ComboBox' show unique values only.
Here is the current code I am using:
Option Explicit
Private Sub ComboBox1_Change()
Call cValues(ComboBox1.Value, ComboBox2, 2)
End Sub
Private Sub ComboBox2_Change()
Call cValues(ComboBox2.Value, ComboBox3, 3)
End Sub
Private Sub ComboBox3_Change()
Call cValues(ComboBox3.Value, ComboBox4, 4)
End Sub
Private Sub ComboBox4_Change()
Call cValues(ComboBox4.Value, ComboBox5, 5)
End Sub
Private Sub ComboBox5_Change()
Call cValues(ComboBox5.Value, ComboBox6, 6)
End Sub
Private Sub UserForm_Initialize()
Dim Rng As Range
Dim Dn As Range
Dim Dic As Object
With Sheets("Listuni")
Set Rng = .Range(.Range("A2"), .Range("A" & Rows.Count).End(xlUp))
End With
Set Dic = CreateObject("scripting.dictionary")
Dic.CompareMode = vbTextCompare
For Each Dn In Rng: Dic(Dn.Value) = Empty: Next
Me.ComboBox1.List = Application.Transpose(Dic.keys)
End Sub
Sub cValues(txt As String, Obj As Object, col As Integer)
Dim Dn As Range
Dim Rng As Range
Dim Dic As Object
With Sheets("Listuni")
Set Rng = .Range(.Cells(2, col), .Cells(Rows.Count, col).End(xlUp))
End With
Set Dic = CreateObject("Scripting.Dictionary")
Dic.CompareMode = 1
For Each Dn In Rng
If Dn.Offset(, -1).Value = txt Then
If Not Dic.exists(Dn.Value) Then
Dic(Dn.Value) = Empty
End If
End If
Next Dn
Obj.List = Application.Transpose(Dic.keys)
End Sub
The problem I am having occurs when a user makes a reselection of a preceding ComboBox. Instead of clearing the subsequent boxes, all existing selections remain.
I am looking for a way to clear/default the values of subsequent ComboBox every time a reselection of a preceding ComboBox is made. For example if I make a selection in ComboBox 1 and 2 but then change my selection at ComboBox 1, I want ComboBox 2 to clear rather than show my previous selection. Note that the default position for the user form on launch shows no values in any ComboBox.
I have tried using the .clear method on change however this always gets hung up at:
Obj.List = Application.Transpose(Dic.keys)
I suspect this is because a clear is technically a change and therefore it cannot transpose the list of values to other boxes based on a null value.
This clears all subsequent ComboBoxes - if Combo1 changes, Combo2, 3, 4, 5, and 6 are cleared
Option Explicit
Private ws As Worksheet
Private d As Object
Private Sub UserForm_Initialize()
Dim cel As Range, txt As String, rng As Range
Set ws = Worksheets("Listuni")
Set d = CreateObject("Scripting.Dictionary"): d.CompareMode = vbTextCompare
Set rng = ws.Range(ws.Cells(2, 1), ws.Cells(ws.Rows.Count, 1).End(xlUp))
For Each cel In rng: d(cel.Value) = Empty: Next
ComboBox1.List = Application.Transpose(d.keys)
End Sub
Private Function setList(ByVal txt As String, ByRef cmb As ComboBox) As Object
Dim xID As Long, rng As Range, cel As Range, x As Control
xID = Right(cmb.Name, 1)
For Each x In Me.Controls
If TypeName(x) = "ComboBox" Then If Val(Right(x.Name, 1)) > xID - 1 Then x.Clear
Next
Set rng = ws.Range(ws.Cells(2, xID), ws.Cells(ws.Rows.Count, xID).End(xlUp))
d.RemoveAll
For Each cel In rng
If cel.Offset(, -1) = txt Then
If Not d.exists(cel.Value) Then
d(cel.Value) = Empty
End If
End If
Next
If d.Count > 0 Then cmb.List = Application.Transpose(d.keys) Else cmb.Clear
End Function
Private Sub ComboBox1_Change()
setList ComboBox1.Value, ComboBox2
End Sub
Private Sub ComboBox2_Change()
setList ComboBox2.Value, ComboBox3
End Sub
Private Sub ComboBox3_Change()
setList ComboBox3.Value, ComboBox4
End Sub
Private Sub ComboBox4_Change()
setList ComboBox4.Value, ComboBox5
End Sub
Private Sub ComboBox5_Change()
setList ComboBox5.Value, ComboBox6
End Sub

Calling a sub to another worksheet

I have a sub that goes through each worksheet and checks for a flag.
If the flag is raised, I want it to run another sub. The flag checking works, but the other sub which is called runs on the main sheet.
Code:
Sub update()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
If (ws.Cells(1, 5) = 1) Then
Call update2(ws)
End If
Next
End Sub
Sub update2(ws As Worksheet)
ws.clear <----does not work
End Sub
sub dothis()
cells(1,6) = "hallo"
end sub
how do I get this to work?
You need to either use a public variable (generally not recommended) or send the necessary arguments to the procedure(s) where they are needed.
Sub update2(ws As Worksheet)
Call dothis(ws)
End Sub
sub dothis(ws as Worksheet)
ws.cells(1,6) = "hallo"
end sub
Sub update2(ws As Worksheet)
ws.Cells.Clear
End Sub