I have a Do Until loop in VBA.
My problem is that there is likely to be an error most days when running the macro as not all the sheets will have info on them.
When that happens I just want to start the loop again. I am assuming its not the "On Error Resume Next" I was thinking of counting the rows on the autofilter and then if it was 1 (ie only titles) starting the loop again. Just not sure how to do that.
Dim rngDates As Range 'range where date is pasted on.
'Dim strDate As String
Dim intNoOfRows As Integer
Dim rng As Range
Sub Dates()
Application.ScreenUpdating = False
Set rngWorksheetNames = Worksheets("info sheet").Range("a1")
dbleDate = Worksheets("front sheet").Range("f13")
Worksheets("info sheet").Activate
Range("a1").Activate
Do Until ActiveCell = ""
strSheet = ActiveCell
Set wsFiltering = Worksheets(strSheet)
intLastRow = wsFiltering.Cells(Rows.Count, "b").End(xlUp).Row
Set rngFilter = wsFiltering.Range("a1:a" & intLastRow)
With rngFilter
.AutoFilter Field:=1, Criteria1:="="
On Error Resume Next
Set rngDates = .Resize(.Rows.Count - 1, 1).Offset(1, 0).SpecialCells(xlCellTypeVisible)
End With
With rngDates
.Value = dbleDate
.NumberFormat = "dd/mm/yyyy"
If wsFiltering.FilterMode Then
wsFiltering.ShowAllData
End If
ActiveCell.Offset(1, 0).Select
End With
Application.ScreenUpdating = True
Worksheets("front sheet").Select
MsgBox ("Dates updated")
Loop
You could check existance of data after filtering by using SUBTOTAL formula.
If Application.WorkSheetFunction.Subtotal(103,ActiveSheet.Columns(1)) > 1 Then
'There is data
Else
'There is no data (just header row)
End If
You can read about SUBTOTAL here
Rather than using the Do Until loop, consider using a For Each loop on the Worksheets Collection.
ie.
Sub ForEachWorksheetExample()
Dim sht As Worksheet
'go to error handler if there is an error
On Error GoTo err
'loop through all the worksheets in this workbook
For Each sht In ThisWorkbook.Worksheets
'excute code if the sheet is not the summary page
'and if there is some data in the worksheet (CountA)
'(this may have to be adjusted if you have header rows)
If sht.Name <> "front sheet" And _
Application.WorksheetFunction.CountA(sht.Cells) > 0 Then
'do some stuff in here. Refer to sht as the current worksheet
End If
Next sht
Exit Sub
err:
MsgBox err.Description
End Sub
Also. I would recommend removing the On Error Resume Next statement. It is much better to deal detect and deal with errors rather than ignore them. It could cause strange results.
Related
I have a workbook which has multiple worksheets that vary in name but the content structure of each sheet remains the same. There is only one sheet name that is always constant pie.
I am trying to apply a formula in cell N2 and then copy the formula down to the last active row in all the worksheets except the one named pie
The code I have so far is works for one loop but then i get an error "AutoFill method of Range Class failed"
I have used
Lastrow = Range("M" & Rows.Count).End(xlUp).Row
to determine the last row as column M is always complete.
Any help to complete this would be very much appreciated
Code i have is:
Sub ConcatForm()
Dim wSht As Worksheet
Lastrow = Range("M" & Rows.Count).End(xlUp).Row
Application.ScreenUpdating = False
For Each wSht In Worksheets
If wSht.Name <> "Pie" Then
wSht.Range("N2").FormulaR1C1 = "=CONCATENATE(RC[-3],RC[-2],RC[-1])"
wSht.Range("N2").AutoFill Destination:=Range("N2:N" & Lastrow)
End If
Next wSht
Application.ScreenUpdating = True
End Sub
You don't need to use Autofill to achieve this.
Just apply your formulas directly to your range and use relative references, i.e. K2, rather than absolute references, i.e. $K$2. It will fill down and update the formula for you.
Make sure you are fully qualifying your references. For example, see where I have used ThisWorkbook and the update to how lastrow is initialized. Otherwise, Excel can get confused and throw other errors.
Your lastrow variable hasn't been dimensioned so it is an implicit Variant. You'd be better off dimensioning it explicitly as a Long.
Sub ConcatForm()
Application.ScreenUpdating = False
Dim wSht As Worksheet
Dim lastrow As Long
With ThisWorkbook.Worksheets("Sheet1") 'which worksheet to get last row?
lastrow = .Range("M" & .Rows.Count).End(xlUp).Row
End With
For Each wSht In ThisWorkbook.Worksheets
If wSht.Name <> "Pie" Then
wSht.Range("N2:N" & lastrow).Formula = "=CONCATENATE(K2,L2,M2)"
End If
Next wSht
Application.ScreenUpdating = True
End Sub
you were just one wSht reference away from the goal:
Sub ConcatForm()
Dim wSht As Worksheet
lastRow = Range("M" & Rows.count).End(xlUp).row '<--| without explicit worksheet qualification it will reference a range in the "active" sheet
Application.ScreenUpdating = False
For Each wSht In Worksheets
If wSht.Name <> "Pie" Then
wSht.Range("N2").FormulaR1C1 = "=CONCATENATE(RC[-3],RC[-2],RC[-1])"
wSht.Range("N2").AutoFill Destination:=wSht.Range("N2:N" & lastRow) '<--| this will reference a range in 'wSht' worksheet
End If
Next
Application.ScreenUpdating = True
End Sub
Use following sub...
Sub ConcatForm()
Dim wSht As Worksheet
Lastrow = Range("A" & Rows.Count).End(xlUp).Row
Application.ScreenUpdating = False
For Each wSht In Worksheets
With wSht
If .Name <> "Pie" Then
.Select
.Range("N2").FormulaR1C1 = "=CONCATENATE(RC[-3],RC[-2],RC[-1])"
.Range("N2").AutoFill Destination:=Range("N2:N" & Lastrow)
End If
End With
Next wSht
Application.ScreenUpdating = True
End Sub
I am trying to copy a number of rows from each worksheet to the worksheet called "renew" in the same workbook.
The rows are defined as between the key words of "Service Requests" and "Renewals".
so step 1: define those row numbers, and step 2: copy them to the Renew sheet.
I run into the problem with step 2, somehow, i couldn't work out how to use the rownumber1 and rownumber2 in the copy command.
Any help would be appreciated. Thanks!
Sub test()
Dim ws As Worksheet
Application.ScreenUpdating = False
Sheets("Renew").Activate
For Each ws In Worksheets
If ws.Name <> "Renew" Then
For i = 1 To 100
Dim rownumber1 As Integer
Dim rownumber2 As Integer
If Range("A" & i).Text = "Service Requests" Then
rownumber1 = i
ElseIf Range("A" & i).Text = "Renewals" Then
rownumber2 = i
End If
Next i
'copy rows between rownumber1 and rownumber2 to the renew sheet
ws.Rows("rownumber1:rownumber2").EntireRow.Copy
ActiveSheet.Paste Range("A65536").End(xlUp).Offset(1, 0)
End If
Next ws
End Sub
update:
Sub test2()
Dim ws As Worksheet
Dim rownumber1 As Integer
Dim rownumber2 As Integer
Dim FoundCell As Excel.Range
Application.ScreenUpdating = False
Sheets("Renew").Activate
For Each ws In Worksheets
If ws.Name <> "Renew" Then
Set FoundCell = ws.Range("A:A").Find(what:="Service Requests", lookat:=xlWhole)
If Not FoundCell Is Nothing Then
rownumber1 = FoundCell.Row
End If
Set FoundCell = ws.Range("A:A").Find(what:="Renewals", lookat:=xlWhole)
If Not FoundCell Is Nothing Then
rownumber2 = FoundCell.Row
End If
'copy renewals to the renewalsummary
ws.Rows(rownumber1 & ":" & rownumber2).EntireRow.Copy
ActiveSheet.Paste Range("A65536").End(xlUp).Offset(1, 0)
End If
Next ws
End Sub
What you're looking for is:
ws.Rows(rownumber1 & ":" & rownumber2).EntireRow.Copy
Although, there are some other things to consider with your code. It may be a work in progress so I only answered your question, but:
Your loop is going to return row 100 every time, so I'm curious what the point of your loop is.
You should never DIM in a loop, since you can only declare a variable once and the loop will attempt to do it every time and should throw an error (Dim your rownumber variables with your ws variable).
Why loop to 100? You should loop to the end of the list of values.
Reply to Edits
It looks pretty good. the main thing is that it works. Although I would change this:
ActiveSheet.Paste Range("A65536").End(xlUp).Offset(1, 0)
to this:
ActiveSheet.Paste Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
Hard-coded values aren't very future-proof, this looks at the last row of the sheet (whatever that might be). If any of your sheets start to reach the row max you need to do this:
If Cells(Rows.Count, 1) <> "" Then
ActiveSheet.Paste Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
Else
MsgBox("Sheet " & ws.Name & " is full, row cannot be copied")
End If
But that's probably way down the road and at that point you might be outgrowing Excel.
This code I have cobbled together for collating content from a named worksheet from all open workbooks seems to run fine on my computer, but not on the clients.
Whats going wrong here? I believe we are running the same version of excel, and using identical workbooks to test with.
It gets stuck on line 22:
wkb.Worksheets(sWksName).Copy _
Before:=ThisWorkbook.Sheets(1)
Sorry I don't have the error message!
Sub CopyandCollateQuery1()
With Application ' Scrubs settings that slow process
.ScreenUpdating = False
.EnableEvents = False
.Calculation = xlCalculationManual
.DisplayAlerts = False
End With
Dim wkb As Workbook ' Dim Variables
Dim sWksName As String
Dim Title1 As Range
Dim Title1end As Range
Dim NewRng As Range
Dim check As String
sWksName = "Query1" ' Sets Worksheet to be collated
For Each wkb In Workbooks ' Pulls said worksheet title from each open workbook and copies into macro workbook
If wkb.Name <> ThisWorkbook.Name Then
wkb.Worksheets(sWksName).Copy _
Before:=ThisWorkbook.Sheets(1)
End If
Next
Set wkb = Nothing
For Each ws In ThisWorkbook.Worksheets
With ws
If .Name <> "Collated" Then
rowscount = .Cells(ws.Rows.Count, 2).End(xlUp).Row
.Range("B3" & ":" & "B" & rowscount).Copy
Worksheets("Collated").Activate
Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Select
ActiveSheet.Paste
Application.CutCopyMode = False
End If
End With
Next ws
ActiveSheet.Range("A:A").RemoveDuplicates Columns:=1, Header:=xlNo
If ActiveSheet.Cells(1, 1).Value = "" Then
Rows(1).Delete
ActiveSheet.Cells(1, 2).Value = "Total Combined Count"
End If
ActiveSheet.Cells(1, 1).Activate
For Each ws In ThisWorkbook.Worksheets
With ws
Set lol = ws.Name
If .Name <> "Collated" Then
i = 4
Do While i < rowscount + 1
check = .Range("B" & i).Value
checknum = .Range("B" & i).Offset(0, -1).Value
Sheets("Collated").Activate
Worksheets("Collated").Range("A:A").Find(check, LookAt:=xlWhole).Activate
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = ActiveCell.Value + checknum
checknum = 0
i = i + 1
Loop
End If
End With
Next ws
With Application ' undoes initial processes scrub
.ScreenUpdating = True
.EnableEvents = True
.Calculation = xlCalculationAutomatic
.DisplayAlerts = True
End With
End Sub
It's also having trouble finding the correct last row when performing the collating action, so I will need to adjust that. But that's beside the point.
As mentioned in your code the For loop For Each wkb In Workbooks is used to Pull said worksheet title from each open workbook and and copy into macro workbook. That means it looks for the sheet Query1 in all the open workbooks and when any of the workbook do not have a sheet named Query1 it throws Subscript out of range error.
You can tackle this error in two ways:
1. Make sure all your workbooks has sheet Query1 (don't think this will always happen)
2. Use error handling in your code
For Each wkb In Workbooks
If wkb.Name <> ThisWorkbook.Name Then
On Error Resume Next '<--- add this line in your code
wkb.Worksheets(sWksName).Copy _
Before:=ThisWorkbook.Sheets(1)
End If
Next
On Error Resume Next resumes execution ignoring any error thrown on the next line of code. Please note that On Error Resume Next does not in any way "fix" the error. It simply instructs VBA to continue as if no error occurred. For details see this link.
I am struggling to get this code right. I need to create a new worksheet for every city that is listed in column A of my worksheet called "AllCities", but only if the name of that city doesn't already exist as a worksheet. Right now my code will run but it will still add new worksheets to the end and not name them, when it should only add the last couple cities listed in the column. My current code is below.
Sub CreateSheetsFromAList()
Dim MyCell As Range
Dim MyRange As Range
With Sheets("AllCities").Range("A2")
Set MyRange = Sheets("AllCities").Range("A2")
Set MyRange = Range(MyRange, MyRange.End(xlDown))
For Each MyCell In MyRange
On Error Resume Next
Sheets.ADD After:=Sheets(Sheets.Count) 'creates a new worksheet
Sheets(Sheets.Count).Name = MyCell.Value ' renames the new worksheet
If Err.Number = 1004 Then
Debug.Print MyCell.Value & "already used as sheet name"
End If
On Error GoTo 0
Next MyCell
End With
End Sub
I find it easier to just start working on the worksheet whether it is there or not. Judicious error control will pause processing when attempted on a non-existent worksheet and allow error control to create one.
Sub CreateSheetsFromAList()
Dim myCell As Range, myRange As Range
With Sheets("AllCities")
Set myRange = Sheets("AllCities").Range("A2")
Set myRange = .Range(.Cells(2, 1), .Cells(2, 1).End(xlDown))
For Each myCell In myRange
On Error GoTo bm_Need_Worksheet
With Worksheets(myCell.Value)
'work on the worksheet here
End With
Next myCell
End With
Exit Sub
bm_Need_Worksheet:
With Worksheets.Add(after:=Sheets(Sheets.Count))
'trap an error on bad worksheet name
On Error GoTo 0
.Name = myCell.Value
'prep the worksheet
.Cells(1, 1).Resize(1, 9).Formula = "=""fld ""&SUBSTITUTE(ADDRESS(1,COLUMN(), 4, 1), 1, """")"
With ActiveWindow
.Zoom = 80
.SplitColumn = 0
.SplitRow = 1
.FreezePanes = True
End With
End With
Resume
End Sub
The key here is the Resume statement on the trapped error. It brings code execution back to the line that threw the error and continues processing from there.
I have a workbook that contains a database .
On that database there is a certain row of data that i would like to copy and paste to all sheets.
The copying range varies as the row data on the database changes, but the paste range for each location remains the same.
I have a code that i have done so far but it only allows to copy paste sheet by sheet and i could not define a fixed range in the code.
In this case i would like the selected data to be pasted to B1:N1 of each sheet.
Some help would be needed to paste at one goal to all sheets.
Here is my code:
Dim rng As Range, inp As Range
Set rng = Nothing
Set inp = Selection
inp.Interior.ColorIndex = 37
On Error Resume Next
Set rng = Application.InputBox("Copy to", Type:=8)
On Error GoTo 0
If TypeName(rng) <> "Range" Then
MsgBox "Cancelled", vbInformation
Exit Sub
Else
rng.Parent.Activate
rng.Select
inp.Copy
Worksheets("Sheet2").Paste Link:=True
End If
Application.CutCopyMode = 0
do you need a Loop for all worksheets ?
Dim ws as Worksheet
For Each ws in ActiveWorkbook.Worksheets
If Not ws.Name = "*Name of the database workbook *" Then
Call ws.Range("B1:N1").PasteSpecial(xlPasteAll, xlPasteSpecialOperationNone)
End If
Next
Dim Rng As Range, _
Inp As Range, _
wS As Worksheet
Set Inp = Selection
Inp.Interior.ColorIndex = 37
On Error Resume Next
Set Rng = Application.InputBox("Copy to", Type:=8)
On Error GoTo 0
If TypeName(Rng) <> "Range" Then
MsgBox "Cancelled", vbInformation
Exit Sub
Else
Rng.Parent.Activate
Inp.Copy
For Each wS In ActiveWorkbook.Worksheets
wS.Range("B1").Paste Link:=True
Next
End If
Application.CutCopyMode = 0