Select all "visible" worksheets (to print to pdf) - vba

I'm using the following in an existing app and this works fine as is to print particular worksheets to a pdf:
Sheets(Array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")).Select
I'm trying to print an array of just my "visible" worksheets.
I've searched for a solution but can't put my finger on it.

A short approach that uses the False argument to add to an existing selection:
Sub Test()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Sheets
If ws.Visible Then ws.Select False
Next
End Sub

try this
Option Explicit
Sub main()
ThisWorkbook.Sheets(GetVisibleWorksheetsNames(ThisWorkbook)).Select
End Sub
Function GetVisibleWorksheetsNames(wb As Workbook) As String()
Dim ws As Worksheet
Dim wsNames() As String
Dim iV As Long
With wb
ReDim wsNames(1 To .Worksheets.Count)
For Each ws In .Worksheets
If ws.Visible Then
iV = iV + 1
wsNames(iV) = ws.name
End If
Next ws
ReDim Preserve wsNames(1 To iV)
End With
GetVisibleWorksheetsNames = wsNames
End Function

Try this code, it should do what you are looking for.
Sub SelectSheets()
Dim myArray() As Variant
Dim i As Integer
Dim j As Integer
j = 0
For i = 1 To Sheets.Count
If Sheets(i).Visible = True Then
ReDim Preserve myArray(j)
myArray(j) = i
j = j + 1
End If
Next i
Sheets(myArray).Select
End Sub

Related

Activate a closed workbook, perform lRow VBA

I've been through heaps of forums and still can't understand where I'm going wrong. One of my first VBA code attempts.
Private Sub Refresh_Click()
Dim ws As Long
Dim lRow As Variant
Dim wb As Workbook
Dim Arr() As Variant
Dim DHSWMP As String
Dim Dams As String
Dim PotR As String
DHSWMP = "E:\Copy of PWC DHSWMP WorkSheets.xlsm"
'Dams = "x"
'PotR = "x"
Application.ScreenUpdating = False
Set wb = Workbooks.Open(DHSWMP, True, False)
wb.Activate
ws = Worksheets.Count
Do While ws > 0
wb.Sheets(ws).Activate
lRow = Cells(Rows.Count, 2).End(xlUp).Row
MsgBox (lRow)
The code makes it through, however it is performing the lRow on the 'mastersheet' from which the macro is run and not the opened workbook. One bit that does confuse me it that the code does perform the worksheets.count on the opened workbook.
Cheers
Your code runs as expected for me. This is a perfect example of why you should fully qualify your references.
To test what is going on:
Don't turn off ScreenUpdating
Improve your MsgBox
Use F8 to step through the code
Use the Immediate Window for additional test
MsgBox Cells(Rows.Count, 2).End(xlUp).Address & vbNewLine & Cells(Rows.Count, 2).End(xlUp).Parent.Name & vbNewLine & Cells(Rows.Count, 2).End(xlUp).Parent.Parent.Name
Cells.Parent.Name: Returns the name of the Worksheet
Cells.Parent.Name: Returns the name of the Workbook
You should avoid the use of Do and While loops while iterating; if you know both the start and the end. For and For Each loops wi
Here are two different ways of writing your code. Notice that the use of With statements and the qualified references. For example both the Cells and the Rows are qualified to the Worksheet in: .Cells(.Rows.Count, 2).End(xlUp).Row. This is very important to avoid any confusion on the what is the actual target of the code.
Private Sub Refresh_Click()
Application.ScreenUpdating = False
Dim ws As Worksheet, wbDHSWMP As Workbook
Dim lRow As Variant
Dim Arr() As Variant
Dim DHSWMP As String, Dams As String, PotR As String
DHSWMP = "C:\Copy of PWC DHSWMP WorkSheets.xlsm"
Set wbDHSWMP = Workbooks.Open(DHSWMP, True, False)
For Each ws In wbDHSWMP.Worksheets
With ws
lRow = .Cells(.Rows.Count, 2).End(xlUp).Row
MsgBox (lRow)
End With
Next
Application.ScreenUpdating = True
End Sub
Private Sub Refresh_Click()
Application.ScreenUpdating = False
Dim wbDHSWMP As Workbook
Dim lRow As Variant, n As Long
Dim Arr() As Variant
Dim DHSWMP As String, Dams As String, PotR As String
DHSWMP = "C:\Copy of PWC DHSWMP WorkSheets.xlsm"
Set wbDHSWMP = Workbooks.Open(DHSWMP, True, False)
For n = wbDHSWMP.Worksheets.Count To 1 Step -1
With wbDHSWMP.Worksheets(n)
lRow = .Cells(.Rows.Count, 2).End(xlUp).Row
MsgBox (lRow)
End With
Next
Application.ScreenUpdating = True
End Sub

Updating my workbook

I have created a Workbook that is used in various different computers.
Sometimes I add features to it and I would like to easily update it.
The idea is whenever I have a new version of it, I take it to a new computer, save in a temp file and copy the sheets where the data is stored.
Based on the answers I have edit my first draft to: (I didn't know that both workbooks needed to be opened at the same time)
Private Sub CommandButton1_Click()
Dim sh As Worksheet
Dim ws As Worksheet
Dim wb As Workbook
Dim wn As Workbook
Set wn = Workbooks("Reception")
Set wb = Workbooks("Reception2")
With wb
.Sheets("Pass").Range("A1") = "flh"
For Each ws In .Worksheets
Select Case .Name
Case "Formularios", "Coordenador", "LookupList", "Pass"
'Do nothing
Case Else
ws.Delete
End Select
Next ws
End With
With wn
For Each sh In .Worksheets
Select Case .Name
Case "Formularios", "Coordenador", "LookupList", "Pass"
'Do nothing
Case Else
sh.Copy After:=wb.Sheets(wb.Sheets.Count)
End Select
Next sh
End With
End Sub
Case at moment is not working and macro deletes every sheet no matter the name
Thank you all for the feedback
You can find the temp folder by using Environ("temp"), but from your code I'm not sure this is the folder you're using.
This code has a couple of functions to check if the workbook exists and is already open. One other bit of code I'd add is to disable any code in Reception.xlsm from firing when it's opened.
Public Sub MyProcedure()
Dim ws As Worksheet
Dim wb As Workbook
Dim wn As Workbook
Dim Rec1Path As String
Dim Rec2Path As String
Rec1Path = "c:\save\Reception.xlsm"
Rec2Path = "c:\temp\Reception2.xlsm"
'Open or set a reference to Reception.xlsm.
If WorkBookExists(Rec1Path) Then
If WorkBookIsOpen(Rec1Path) Then
'Don't need path for open workbook, just name.
'InStrRev finds last occurrence of "\" (same as InStr, but in Reverse).
Set wn = Workbooks(Mid(Rec1Path, InStrRev(Rec1Path, "\") + 1))
Else
Set wn = Workbooks.Open(Rec1Path)
End If
End If
'Open or set a reference to Reception2.xlsm.
If WorkBookExists(Rec2Path) Then
If WorkBookIsOpen(Rec2Path) Then
Set wb = Workbooks(Mid(Rec2Path, InStrRev(Rec2Path, "\") + 1))
Else
Set wb = Workbooks.Open(Rec2Path)
End If
End If
With wb
.Worksheets("Pass").Range("A1") = "flh"
For Each ws In .Worksheets
Select Case .Name
Case "Formularios", "Coordenador", "LookupList", "Pass"
'Do nothing
Case Else
'You don't really need the count of worksheets if you can guarantee
'you're not going to try and delete the last remaining sheet.
If .Worksheets.Count > 1 Then
Application.DisplayAlerts = False
ws.Delete
Application.DisplayAlerts = True
End If
End Select
Next ws
End With
With wn
'Re-using the ws variable.
For Each ws In .Worksheets
Select Case .Name
Case "Formularios", "Coordenador", "LookupList", "Pass"
'Do nothing
Case Else
ws.Copy After:=wb.Sheets(wb.Sheets.Count)
End Select
Next ws
End With
End Sub
Public Function WorkBookExists(sPath As String) As Boolean
WorkBookExists = Dir(sPath) <> ""
End Function
Public Function WorkBookIsOpen(FullFilePath As String) As Boolean
Dim ff As Long
On Error Resume Next
ff = FreeFile()
Open FullFilePath For Input Lock Read As #ff
Close ff
WorkBookIsOpen = (Err.Number <> 0)
On Error GoTo 0
End Function
Is the workbook open when you try to 'SET' it? If not you will need to open it as such:
Dim wb As Workbook
Set wb = Workbooks.Open("c:\temp\Reception.xlsm")
With some more googling I was able to craft the code that I wanted in the end.
Here is the answer for the curious or for other people looking to do the same:
Private Sub CommandButton1_Click()
Dim sh As Worksheet
Dim ws As Worksheet
Dim LastRow As Long
Dim LastCol As Long
Dim j As Long
Dim Rng As Range
Dim wb As Workbook
Dim wn As Workbook
Set wn = Workbooks("Reception")
Set wb = Workbooks("Reception2")
With wb
.Sheets("Pass").Range("A1") = "flh"
For Each ws In .Worksheets
Select Case ws.Name
Case "Formularios"
'Do nothing
Case "Coordenador"
'Do nothing
Case "LookupList"
'Do nothing
Case "Pass"
'Do nothing
Case Else
With ws
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
LastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
Set Rng = .Range(.Cells(2, 1), .Cells(LastRow, LastCol))
Rng.ClearContents
End With
End Select
Next ws
End With
With wn
For Each sh In .Worksheets
Select Case sh.Name
Case "Formularios"
'Do nothing
Case "Coordenador"
'Do nothing
Case "LookupList"
'Do nothing
Case "Pass"
'Do nothing
Case Else
For j = 1 To wb.Sheets.Count
If sh.Name = wb.Worksheets(j).Name Then
On Error Resume Next
sh.Range("A:J").Copy wb.Worksheets(j).Range("A1")
End If
Next j
End Select
Next sh
End With
Application.CutCopyMode = False
End Sub
Thanks to #Darren Bartrup-Cook for the help.

Subscript out of range - runtime error 9

can you please advise why the below code does not select the visible sheets, but ends in a runtime error. This is driving me crazy. Thanks for any help.
Sub SelectSheets1()
Dim mySheet As Object
Dim mysheetarray As String
For Each mySheet In Sheets
With mySheet
If .Visible = True And mysheetarray = "" Then
mysheetarray = "Array(""" & mySheet.Name
ElseIf .Visible = True Then
mysheetarray = mysheetarray & """, """ & mySheet.Name
Else
End If
End With
Next mySheet
mysheetarray = mysheetarray & """)"
Sheets(mysheetarray).Select
End Sub
Long story short - you are giving a string (mysheetarray) when it is expecting array. VBA likes to get what it expects.
Long story long - this is the way to select all visible sheets:
Option Explicit
Sub SelectAllVisibleSheets()
Dim varArray() As Variant
Dim lngCounter As Long
For lngCounter = 1 To Sheets.Count
If Sheets(lngCounter).Visible Then
ReDim Preserve varArray(lngCounter - 1)
varArray(lngCounter - 1) = lngCounter
End If
Next lngCounter
Sheets(varArray).Select
End Sub
You should define Dim mySheet As Object as Worksheet.
Also, you can use an array of Sheet.Names that are visible.
Code
Sub SelectSheets1()
Dim mySheet As Worksheet
Dim mysheetarray() As String
Dim i As Long
ReDim mysheetarray(Sheets.Count) '< init array to all existing worksheets, will optimize later
i = 0
For Each mySheet In Sheets
If mySheet.Visible = xlSheetVisible Then
mysheetarray(i) = mySheet.Name
i = i + 1
End If
Next mySheet
ReDim Preserve mysheetarray(0 To i - 1) '<-- optimize array size
Sheets(mysheetarray).Select
End Sub
I have tried to explain the Sheets a little, HTH.
Note: Sheets property is defined on Workbook and on Application objects, both works and returns the Sheets-Collection.
Option Explicit
Sub SheetsDemo()
' All sheets
Dim allSheets As Sheets
Set allSheets = ActiveWorkbook.Sheets
' Filtered sheets by sheet name
Dim firstTwoSheets As Sheets
Set firstTwoSheets = allSheets.Item(Array("Sheet1", "Sheet2"))
' or simply: allSheets(Array("Sheet1", "Sheet2"))
' Array("Sheet1", "Sheet2") is function which returns Variant with strings
' So you simply need an array of sheet names which are visible
Dim visibleSheetNames As String
Dim sh As Variant ' Sheet class doesn't exist so we can use Object or Variant
For Each sh In allSheets
If sh.Visible Then _
visibleSheetNames = visibleSheetNames & sh.Name & ","
Next sh
If Strings.Len(visibleSheetNames) > 0 Then
' We have some visible sheets so filter them out
visibleSheetNames = Strings.Left(visibleSheetNames, Strings.Len(visibleSheetNames) - 1)
Dim visibleSheets As Sheets
Set visibleSheets = allSheets.Item(Strings.Split(visibleSheetNames, ","))
visibleSheets.Select
End If
End Sub

Sorting Macro not functioning properly

I've written a macro to cut and paste rows from one sheet ("New Providers - FPPE") into multiple sheets based on a column (H). When I first used it, it was working well, but when I have added additional data to the sorting sheet ("New Providers - FPPE") it is not fully functioning. The macro continues to cut the rows from "New Providers - FPPE" but the rows fail to populate onto the sheets. I have no idea where the rows are going. Does anyone have any insight as to what could be happening? I'm very new to writing macros so any help is appreciated!
Option Explicit
Sub Fr33M4cro()
Dim sh33tName As String
Dim custNameColumn As String
Dim i As Long
Dim stRow As Long
Dim customer As String
Dim ws As Worksheet
Dim sheetExist As Boolean
Dim sh As Worksheet
sh33tName = "New Providers - FPPE"
custNameColumn = "H"
stRow = 7
Set sh = Sheets(sh33tName)
For i = sh.Range(custNameColumn & sh.Rows.Count).End(xlUp).Row To stRow Step -1
customer = sh.Range(custNameColumn & i).Value
For Each ws In ThisWorkbook.Sheets
If StrComp(ws.Name, customer, vbTextCompare) = 0 Then
sheetExist = True
Exit For
End If
Next
If sheetExist Then
CopyRow i, sh, ws, custNameColumn
Else
InsertSheet customer
Set ws = Sheets(Worksheets.Count)
CopyRow i, sh, ws, custNameColumn
End If
Reset sheetExist
Next i
End Sub
Private Sub CopyRow(i As Long, ByRef sh As Worksheet, ByRef ws As Worksheet, custNameColumn As String)
Dim wsRow As Long
wsRow = ws.Range(custNameColumn & ws.Rows.Count).End(xlUp).Row + 1
ws.Rows(wsRow).EntireRow.Value = sh.Rows(i).EntireRow.Value
sh.Rows(i).EntireRow.Delete
End Sub
Private Sub Reset(ByRef x As Boolean)
x = False
End Sub
Private Sub InsertSheet(shName As String)
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = shName
End Sub
I suggest you change your InsertSheet sub to a function that returns a reference to the inserted worksheet:
Function InsertSheet(shName As String) As Worksheet
Set InsertSheet = Worksheets.Add(After:=Worksheets(Worksheets.Count))
InsertSheet.Name = shName
End Function
then change this part of the code:
InsertSheet customer
Set ws = Sheets(Worksheets.Count)
CopyRow i, sh, ws, custNameColumn
to this:
Set ws = InsertSheet(customer)
CopyRow i, sh, ws, custNameColumn

Looping through different sheets

I would appreciate your help with the macro I am trying to create.
I have an xls file with a bunch of worksheets, some of which named "1", "2", "3", and so forth. I would like to create a macro that loops only through those 'number-named' worksheets, hence NOT according to the index as in the code below. (Sheet "1" is not the first sheet in the workbook). Before the loop I need to define both the cell range and sheets.
Below is my (wrong) attempt.
Sub Refresh ()
Dim i As Integer
Dim rng As Range
Set rng = Range("A10:TZ180")
For i = 1 To 30
Sheets(i).Activate
rng.Select
rng.ClearContents
Application.Run macro:="xxx"
Next i
End Sub
dim w as worksheet
for each w in activeworkbook.worksheets
if isnumeric(w.name) then
w.range("A10:TZ180").clearcontents
xxx()
end if
next
If the macro "xxx()" requires a selected range you just need to add a select statement. (Borrowing from GSerg)
Dim w As Worksheet
For Each w In ActiveWorkbook.Worksheets
If IsNumeric(w.Name) Then
w.Range("A10:TZ180").ClearContents
w.Range("A10:TZ180").Select
Application.Run macro:="xxx"
End If
Next
To clear up your misunderstanding about assigning a range see the following:
Sub Refresh()
Dim ws As Worksheet
Dim rng As Range
Dim i As Integer
For Each ws In ActiveWorkbook.Worksheets
If IsNumeric(ws.Name) Then
'you must activate the worksheet before selecting a range on it
ws.Activate
'note the qualifier: ws.range()
Set rng = ws.Range("A10:TZ180")
'since the range is on the active sheet, we can select it
rng.Select
rng.ClearContents
Application.Run macro:="xxx"
End If
Next
End Sub
Sub test2()
Dim ws As Worksheet
Dim rg As Range
Dim arrSheets As Variant
arrSheets = Array("Sheet1", "Sheet2", "Sheet3")
Dim x As Long
For x = LBound(arrSheets) To UBound(arrSheets)
Set ws = Worksheets(arrSheets(x))
ws.Activate
'...
Next
End Sub
Sub test3()
Dim ws As Worksheet
Dim x As Long
For x = 1 To 20
Set ws = Worksheets(CStr(x))
ws.Activate
'...
Next
End Sub
try this
Sub main()
Dim shtNames As Variant, shtName As Variant
shtNames = Array(1, 2, 3, 4) '<== put your actual sheets "number name"
For Each shtName In shtNames
With Worksheets(CStr(shtName))
.Range("A10:TZ180").ClearContents
.Range("A10:TZ180").Select
Application.Run macro:="MacroToRun"
End With
Next shtName
End Sub
Sub MacroToRun()
MsgBox "hello from cells '" & Selection.Address & "' in sheet '" & ActiveCell.Parent.Name & "'"
End Sub