Is there any way to fix this loop in VBA Excel? - excel

I have few sheets in my Excel. I want this code to apply Some specific Sheet. Since I am not good at vba I am unable to do it. Please somebody help me. How do I add Sheet3 to 17 to this code so that code only run for these sheets.
Sub insertRowsSheets()
' Disable Excel properties before macro runs
With Application
.Calculation = xlCalculationManual
.EnableEvents = False
.ScreenUpdating = False
End With
' Declare object variables
Dim ws As Worksheet, iCountRows As Integer
Dim activeSheet As Worksheet, activeRow As Long
Dim startSheet As String
' State activeRow
activeRow = ActiveCell.Row
' Save initial active sheet selection
startSheet = ThisWorkbook.activeSheet.Name
' Trigger input message to appear - in terms of how many rows to insert
iCountRows = Application.InputBox(Prompt:="How many rows do you want to insert, starting with row " _
& activeRow & "?", Type:=1)
' Error handling - end the macro if a zero, negative integer or non-integer value is entered
If iCountRows = False Or iCountRows <= 0 Then End
' Loop through the worksheets in active workbook
For Each ws In ActiveWorkbook.Sheets
ws.Activate
Rows(activeRow & ":" & activeRow + iCountRows - 1).Insert
Range("A9").Select
Range("A8:C8").Select
Selection.Copy
Range("A9").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
Range("D8:J8").Select
Selection.AutoFill Destination:=Range("D8:J9")
Range("D8:J9").Select
Range("K8:L8").Select
Selection.Copy
Range("K9").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
Range("M8:T8").Select
Selection.AutoFill Destination:=Range("M8:T9")
Range("M8:T9").Select
Range("A8").Select
Next ws
' Move cursor back to intial worksheet
Worksheets(startSheet).Select
Range("A8").Select
' Re-enable Excel properties once macro is complete
With Application
.Calculation = xlCalculationAutomatic
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub

Update Worksheets
This should work the same way as before.
At least it should help you to figure out how to loop through an array of worksheet names instead of the worksheets collection.
I could not figure out the logic of copying and filling. Shouldn't you be filling as many rows as the user selected starting from the active row?
The Code
Option Explicit
Sub insertRowsSheets()
' Define Worksheet Names Array.
Dim wsNames As Variant ' Tab names, not code names.
wsNames = Array("Sheet3", "Sheet4", "Sheet5", "Sheet6", "Sheet7", _
"Sheet8", "Sheet9", "Sheet10", "Sheet11", "Sheet12", _
"Sheet13", "Sheet14", "Sheet15", "Sheet16", "Sheet17")
' Declare object variables
Dim wb As Workbook
Dim ws As Worksheet
Dim RowsCount As Long
Dim ActiveRow As Long
Dim StartSheet As String
Dim i As Long
' Define workbook.
Set wb = ThisWorkbook ' The workbook containing this code.
' State activeRow
ActiveRow = ActiveCell.Row
' Trigger input message to appear - in terms of how many rows to insert
RowsCount = Application.InputBox(Prompt:="How many rows do you want to insert, starting with row " _
& ActiveRow & "?", Type:=1)
' Error handling - end the macro if a zero, negative integer or non-integer value is entered
If RowsCount = False Or RowsCount <= 0 Then Exit Sub
' Loop through the worksheets.
For i = LBound(wsNames) To UBound(wsNames)
With wb.Worksheets(wsNames(i))
.Rows(ActiveRow & ":" & ActiveRow + RowsCount - 1).Insert
.Range("A9:C9").Value = .Range("A8:C8").Value
.Range("D8:J8").AutoFill Destination:=.Range("D8:J9")
.Range("K9:L9").Value = .Range("K8:L8").Value
.Range("M8:T8").AutoFill Destination:=.Range("M8:T9")
End With
Next i
End Sub

