Excel VBA create new sheet and copy text into cell - excel

I have been using the below code successfully for years but recently it has stopped working. I've since upgraded to Office 365 and still no joy. Essentially the code should copy the Sheet "Response", paste a copy of a cell from "Database" and name the new sheet appropriately. It continues creating new sheets in the workbook until the end of the Database list.
If I run the code as is I get the following: "Run-time error '1004': Microsoft Excel cannot paste the data." When I look at the worksheets, evidentally the code runs and creates a sheet "Response4" (I've only given the database 4 lines to copy). Debug highlights the line ActiveSheet.Paste link:=True. I tested
Frustratingly the code works outside of my company's system (i.e., I sent it to a friend with dummy data and it worked perfectly fine).
Any suggestions very welcome!
Sub CopyCatView()
'NumResp = last row with a responses to the question held within the question 'Themes' database sheet
Dim NumResp As Integer
'x for looping variable
Dim x As Integer
'y for response number variable
Dim y As Integer
Dim ws As Worksheet
Sheets("Database").Activate
NumResp = Range("NumRowsD1").Value + 2
'NumRowsD1 is a named range comprising cell A1 on the Database sheet, which calculates by formula the number of comments in the database
For x = 3 To NumResp
Sheets("Response").Copy before:=Sheets("Response")
y = NumResp - x + 1
ActiveSheet.Name = "Response" & y
ActiveSheet.Range("C2").Value = Sheets("Database").Range("B" & x).Value
ActiveSheet.Range("AA5:CR5").Select
Selection.Copy
Sheets("Database").Select
Cells(x, 3).Select
ActiveSheet.Paste link:=True
Sheets("Response" & y).Activate
ActiveSheet.Range("F4").Select
Selection.Copy
Sheets("database").Select
Cells(x, 70).Select
ActiveSheet.Paste link:=True
'duplicates the Response sheet as many times as there are comments (=X), numbers them Response1 to ResponseX, copies each comment into the white box on a different response sheet from Response1 to ResponseX
'Also links through the check box reporting to the relevant row in the Database sheet
Next x
'at the end hide Sheet "Response"(deleting brings up prompts for every sheet deleted!)
Sheets("Response").Select
ActiveWindow.SelectedSheets.Visible = False
Sheets("Database").Activate
Range("A1").Select
End Sub

Since the "paste with link" requires ranges to be selected before pasting, I'd skip that and create a method to perform that function.
Also - use worksheet variables to reduce the repetition in your code and make for easier maintenance.
Sub CopyCatView()
Dim NumResp As Long, x As Long, y As Long 'prefer Long over Integer
Dim wsDB As Worksheet, wsResp As Worksheet, ws As Worksheet
Set wsDB = ThisWorkbook.Worksheets("Database")
Set wsResp = ThisWorkbook.Worksheets("Response")
NumResp = wsDB.Range("NumRowsD1").Value + 2
For x = 3 To NumResp
wsResp.Copy before:=wsResp
Set ws = ThisWorkbook.Sheets(wsResp.Index - 1) 'get a reference to the copy
y = NumResp - x + 1
ws.Name = "Response" & y
ws.Range("C2").Value = wsDB.Range("B" & x).Value
LinkRanges ws.Range("AA5:CR5"), wsDB.Cells(x, 3)
LinkRanges ws.Range("F4"), wsDB.Cells(x, 70)
Next x
wsResp.Visible = False
wsDB.Activate
wsDB.Range("A1").Select
End Sub
'Link two ranges in the same workbook
' rngFrom = contiguous (single-area) source range
' rngTo = top-left cell of the destination range
Sub LinkRanges(rngFrom As Range, rngTo As Range)
Dim r As Long, c As Long, nm As String
If Not rngFrom.Parent Is rngTo.Parent Then
nm = "'" & rngFrom.Parent.Name & "'!"
End If
For r = 1 To rngFrom.Rows.Count
For c = 1 To rngFrom.Columns.Count
rngTo.Cells(r, c).Formula = "=" & nm & _
rngFrom.Cells(r, c).Address(False, False)
Next c
Next r
End Sub

Related

VB -Copy and Paste Nested Loop in Excel

So I have a problem that this is generating random results with the Qty.
I am trying to make each qty (in their qty's) a new line on a new spreadsheet.
It creates the new sheet, and references the old sheet...
the code copies and pastes the lines...
It just doesn't loop the do while in the correct amount of times. I have tried different operands (>= 0) and altering the variable values to make this work.
It does not seem to be patternized as to why it is happening. Sometimes it does it in the correct amount of loop cycles, others it does not. This occurs on multiple values. Any help is greatly appreciated.
Sub copyPasta()
'
' copyPasta Macro
' This will take the qty, if greater than one in Column C and copy the row
'to a new sheet the amount of time the qty.
'
'
'Set Variable Types
Dim lineItemQty As Integer
Dim newLineItemQty As Integer
Dim LastRow As Integer
Dim strSheetName As String
Dim newSheetName As String
Dim i As Integer
Application.DisplayAlerts = False
'name a variable after the existing active sheet
strSheetName = ActiveSheet.Name
'add a sheet in addition to the current
Sheets.Add After:=ActiveSheet
'set a variable used in loops to the sheet being copied to
newSheetName = ActiveSheet.Name
'Return to first sheet
Sheets(strSheetName).Activate
' Set For Loop to max row
LastRow = Sheets(strSheetName).Range("C:C").Find("*", searchdirection:=xlPrevious).Row
'for loop to run through all rows
For i = 3 To LastRow Step 1
'initializing variable to Qty value in table
lineItemQty = Range("C" & i).Value
'initializing variable within in line of for looping
newLineItemQty = lineItemQty
'do while loop to keep copying/pasting while there are still qty's
Do While newLineItemQty > 0
'do while looped copy and paste
'copy the active row
Sheets(strSheetName).Activate
Rows(i).Select
Selection.Copy
'paste active row into new sheet
Sheets(newSheetName).Select
Rows("3:3").Select
Selection.Insert Shift:=xlDown
newLineItemQty = newLineItemQty - 1
Loop
Next i
Application.DisplayAlerts = True
End Sub
You can consider using (or taking parts from) the below alternative. A couple of note worthy notes are
You should avoid using .Select and .Activate. See here for details
Life is easier when you declare short variables. Here we just have ws for worksheet and ns for newsheet. You then need to actively state what sheet you are refferring to in your code (instead of using .Select or .Activate to do so by prefixing all objects with the appropriate worksheet variable)
You do not need to add Step 1 in your loop. This is the default - you only need to add this when you are deviating from the default!
There are a few ways to add sheets. Nothing wrong with the way you did - here is just an alternative (yay learning) that happens to be my preferred method.
To copy n many times, just create a nested loop and for 1 to n. Notice we never really use the variable n inside the loop which means the exact same operation will execute, we just want it to execute n times.
Sub OliveGarden()
Dim ws As Worksheet: Set ws = ThisWorkbook.ActiveSheet
Dim ns As Worksheet: Set ns = ThisWorkbook.Sheets.Add(After:=Sheets(ThisWorkbook.Sheets.Count))
ns.Name = ws.Name & " New"
Dim i As Long, c As Long
'Application.ScreenUpdating = False
For i = 3 To ws.Range("C" & ws.Rows.Count).End(xlUp).Row
If ws.Range("C" & i) > 0 Then
For c = 1 To ws.Range("C" & i)
LRow = ns.Range("A" & ns.Rows.Count).End(xlUp).Offset(1).Row
ws.Range("C" & i).EntireRow.Copy
ns.Range("A" & LRow).PasteSpecial xlPasteValues
Next c
End If
Next i
'Application.ScreenUpdating = True
End Sub

VBA - Prevent the Adding of Multiple Sheets

The purpose of my macro is to allow a user to select a range in their model that they want to check for hard codes. The macro then prints the worksheet, cell address, and value of the hard code on a summary sheet. The macro currently works great if you're selecting only from one sheet; however, if you extend your selection to multiple sheets, the macro will create multiple sheets instead of just one which it is intended to do. Thank you in advance for your time and help
Set RngCon = Selection.SpecialCells(xlCellTypeConstants, xlNumbers)
Set SumWS = Worksheets.Add
Username = InputBox("Please create a name for the output sheet (i.e. Whs Industry Hard Codes)")
SumWS.Name = Username
x = 1
SumWS.Cells(x, 1) = "Worksheet"
SumWS.Cells(x, 2) = "Address"
SumWS.Cells(x, 3) = "Value"
For Each c In RngCon
x = x + 1
SumWS.Cells(x, 1) = c.Worksheet.Name
SumWS.Cells(x, 2) = c.Address(False, False)
SumWS.Cells(x, 3) = c.Value
Next c
you could do something like that:
Sub test()
Dim SumWS As Worksheet
Dim ws As Worksheet
Dim SelectedSheets() As String
Dim n As Long
Dim i As Long
n = 0
For Each ws In ActiveWindow.SelectedSheets
ReDim Preserve SelectedSheets(n)
SelectedSheets(n) = ws.Name
n = n + 1
Next
Sheets(SelectedSheets(0)).Select
Set SumWS = Worksheets.Add
Debug.Print "Sum Sheet: " & SumWS.Name
For i = LBound(SelectedSheets) To UBound(SelectedSheets)
Debug.Print "Selected Sheet #" & i & ": " & SelectedSheets(i)
Next i
End Sub
In the first for you save the selected sheets in an array. Then you can select one specific sheet and add your sum sheet. The second for shows how to work with the stored information. You can loop the selected sheets to get all values and - if needed - select them again.
credits to Siddharth Rout (Similar case)

Looping Excel VBA Macro that Runs other Macros

I am trying to make a vba program that will take the stock ticker in column A and paste it on a different "settings" sheet in a cell, then the program will execute two other vba codes that download historical data and backtest my formula. Then the program will return to the "data" sheet and print the value in "B10" on "settings" into column D in "data". I need the printed value to be in column d corresponding to the ticker's row. The program has to repeat 500 times. Can you help me find how to do this or point out what is wrong in my code? Thanks!
Sub finalbalance()
Dim ticker As Range
Dim i As Long
Sheets("results").Activate
Set ticker = ActiveCell
For i = 1 To 500
Sheets("results").Activate
ticker.Select
Selection.Copy
Sheets("Settings").Select
Range("B1").Select
ActiveSheet.Paste
Application.Run "datadownload"
Application.Run "btest"
ticker.Offset(0, 3) = Sheets("settings").Range("B10")
ticker.Address = ticker.Offset(1, 0)
Next i
End Sub
The problem is you can't assign a value to the .Address property:
'Instead of
ticker.Address = ticker.Offset(1, 0)
'Use:
Set ticker = ticker.offset(1, 0)
And that will get your code working as is. However, the select statements really aren't necessary and should be avoided. Here's a cleaned up version of the code:
Sub finalbalance()
Dim wsResults As Worksheet
Dim wsSettings As Worksheet
Dim rngStartCell As Range
Dim arrResults() As Variant
Dim lNumReps As Long
Dim i As Long
Set wsResults = Sheets("Results")
Set wsSettings = Sheets("Settings")
Set rngStartCell = wsResults.Range("A2")
lNumReps = 500
ReDim arrResults(1 To lNumReps)
For i = 1 To lNumReps
wsSettings.Range("B1").Value = rngStartCell.Offset(i - 1).Value
Application.Run "datadownload"
Application.Run "btest"
arrResults(i) = wsSettings.Range("B10").Value
Next i
rngStartCell.Offset(, 3).Resize(lNumReps).Value = Application.Transpose(arrResults)
End Sub

How to keep a log of usage of a macro

I have a rather silly problem. I have a macro (linked to a button) which copies cells A1:A2 from one worksheet (namedFP) to another worksheet (Log). I intend to copy these 2 cells on the log sheet every time I hit the macro button. The problem I am facing right now is that when I use the button multiple times, these cells are getting copied over each other instead of using the next available row to paste the cells.
This is what I have now, and I tried changing the 'Rowcount+1' to 'RowCount+2' but that did not work. Any help is appreciated.
DHRSheet.Select
Range("A1:A2").Select
Selection.Copy
LogSheet.Select
RowCount = LogSheet.UsedRange.Rows.Count
Dim r As Integer
r = RowCount + 1
Dim infocell As Range
Set infocell = Cells(r, 1)
infocell.Select
ActiveSheet.Paste
infocell.Value = DHRSheet.Name & "$" & infocell.Value
DHRSheet.Select
ActiveWorkbook.Save
Is this what you are trying?
Sub Sample()
Dim LogSheet As Worksheet, DHRSheet As Worksheet
Dim lrow As Long
'~~> Change this as applicable
Set LogSheet = Sheets("Sheet1")
Set DHRSheet = Sheets("Sheet2")
With LogSheet
lrow = LogSheet.Range("A" & .Rows.Count).End(xlUp).Row + 1
DHRSheet.Range("A1:A2").Copy .Range("A" & lrow)
End With
End Sub
Here's a function I use that is very reliable and always returns the last row of a sheet without fail:
(possibly excessive for your simple use, but I always recommend it)
Public Function LastRowOfSheet(ByVal TestSheetNumber As Variant)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Input: Sheet index # or Sheet name
' Output: Last row of sheet.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim intNumberOfRowsInWorksheet As Long
intNumberOfRowsInWorksheet = Sheets(TestSheetNumber).UsedRange.Rows.Count
intNumberOfRowsInWorksheet = intNumberOfRowsInWorksheet + Sheets(TestSheetNumber).UsedRange.Row - 1
LastRowOfSheet = intNumberOfRowsInWorksheet
End Function
And I'd clean up your above code and use something like this:
Sub Move2RowsToEnd()
Dim iNextRowOfOutput As Long
Dim iRowNumber As Long
'- use the function to find the last row of the output sheet. we'll be pasting to the first row after.
iNextRowOfOutput = (LastRowOfSheet("Log") + 1)
'- you can adjust this for loop to loop through additional cells if you need to paste more than 2 rows in the future.
For iRowNumber = 1 To 2
'- for each row of input (2 total) set the value of the output sheet equal to it.
Sheets("Log").Range("A" & iNextRowOfOutput).Value = Sheets("namedFP").Range("A" & iRowNumber).Value
iNextRowOfOutput = iNextRowOfOutput + 1
Next iRowNumber
'- not sure which of these you want to save (one or both)
Sheets("namedFP").Save
Sheets("Log").Save
End Sub
Just paste the function above or below the Subroutine and let me know if you have any issues or questions regarding the 'Move2RowsToEnd' code.

Copy/paste a row from one worksheet to another produces type mismatch error

This macro is to move records from a master sheet to other sheets based on criteria from column F.
A type mismatch error occurs in the "Termination" case where it is selecting the cell "B2".
I tried several different options, but each ends up with a different error.
Public Sub moveToSheet()
Sheets("Master").Select
' Find the last row of data
FinalRow = Range("E65000").End(xlUp).Row
'Loop through each row
For x = 2 To FinalRow
' Decide where to copy based on column F
ThisValue = Range("F" & x).Value
Select Case True
Case ThisValue = "Hiring "
Sheets("Master").Cells(x, 2).EntireRow.Copy
Sheets("Hiring").Select
Sheets("Hiring").Range("B2:W2500").Clear
Sheets("Hiring").Cells("B2").Select
ActiveSheet.Paste
Sheets("Master").Select
Case ThisValue = "Re-Hiring "
Sheets("Master").Cells(x, 2).EntireRow.Copy
Sheets("Hiring").Select
Sheets("Hiring").Range("B2:W2500").Clear
Sheets("Hiring").Cells("B2").Select
ActiveSheet.Paste
Case ThisValue = "Termination "
Sheets("Master").Cells(x, 2).EntireRow.Copy
Sheets("Terminations").Select
Sheets("Terminations").Range("B2:W2500").Clear
Sheets("Terminations").Cells("B2").Select
ActiveSheet.Paste
Case ThisValue = "Transfer "
Sheets("Master").Cells(x, 2).EntireRow.Copy
Sheets("Transfers").Select
Sheets("Transfers").Range("B2:W2500").Clear
Sheets("Transfers").Cells("B2").Select
ActiveSheet.Paste
Case ThisValue = "Name Change "
Sheets("Master").Cells(x, 2).EntireRow.Copy
Sheets("Name Changes").Select
Sheets("Name Changes").Range("B2:W2500").Clear
Sheets("Name Changes").Cells("B2").Select
ActiveSheet.Paste
Case ThisValue = "Address Change "
Sheets("Master").Cells(x, 2).EntireRow.Copy
Sheets("Address Changes").Select
Sheets("Address Changes").Range("B2:W2500").Clear
Sheets("Address Changes").Cells("B2").Select
ActiveSheet.Paste
Case Else
Sheets("Master").Cells(x, 2).EntireRow.Copy
Sheets("New Process").Select
Sheets("New Process").Range("B2:W2500").Clear
Sheets("New Process").Cells("B2").Select
ActiveSheet.Paste
End Select
Next x
End Sub
There are a couple problems, first, you need to use the syntax Range("B2").Select to select the cell. BUT, since you selected the entire row from the master sheet, you can't copy the entire row into B2, because the ranges aren't the same size, so you need to select the first cell (A2) instead.
So, the entire case statement should look like this:
Case ThisValue = "Termination "
Sheets("Master").Cells(x, 2).EntireRow.Copy
Sheets("Terminations").Activate
Range("A2").Select
ActiveSheet.Paste
There are a number of issues
No need to Select, use variables instead
Dim all your variables - help with debugging and learning
Some general good practice techniques will help
Here's a (partially) refactored version of your code
Public Sub moveToSheet()
Dim wb As Workbook
Dim shMaster As Worksheet, shHiring As Worksheet
Dim rngMaster As Range
Dim x As Long
Dim rw As Range
Set wb = ActiveWorkbook
Set shMaster = wb.Worksheets("Master")
Set shHiring = wb.Worksheets("Hiring")
' etc
' Find the data
x = shMaster.UsedRange.Count ' trick to reset used range
Set rngMaster = shMaster.UsedRange
'Loop through each row NOTE looping thru cells is SLOW. There are faster ways
For Each rw In rngMaster.Rows
' Decide where to copy based on column F
Select Case Trim$(rw.Cells(1, 6).Value) ' Is there really a space on the end?
Case "Hiring"
shHiring.[B2:W2500].Clear
rw.Copy shHiring.[B2]
' Case ' etc
End Select
Next rw
This is what I basically use to do exactly what you are talking about. I have a "master" sheet that is several thousand rows and a couple hundred columns. This basic version only searches in Column Y and then copies rows. Because other people use this, though, I have several template worksheets that I keep very hidden so you can edit that out if you don't want to use templates. I also can add additional search variables if needed and simply adding in another couple of lines is easy enough. So if you wanted to copy rows that match two variables then you'd define another variable Dim d as Range and Set d = shtMaster.Range("A1") or whatever column you wanted to search the second variable. Then on the If line change it to If c.Value = "XXX" and d.Value = "YYY" Then . Finally make sure you add an offset for the new variable with the c.offset (so it would have a line Set d = d.Offset(1,0) at the bottom with the other). It really has turned out to be pretty flexible for me.
Sub CreateDeptReport(Extras As String)
Dim shtRpt As Excel.Worksheet, shtMaster As Excel.Worksheet
Dim LCopyToRow As Long
Dim LCopyToCol As Long
Dim arrColsToCopy
Dim c As Range, x As Integer
Application.DisplayAlerts = False
Application.ScreenUpdating = False
On Error GoTo Err_Execute
arrColsToCopy = Array(1, 3, 4, 8, 25, 25, 21, 16, 17, 15, 31, 7) 'which columns to copy ?
Set shtMaster = ThisWorkbook.Sheets("MasterSheet")
Set c = shtMaster.Range("Y5") 'Start search in Column Y, Row 5
LCopyToRow = 10 'Start copying data to row 10 in Destination Sheet
While Len(c.Value) > 0
'If value in column Y equals defined value, copy to destination sheet
If c.Value = “XXX” Then
'only create the new sheet if any records are found
If shtRpt Is Nothing Then
'delete any existing sheet
On Error Resume Next
ThisWorkbook.Sheets("Destination").Delete
On Error GoTo 0
ThisWorkbook.Sheets("Template").Visible = xlSheetVisible
ThisWorkbook.Sheets("Template").Copy After:=shtMaster
Set shtRpt = ThisWorkbook.Sheets(shtMaster.Index + 1)
shtRpt.Name = "Destination" 'rename new sheet to Destination
‘Optional Information; can edit the next three lines out -
Range("F1").Value = "Department Name"
Range("F2").Value = "Department Head Name"
Range("B3").Value = Date
ThisWorkbook.Sheets("Template").Visible = xlSheetVeryHidden
End If
LCopyToCol = 1
shtRpt.Cells(LCopyToRow, LCopyToCol).EntireRow.Insert shift:=xlDown
For x = LBound(arrColsToCopy) To UBound(arrColsToCopy)
shtRpt.Cells(LCopyToRow, LCopyToCol).Value = _
c.EntireRow.Cells(arrColsToCopy(x)).Value
LCopyToCol = LCopyToCol + 1
Next x
LCopyToRow = LCopyToRow + 1 'next row
End If
Set c = c.Offset(1, 0)
Wend
Application.DisplayAlerts = True
Application.ScreenUpdating = True
Range("A9").Select 'Position on cell A9
MsgBox "All matching data has been copied."
Exit Sub
Err_Execute:
MsgBox "An error occurred."
End Sub
Also, if you wanted then you could remove the screenupdating lines. As stupid as it sounds some people actually like to see excel working at it. With screenupdating off you don't get to see the destination sheet until the copying is completed, but with updating on the screen flickers like crazy because of it trying to refresh when each row is copied. Some of the older people in my office think that excel is broken when they can't see it happening so I keep screenupdating on most of the time. lol
Also, I like having the templates because all of my reports have quite a few formulas that need to be calculated after the information is broken down so I am able to keep all the formulas where I want them with a template. Then all I have to do is run the macro to pull from the master sheet and the report is ready to go without any further work.

Resources