' Loop through the worksheets in active workbook
For i = 3 To 17 Step 1 'This runs from the 3rd Sheet to the 17th irrespective of the name. Use array method if the sheets are mixed up
If WorksheetIDExists(i, ActiveWorkbook) Then
Set ws = ActiveWorkbook.Worksheets(i)
With ws
.Rows(activeRow & ":" & activeRow + iCountRows - 1).Insert '<- Kindly note that, if the active row is above A8, the whole script becomes a mess
.Range("A8:C8").Copy
.Range("A9").PasteSpecial Paste:=xlPasteValues
.Range("D8:J9").FillDown
.Range("K8:L8").Copy
.Range("K9").PasteSpecial Paste:=xlPasteValues
.Range("M8:T8").FillDown
.Range("A8").Select
End With
End If
Next i
Add this Function as well.
Function WorksheetIDExists(shtid As Integer, wb As Workbook) As Boolean
Dim sht As Worksheet
On Error Resume Next
Set sht = wb.Worksheets(shtid)
On Error GoTo 0
WorksheetIDExists = Not sht Is Nothing
End Function

Related

How to work with the range method with only one specific cell?

I would like the cells I have selected in the spreadsheet to receive the +1 increment. The code below works fine when I have a range, but when I have only one cells selected the code adds +1 to every cell in the spreadsheet.
Sub Macro_MAIS_1()
'
' Macro_MAIS_1 Macro
'
'
Dim AlocationWorksheet As Worksheet
Dim ActSheet As Worksheet
Dim SelRange As Range
Dim iCells As Integer
On Error GoTo Fim
Set AlocationWorksheet = Worksheets("ALOCAÇÃO")
AlocationWorksheet.Unprotect
Set ActSheet = ActiveSheet
Set SelRange = Selection.SpecialCells(xlCellTypeVisible)
iCells = SelRange.Cells.Count
Range("O7").Select
Selection.Copy
SelRange.PasteSpecial Paste:=xlPasteValues, Operation:=xlAdd, SkipBlanks _
:=False, Transpose:=False
Exit Sub
Fim:
MsgBox Selection.Address
Range("O7").Select
Selection.Copy
SelRange.PasteSpecial Paste:=xlPasteValues, Operation:=xlAdd, SkipBlanks _
:=False, Transpose:=False
End Sub
I would avoid using a selection, but this should work. If you have text you'll run into trouble and need to write out some checks. You also should not be counting all cells, as you might have an overflow of values. Check rows and columns, but not both.
Sub addPlusOne()
Dim aRange As Range, i As Long, j As Long
Set aRange = Selection
If aRange.Rows.Count > 1 Or aRange.Columns.Count > 1 Then
Dim zRng()
zRng = aRange.Value
For i = LBound(zRng) To UBound(zRng)
For j = LBound(zRng, 2) To UBound(zRng, 2)
zRng(i, j) = zRng(i, j) + 1
Next j
Next i
aRange.Value = zRng
Else
aRange.Value = aRange.Value + 1
End If
End Sub
EDIT: OP commented that they want to use visible selection. While this isn't best practice, this will work.
Sub plusOneOnSelection()
Dim aCell As Range
For Each aCell In Selection.SpecialCells(xlCellTypeVisible).Cells
If IsNumeric(aCell) Then aCell.Value = aCell.Value + 1
Next aCell
End Sub

Speeding up macro that goes through a workbook overwriting sheets by value

I made an Excel VBA macro that goes through a workbook sheet by sheet and if the sheet contains no pivots it will overwrite it by value. Unfortunately, on at least one workbook it takes so long that I can go through each sheet myself faster. I am wondering what to do to speed it up. Can anyone suggest what I need to do to accomplish this?
' Convert entire workbook's worksheets to values
'
Sub workbook_overrideSheetsToValues_noSave()
Dim answer As Long, c As Long, ws As Worksheet, report As String
answer = MsgBox("Overwrite formulas in this workbook?", vbYesNo + vbQuestion, "Warning! Formula overwrite!")
If answer = vbNo Then Exit Sub
For Each ws In Worksheets
' only copy over by value if there aren't any pivot tables in the sheet.
If ws.PivotTables.count = 0 Then
Call copySheetByValue(ws.Name)
' save all sheets being skipped
Else
c = c + 1
report = report & Chr(10) & c & ". " & ws.Name
End If
Next ws
If report <> "" Then Call MsgBox("Sheets with pivots were skipped:" & report, 0, "Warning!")
End Sub
Sub copySheetByValue(sheetName As Variant, Optional cellPos As String = "A1")
Dim vFlag As Boolean
' Handle case where sheet is hidden
If sheets(sheetName).Visible = False Then
sheets(sheetName).Visible = True
vFlag = True
End If
Worksheets(sheetName).Unprotect
On Error Resume Next
Worksheets(sheetName).ShowAllData ' Clear filters on all columns
Worksheets(sheetName).Cells.EntireColumn.Hidden = False ' Unhide all columns
On Error GoTo 0
Worksheets(sheetName).Cells.Copy
Worksheets(sheetName).Cells.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
Application.Goto Worksheets(sheetName).Range(cellPos)
' Hide sheet if it was unhidden above
If vFlag = True Then
sheets(sheetName).Visible = False
vFlag = False
End If
End Sub
The workbook where it works really slowly has 27 worksheets with 12 containing pivots. The 15 remaining each have under 1000 rows with the exception of one containing 24000. Doing a Ctrl-A, Ctrl-C and then pasting by value only takes a moment when done manually.
Ben,
First thing would be to add a few controls to make sure Excel isn't allowed to calculated things:
Application.ScreenUpdating=False
Application.Calculation = xlCalculationManual 'xlCalculationAutomatic to revert back
Application.EnableEvents = False
Application.DisplayAlerts = False
Application.DisplayStatusBar = False
Application.StatusBar = False
Then, you did a great thing using Worksheets(sheetName) to make sure your code really execute on the good worksheet but you should really encapsulate it in a With block to avoid the code to evaluate what this Worksheet object is:
With Worksheets(sheetName)
' Handle case where sheet is hidden
If .Visible = False Then
.Visible = True
vFlag = True
....
' Hide sheet if it was unhidden above
If vFlag = True Then
.Visible = False
vFlag = False
End If
End with
Finally, think about writing the value to your cell instead of doing a copy-paste, because copy-paste is really slow. Think about limiting your macro to the range your worksheet really use.
'Supposing your data start at A1
Dim EndRow As Long
Dim EndColumn As Long
With Worksheets(sheetName)
EndRow = .Range("A" & .Rows.Count).End(xlUp).Row
EndColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
.Range(.Cells(1, 1), .Cells(EndColumn, EndColumn)).Value2 = .Range(.Cells(1, 1), .Cells(EndColumn, EndColumn)).Value2
End With

Copy specified columns in particular order

I have 80 or so columns of data. I need just 21 columns.
In my output, I would like the 21 columns to be in a particular order. For example, I want the value from the cell AX2 from my source file to go to A2, BW2 to go to B2, etc.
The source data may differ from month to month and could have as little as 1 row of data or hundreds so I would like this to loop until no data is left.
I got a run time error 424 object required. I have only outlined the rules for two columns but will work on the rest when I get the proper set up.
Sub Macro1()
'
' Macro1 Macro
'
'
Sheet4.Select
Application.ScreenUpdating = False
row_count = 2
Do While Sheet2.Range("A" & row_count) <> ""
Range("AX2:AX1000").Select
Selection.Copy
ActiveWindow.ActivateNext
Range("A").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
ActiveWindow.ActivateNext
Range("BW2:BW1000").Select
Application.CutCopyMode = False
Selection.Copy
ActiveWindow.ActivateNext
Range("B").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
x = x + 1
ActiveWindow.ActivateNext
ActiveSheet.Next.Select
ActiveSheet.Next.Select
Loop
End Sub
I hope I didn't go too far. Try this subscript, it asks you to select a workbook, it will open the workbook, copy column B2 to last used Row on Column B, and paste it on the first workbook. Make sure to rename the CopyFromSheet and CopyToSheet on the code. Please read each line and try to understand what it is doing. Let me know if any questions.
Sub CopyPaste()
Dim openFile As FileDialog, wb As Workbook, sourceWb As Workbook
Dim CopyTo As String, CopyFrom As String
Dim lastRow As Long
Application.ScreenUpdating = False
Set wb = ThisWorkbook
Set openFile = Application.FileDialog(msoFileDialogFilePicker)
openFile.Title = "Select Source File"
openFile.Filters.Clear
openFile.Filters.Add "Excel Files Only", "*.xl*"
openFile.Filters.Add "All Files", "*.*"
openFile.Show
If openFile.SelectedItems.Count <> 0 Then
Set sourceWb = Workbooks.Open(openFile.SelectedItems(1), False, True, , , , True)
CopyFrom = "CopyFromSheetName"
CopyTo = "CopyToSheetName"
lastRow = sourceWb.Sheets(CopyFrom).Cells(Rows.Count, "B").End(Excel.xlUp).Row
sourceWb.Sheets(CopyFrom).Range("B2:B" & lastRow).Copy 'You can copy this Row and the Next and add as many as you want to copy the Columns Needed
wb.Sheets(CopyTo).Range("B1").PasteSpecial xlValues
Application.CutCopyMode = xlCopy
Else
MsgBox "A file was not selected"
End If
Application.ScreenUpdating = True
End Sub
I suggest you separate the copy logic from the setup of which columns to copy. That way it will be much easier to manage the setup.
In this code I have hard coded to Columns Pairs. Alternatively, you could put that data on a sheet and read it in.
Sub Demo()
'declare all your variables
Dim wsSource As Worksheet
Dim wsDest As Worksheet
Dim rSource As Range
Dim rDest As Range
Dim CP() As Variant 'Column Pairs array
Dim idx As Long
'Set up an array of Source and Destination columns
ReDim CP(1 To 21, 1 To 2) 'Adjust size to suit number of column pairs
CP(1, 1) = "AX": CP(1, 2) = "A"
CP(2, 1) = "BW": CP(2, 2) = "B"
'and so on
' Source and Destination don't have to be in the same Workbook
' This code assumes the Source (and Destination) worksbooks are already open
' You can add code to open them if required
' If the data is in the same book as the code, use ThisWorkbook
' If the data is in a different book from the code,
' specify the book like Application.Workbooks("BookName.xlsx")
' or use ActiveWorkbook
'Update the names to your sheet names
Set wsSource = ThisWorkbook.Worksheets("SourceSheetName")
Set wsDest = ThisWorkbook.Worksheets("DestSheetName")
' Notice that form here on the code is independent of the Sheet and Column names
'Loop the column pairs array
For idx = 1 To UBound(CP, 1)
'if the entry is not blank
If CP(idx, 1) <> vbNullString Then
'Get reference to source column cell on row 2
Set rSource = wsSource.Columns(CP(idx, 1)).Cells(2, 1)
'If that cell is not empty
If Not IsEmpty(rSource) Then
'If the next cell is not empty
If Not IsEmpty(rSource.Offset(1, 0)) Then
'extend range down to first blank cell
Set rSource = wsSource.Range(rSource, rSource.End(xlDown))
End If
'Get a reference to the destination range, from row 2, same size as source
Set rDest = wsDest.Columns(CP(idx, 2)).Cells(2, 1).Resize(rSource.Rows.Count)
'Copy the values
rDest.Value = rSource.Value
End If
End If
Next
End Sub

VBA program sub error; trying to set up conditional loop

This program is intended to be used to copy data from a pivot table on another sheet (varying number of rows for each data set). Each set of pasted data is used to create its own waterfall chart, for which I have templates already made on a different sheet.
There are a couple of issues I am having with this code.
1) For some reason, it no longer runs (I refactored the code from a macro) and gives me the error 'Compile Error: Sub or Function not defined'
- I've tried making a new module and a new macro but to no avail
2) Also, I want to change the range that the chart graphs based on the size of the data set. Here's what I have currently hardcoded:
Selection.Formula = "=SERIES(,Sheet5!R8C1:R17C1,Sheet5!R8C4:R17C4,1)"
So, Sheet5!R8C1:R17C1 would need to become SheetN!Start:End
Complete Code below:
Sub WF_New_Sheet()
Dim copyFrom As Range
Dim wS As Worksheet 'use as current worksheet
Dim cht As Chart
'Paste and format data
Set wS = Sheets("Pivot 1")
copyFrom = wSRange("C82:D90")
Set wS = Sheets.Add(After:=Worksheets.Count)
wS.Range("A9").Select
Selection.PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
Selection.Columns.AutoFit
Application.CutCopyMode = False
Range("A9", Range("B" & Rows.Count).End(xlUp).Address).sort Key1:=[b9], _
Order1:=xlAscending, Header:=xlNo 'sorts in 2 lines
Range("A8").Value = "Total"
Range("B8").Value = "=SUM(R[1]C:R[9]C)"
Dim rNum As Integer: rNum = Range("A9", Range("B" & Rows.Count).End(xlUp).Address).Rows.Count
'Paste data template and chart
copyFrom = Sheets("Sheet4").Range("D2:G15") 'sheet 4 is hardcoded and contains templates
wS.Range("D6").Resize(copyFrom.Rows.Count).Value = copyFrom.Value
Sheets("Sheet4").ChartObjects("Chart 1").Activate
Application.CutCopyMode = False
ActiveChart.ChartArea.Copy
wS.Range("I7").Select
ActiveSheet.Paste
ActiveSheet.ChartObjects("Chart 1").Activate
'Set appropriate ranges for chart data; format data for display
ActiveChart.SeriesCollection(2).Select
Selection.Formula = "=SERIES(,Sheet5!R8C1:R17C1,Sheet5!R8C5:R17C5,2)" 'How to make this dynamic?
ActiveChart.SeriesCollection(1).Select
Selection.Formula = "=SERIES(,Sheet5!R8C1:R17C1,Sheet5!R8C4:R17C4,1)" 'How to make this dynamic?
Range("B8").Value = "=SUM(R[1]C:R[9]C)*-1"
With Range("b9", "b17")
.Value = Evaluate(.Address & "*" & -1)
End With
End Sub
*edit code fixed to include sub and end sub
Figured out how to adjust the chart size. Added these lines:
Dim rowStart As Integer: rowStart = InputBox("Please enter starting row of your dataset.")
Dim rowEnd As Integer: rowEnd = InputBox("Please enter ending row of your dataset.")
Set copyFrom = Sheets("Pivot 1").Range(Sheets("Pivot 1").Cells(rowStart, colOne), Sheets("Pivot 1").Cells(rowEnd, colOne))
Set wS = Sheets.Add
wS.Move After:=Sheets(ActiveWorkbook.Sheets.Count)
wS.Range("A9").Resize(copyFrom.Rows.Count, copyFrom.Columns.Count).Value = copyFrom.Value
Set copyFrom = Sheets("Pivot 1").Range(Sheets("Pivot 1").Cells(rowStart, colTwo), Sheets("Pivot 1").Cells(rowEnd, colTwo))
wS.Range("B9").Resize(copyFrom.Rows.Count, copyFrom.Columns.Count).Value = copyFrom.Value

copy every cells in a column to particular cell in new sheet every time with a loop

I seek your help to copy the cell values in (column D) of the first worksheet to a specified cell location in 16 existing worksheets
i want value in
D2 in in sheet1 to sheet2 (G5)
D3 in in sheet1 to sheet3 (G5)
D4 in in sheet1 to sheet4 (G5)
and so on until the D16 is copied to G5 of sheet16
i am a newbie, i looked into several answers and tried to work out on my own but.... nothing happened
Sub latitude()
Dim WS_Count As Integer
Dim I As Integer
WS_Count = ActiveWorkbook.Worksheets.Count
For I = 1 To WS_Count
Do Until IsEmpty(ActiveCell)
Sheets("Calculations").Select
Range("d2").Copy
ActiveCell.Offset(1, 0).Select
'at this point i want it to copy "D3" on next loop
ActiveSheet.Range("G5").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Loop
ActiveSheet.Next.Select
' and because the "Sheets("Calculations").Select" above takes it to the first sheet the whole script is a waste till now
Next I
End Sub
Alistairs attempt is good, i would however not use shtname = "Sheet" & i, instead try the following solution and think about bulletprooving it a bit (existance of worksheets) ;)
Sub Copy_to_G5()
Dim i As Integer
With Application
.DisplayAlerts = False
.ScreenUpdating = False
End With
i = 2
Do Until i = 17
With ThisWorkbook
.Worksheets(1).Cells(i, 4).Copy
.Worksheets(i).Range("G5").PasteSpecial
End With
i = i + 1
Loop
With Application
.DisplayAlerts = True
.ScreenUpdating = True
End With
End Sub
Give this a try.
Option Explicit
Sub Copy_to_G5()
Dim sht1 As Worksheet, ws As Worksheet
Dim i As Integer
Dim shtname As String
With Application
.DisplayAlerts = False
.ScreenUpdating = False
End With
Set sht1 = Sheets("Sheet1")
i = 2
Do Until i = 17
shtname = "Sheet" & i
sht1.Cells(i, 4).Copy
Sheets(shtname).Range("G5").PasteSpecial
i = i + 1
Loop
With Application
.DisplayAlerts = True
.ScreenUpdating = True
End With
End Sub

Resources