How to save to the next available row instead of replacing it - vba

I had tried to use VBA to help grab values from a Excel form into a another workbook (Which is blank inside for now) here is the VBA code I'm using:
Sub RunMe()
Dim lRow, lCol As Integer
Sheets("Sheet1").Select
lRow = Range("A" & Rows.Count).End(xlUp).Row
lCol = Cells(1, Columns.Count).End(xlToLeft).Column
For Each cell In Range(Cells(1, "B"), Cells(1, lCol))
Union(Range("A1:A" & lRow), Range(Cells(1, cell.Column), Cells(lRow, cell.Column))).Copy
Workbooks.Add
Range("A1").PasteSpecial
ActiveWorkbook.SaveAs Filename:= _
"C:\Users\john\Desktop\Testforvba" & cell.Value & ".xls"
ActiveWorkbook.Close
Next cell
Application.CutCopyMode = False
End Sub
But the thing is that when I run this code after inputting a sample value on the form and manually run the macro, it works as it creates a new file and store the sample data I had type on the Form workbook.
But once I tried running the macro again, I realised that it recreates the file AGAIN which means it replace all the previous data. VBA is so dang hard lol. Anyone please give a hand thanks.
P.S The macro I stored in my personal Workbook

Try this one:
Option Explicit
Sub RunMe()
Application.ScreenUpdating = False
Dim lRow As Long, lCol As Long
Dim wb As Workbook, wbNew as Workbook
Dim ws As Worksheet, wsNew as Worksheet
Set wb = ThisWorkbook
Set ws = wb.Sheets("Sheet1")
lRow = ws.Range("A" & Rows.Count).End(xlUp).Row
lCol = ws.Cells(1, Columns.Count).End(xlToLeft).Column
Dim cell As Variant
For Each cell In ws.Range(ws.Cells(1, "B"), ws.Cells(1, lCol))
Union(ws.Range("A1:A" & lRow), ws.Range(ws.Cells(1, cell.Column), ws.Cells(lRow, cell.Column))).Copy
Set wbNew = Workbooks.Add
Set wsNew = wbNew.Sheets("Sheet1")
Dim yourdesktopaddress As String
yourdesktopaddress = CreateObject("WScript.Shell").specialfolders("Desktop") & "\Testforvba"
wsNew.Range("A1").PasteSpecial
If Not Dir(yourdesktopaddress & "\" & cell.Value & ".xls", vbDirectory) = vbNullString Then
'MsgBox "exists"
Application.DisplayAlerts = False
Else
Application.DisplayAlerts = False
ActiveWorkbook.SaveAs Filename:= _
yourdesktopaddress & "\" & cell.Value & ".xls"
End If
wbNew.Close
Application.DisplayAlerts = True
Next cell
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
The main issue was, whenever macro runs, Excel was creating the files from zero and this was causing Excel to recreate each file again. I have added that If Not Dir(yourdesktopaddress & "\Testforvba" & cell.Value & ".xls", vbDirectory) = vbNullString part of my code to check whether did this file already created or not, if not it's creating the file now. If you delete the "'" from the beginning of 'MsgBox "exists" it will always tell you that this file is already exist.
Also In the case of variables are not defined well, excel might start to overwrite itself and it may replace the previous data. (Especially Range("A1).PasteSpecial and Activeworkbook.close kind of critical points are most important ones) So I also edited whole code,defined all variables, defined the user's desktop address to ensure code works on every user.

Related

Efficiently import multiple spreadsheets into one master sheet using Excel VBA

I am new to VBA and I have some code which I have written and although it works I think it is bulky and not very good should a change ever need to be made to it.
The code opens a spreadsheet, runs a Function (Called "LastRow") to copy the data and another (Called "NxtRow")to paste it into the next empty row of the spreadsheet with the macro then closes the sheet that the data was copied from and moves on to the next one. Basically it is concatenating multiple sheets into one.
I am thinking that there must be a way to write the code to call the functions once and then loop through each sheet in a list. Is this possible?
My code is:
NxtRow() Function
Public Function NxtRow()
Dim BlankRow As Long
Windows("GA_BudgetTool_MASTER.xlsm").Activate
BlankRow = Range("A" & Rows.Count).End(xlUp).Row + 1
Cells(BlankRow, 1).Select
ActiveSheet.Paste
BlankRow = Range("A" & Rows.Count).End(xlUp).Row + 1
Cells(BlankRow, 1).Select
End Function
LstRow() Function
Public Function LastRow()
Dim LstRow As Long, LstCol As Long, Rng As Range, A3 As Range
LstRow = Range("A" & Rows.Count).End(xlUp).Row
LstCol = Range("O" & LstRow).Column
Set Rng = Range(Cells(LstRow, 1), Cells(LstRow, LstCol))
Set A3 = Range("A3")
Range(A3, Rng).Select
Selection.Copy
End Function
VBA Sub()
Sub ImpData()
' Deactivate Screen Updating and Display Alerts
Application.ScreenUpdating = False
Application.DisplayAlerts = False
' Import Worksheet 1
Workbooks.Open Filename:= _
"Worksheet1_Filename.xlsx"
LastRow
NxtRow
Windows("Worksheet1.xlsx").Activate
ActiveWindow.Close
' Import Worksheet 2
Workbooks.Open Filename:= _
"Worksheet2_Filename.xlsx"
LastRow
NxtRow
Windows("Worksheet2.xlsx").Activate
ActiveWindow.Close
' Import Worksheet 3
Workbooks.Open Filename:= _
"Worksheet3_Filename.xlsx"
LastRow
NxtRow
Windows("Worksheet3.xlsx").Activate
ActiveWindow.Close
This goes on in this fashion for about 30 sheets. Is there an easier way to write this and make it easier to amend later if needed?
I would just make a little array of your filenames and then use a for loop to repeat the function calls as many times as necessary
Sub ImpData()
'Deactivate Screen Updating and Display Alerts
With Application
.ScreenUpdating = False
.DisplayAlerts = False
End With
Dim filenames As Variant
filenames = Array("file1", "file2")
For i = 1 To UBound(filenames) + 1
Workbooks.Open Filename:=filenames(i - 1)
LastRow
NxtRow
Windows("Worksheet" & i & ".xlsx").Activate
ActiveWindow.Close
Next i
'Reactivate Screen Updating and Display Alerts
With Application
.ScreenUpdating = True
.DisplayAlerts = True
End With
End Sub

Extract a particular column from another workbook and copy to the destination workbook

I have two workbooks, source workbook and a Destination workbook.
I would like to copy particular column (A,C;D;E;F;G;K;AP;AV;AW;AX)containing data from source workbook to the Destination workbook in column (A till E). I already searched through the page and could find a code like below
Sub Extract()
Dim x As Workbook
Dim y As Workbook
Dim Value As Variant
Set y = ThisWorkbook
Set x = Workbooks.Open("D:\data\Jenny_Work.xlsx")
Value = x.Sheets("Work_Jenny").Range("A1").Value
y.Sheets("Sheet1").Range("A1").Value = Value
x.Close
End Sub
It would be great if you can suggest me for the above criteria
Thanking in advance
try this
it is working for me!
Sub Extract()
Dim x As Workbook
Dim y As Workbook
Dim Value As Variant
Set y = ThisWorkbook
Set x = Workbooks.Open("G:\Book1.xlsm")
lRow = x.Worksheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
dRow = y.Worksheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
Union(y.Worksheets("Sheet1").Range("A1:A" & dRow), y.Worksheets("Sheet1").Range("B1:B" & dRow)).Copy
x.Worksheets("Sheet1").Range("A" & lRow).PasteSpecial xlPasteAll
Application.CutCopyMode = False
x.Save
End Sub
Please note that a sheet is not the same as a workbook as this will create confusion.
A workbook/excel file can contain multiple sheets/worksheets and not the other way around.
That being said here is your code:
Sub RunMe()
Dim lRow, lCol As Integer
Sheets("Master").Select
lRow = Range("A" & Rows.Count).End(xlUp).Row
lCol = Cells(1, Columns.Count).End(xlToLeft).Column
For Each cell In Range(Cells(1, "B"), Cells(1, lCol))
Union(Range("A1:A" & lRow), Range(Cells(1, cell.Column), Cells(lRow, cell.Column))).Copy
Workbooks.Add
Range("A1").PasteSpecial
ActiveWorkbook.SaveAs Filename:= _
"C:\YourMap\" & cell.Value & ".xls" 'You might want to change the extension (.xls) according to your excel version
ActiveWorkbook.Close
Next cell
Application.CutCopyMode = False
End Sub
Just watch the file extension within the code (look for green text after pasting.).

How to copy columns within same worksheet Excel VBA

I have a program that needs to copy select columns within the same workbook and worksheet.
The current code results in Excel crashing, so I'm not sure if it is working or not.
Is there a better way to copy the columns within the same worksheets with the same workbook?
Code:
Sub Macro1()
Dim wb1 As Workbook
'Set it to be the file location, name, and file extension of the Working File
Set wb1 = Workbooks.Open("Z:\XXX\Working File.xlsx")
MsgBox "Copying Fields within Working File"
wb1.Worksheets(1).Columns("G").Copy wb1.Worksheets(1).Columns("H").Value
wb1.Worksheets(1).Columns("J").Copy wb1.Worksheets(1).Columns("O").Value
wb1.Worksheets(1).Columns("K").Copy wb1.Worksheets(1).Columns("N").Value
wb1.Worksheets(1).Columns("M").Copy wb1.Worksheets(1).Columns("P").Value
wb1.Close SaveChanges:=True
End Sub
Try this, it sets two ranges' values equal, which will keep the data, but no formatting. It should be quicker.
Sub Macro1()
Dim wb1 As Workbook
'Set it to be the file location, name, and file extension of the Working File
Set wb1 = Workbooks.Open("Z:\XXX\Working File.xlsx")
MsgBox "Copying Fields within Working File"
With wb1.Worksheets(1)
.Columns("H").Value = .Columns("G").Value
.Columns("O").Value = .Columns("J").Value
.Columns("N").Value = .Columns("K").Value
.Columns("P").Value = .Columns("M").Value
End With
wb1.Close SaveChanges:=True
End Sub
Note you're using a whole column, so it might hang up or take a little longer. If you want, you can instead just get the last Row of each column and use that to shorten the ranges being copied.
Edit: As mentioned above, you may be better off using a smaller range. This is a little more verbose, but you should be able to follow what it's doing:
Sub Macro1()
Dim wb1 As Workbook
Dim lastRow As Long
'Set it to be the file location, name, and file extension of the Working File
Set wb1 = ActiveWorkbook
MsgBox "Copying Fields within Working File"
With wb1.Worksheets(1)
lastRow = .Cells(.Rows.Count, "G").End(xlUp).Row
.Range("H1:H" & lastRow).Value = .Range("G1:G" & lastRow).Value
lastRow = .Cells(.Rows.Count, "J").End(xlUp).Row
.Range("O1:O" & lastRow).Value = .Range("J1:J" & lastRow).Value
lastRow = .Cells(.Rows.Count, "K").End(xlUp).Row
.Range("N1:N" & lastRow).Value = .Range("K1:K" & lastRow).Value
lastRow = .Cells(.Rows.Count, "M").End(xlUp).Row
.Range("P1:P" & lastRow).Value = .Range("M1:M" & lastRow).Value
End With
wb1.Close SaveChanges:=True
End Sub

Trying to combine a VBA copy/paste loop with a VBA 'Open File' Dialog Box

I have an Excel macro that copies and pastes line items from a source file to an Excel-based form. It opens a form template and saves each line item as it's own file then loops through the remaining rows. Right now I have a file path built into the code that refers to the form template needed, but I need the user to be able to choose which file they want to use as their template. I have code for both of these processes but I haven't been able to combine them. My example below results in a Compile Error: Variable not defined.
Here's what I have so far:
Option Explicit
Sub CopyToForm()
Dim wbSource As Workbook, wbForm As Workbook
Dim wsSource As Worksheet, wsForm As Worksheet
Dim formpath As String, foldertosavepath As String
Dim lrow As Long, i As Integer
Set wbSource = ThisWorkbook '~~> Write your code in Indication Tool.xls
Set wsSource = wbSource.Sheets("Indication Tool") '~~> Put the source sheet name
'~~> This opens the Processing Form template.
formpath = "C:\File path.xls"
'~~> Prompts user with Open File Dialog Box
strCancel = "N"
strWorkbookNameAndPath = Application.GetOpenFilename _
(FileFilter:=strFilt, _
FilterIndex:=intFilterIndex, _
Title:=strDialogueFileTitle)
'~~> Exits If No File Selected
If strWorkbookNameAndPath = "" Then
MsgBox ("No Filename Selected")
strCancel = "Y"
Exit Sub
ElseIf strWorkbookNameAndPath = "False" Then
MsgBox ("You Clicked The Cancel Button")
strCancel = "Y"
Exit Sub
End If
Workbooks.Open strWorkbookNameAndPath
'~~> This declares path where the Individual forms will be saved.
foldertosavepath = "C:\File path\Forms\"
With wsSource
'~~> Counts how many rows are in the Indication Tool
lrow = .Range("B" & .Rows.Count).End(xlUp).Row
If lrow < 18 Then MsgBox "No data for transfer": Exit Sub
For i = 18 To lrow
Set wbForm = Workbooks.Open(formpath) '~~> open the form
Set wsForm = wbForm.Sheets("Processing Form") '~~> Declare which worksheet to activate
'~~> Proceed with the copying
.Range("B" & i).Copy wsForm.Range("F7:K7")
.Range("C" & i).Copy wsForm.Range("D8")
.Range("C" & i).Copy wsForm.Range("D30")
.Range("D" & i).Copy wsForm.Range("H29")
.Range("E" & i).Copy wsForm.Range("E29")
.Range("F" & i).Copy wsForm.Range("D33")
.Range("G" & i).Copy wsForm.Range("J30:K30")
.Range("H" & i).Copy wsForm.Range("P33")
.Range("I" & i).Copy wsForm.Range("L33:N33")
.Range("L" & i).Copy wsForm.Range("H32")
.Range("R" & i).Copy wsForm.Range("D87")
.Range("C2:F2").Copy wsForm.Range("J101:M101")
.Range("C3:M3").Copy wsForm.Range("E102:O102")
'~~> Save the form using the client name
wbForm.SaveAs foldertosavepath & .Range("B" & i).Value & ".xls"
'~~> These steps are for formatting, as I haven't figured out how paste values only
Set wbForm = Workbooks.Open(formpath)
Cells.Select
Selection.Copy
wsForm.Activate
'~~> This allows the format to be pasted into the updated Form
wsForm.Unprotect
Cells.Select
Selection.PasteSpecial Paste:=xlPasteFormats
wsForm.Protect
ActiveWorkbook.Save
ActiveWorkbook.Close
wbForm.Close ([SaveChanges:=False])
Set wbForm = Nothing
Set wsForm = Nothing
Next
End With
End Sub
When I debug the error, Sub CopyToForm() is highlighted in yellow and strCancel = is selected. Is there a way to set the user-chosen file as the formpath? Thanks in advance for your help, this has been a thorn in my side for quite some time.
Option Explicit is declared, and strCancel isn't declared as a variable.
Add Dim strCancel As String to your code

Need help merging two macros for Excel VBA

Both of these macros are macros I found online and adapted to my use. I am using this code and it works well to separate specific data into new sheets:
Sub Copy_To_Worksheets()
'Note: This macro use the function LastRow
Dim My_Range As Range
Dim FieldNum As Long
Dim CalcMode As Long
Dim ViewMode As Long
Dim ws2 As Worksheet
Dim Lrow As Long
Dim cell As Range
Dim CCount As Long
Dim WSNew As Worksheet
Dim ErrNum As Long
'Set filter range on ActiveSheet: A1 is the top left cell of your filter range
'and the header of the first column, D is the last column in the filter range.
'You can also add the sheet name to the code like this :
'Worksheets("Sheet1").Range("A1:D" & LastRow(Worksheets("Sheet1")))
'No need that the sheet is active then when you run the macro when you use this.
Set My_Range = Range("A1:Z" & Range("A" & Rows.Count).End(xlUp).Row)
My_Range.Parent.Select
If ActiveWorkbook.ProtectStructure = True Or _
My_Range.Parent.ProtectContents = True Then
MsgBox "Sorry, not working when the workbook or worksheet is protected", _
vbOKOnly, "Copy to new worksheet"
Exit Sub
End If
'This example filters on the first column in the range(change the field if needed)
'In this case the range starts in A so Field:=1 is column A, 2 = column B, ......
FieldNum = 5 'I changed this to 3 for column C
'Turn off AutoFilter
My_Range.Parent.AutoFilterMode = False
'Change ScreenUpdating, Calculation, EnableEvents, ....
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
ViewMode = ActiveWindow.View
ActiveWindow.View = xlNormalView
ActiveSheet.DisplayPageBreaks = False
'Add a worksheet to copy the a unique list and add the CriteriaRange
Set ws2 = Worksheets.Add
With ws2
'first we copy the Unique data from the filter field to ws2
My_Range.Columns(FieldNum).AdvancedFilter _
Action:=xlFilterCopy, _
CopyToRange:=.Range("A1"), Unique:=True
'loop through the unique list in ws2 and filter/copy to a new sheet
Lrow = .Cells(Rows.Count, "A").End(xlUp).Row
For Each cell In .Range("A2:A" & Lrow)
'Filter the range
My_Range.AutoFilter Field:=FieldNum, Criteria1:="=" & _
Replace(Replace(Replace(cell.Value, "~", "~~"), "*", "~*"), "?", "~?")
'Check if there are no more then 8192 areas(limit of areas)
CCount = 0
On Error Resume Next
CCount = My_Range.Columns(1).SpecialCells(xlCellTypeVisible) _
.Areas(1).Cells.Count
On Error GoTo 0
If CCount = 0 Then
MsgBox "There are more than 8192 areas for the value : " & cell.Value _
& vbNewLine & "It is not possible to copy the visible data." _
& vbNewLine & "Tip: Sort your data before you use this macro.", _
vbOKOnly, "Split in worksheets"
Else
'Add a new worksheet
Set WSNew = Worksheets.Add(After:=Sheets(Sheets.Count))
On Error Resume Next
WSNew.Name = cell.Value
If Err.Number > 0 Then
ErrNum = ErrNum + 1
WSNew.Name = "Error_" & Format(ErrNum, "0000")
Err.Clear
End If
On Error GoTo 0
'Copy the visible data to the new worksheet
My_Range.SpecialCells(xlCellTypeVisible).Copy
With WSNew.Range("A1")
' Paste:=8 will copy the columnwidth in Excel 2000 and higher
' Remove this line if you use Excel 97
.PasteSpecial Paste:=8
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
.Select
End With
End If
'Show all data in the range
My_Range.AutoFilter Field:=FieldNum
Next cell
'Delete the ws2 sheet
On Error Resume Next
Application.DisplayAlerts = False
.Delete
Application.DisplayAlerts = True
On Error GoTo 0
End With
'Turn off AutoFilter
My_Range.Parent.AutoFilterMode = False
If ErrNum > 0 Then
MsgBox "Rename every WorkSheet name that start with ""Error_"" manually" _
& vbNewLine & "There are characters in the name that are not allowed" _
& vbNewLine & "in a sheet name or the worksheet already exist."
End If
'Restore ScreenUpdating, Calculation, EnableEvents, ....
My_Range.Parent.Select
ActiveWindow.View = ViewMode
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
What I need help with is to add a particular set of formulas to the bottom of each sheet that is created from the above macro. The following macro adds the formulas to all the sheets in the workbook. I need it to add the formulas to only the sheets that are created in the above macro. The number of sheets created change every time they are generated, depending on the source data. I was thinking it might be best to merge bottom macro into the top but I have no idea how to go about doing that.
Sub Insert_Formulas()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
ws.Activate
'Start
NxtRw = Cells(Rows.Count, "B").End(xlUp).Row + 1
With Cells(NxtRw, "B")
.Value = "Total Open Cases"
End With
With Cells(NxtRw, "C")
.EntireRow.Insert
.Value = "Total Closed Cases"
End With
'Next Row below
NxtRw = Cells(Rows.Count, "B").End(xlUp).Row + 1
With Cells(NxtRw, "B")
.Formula = "=CountIf(B2:B" & NxtRw - 1 & ", ""Open*"")"
End With
With Cells(NxtRw, "C")
.Formula = "=CountIf(B2:B" & NxtRw - 1 & ", ""Closed*"")"
End With
Next
End Sub
Any help would be greatly appreciated.
Thank you, Ck
I wouldn't combine the 2 macros, simply call the Insert_Formulas macro from the Copy_To_Worksheets macro when it is needed.
To call the macro all you need is this line:
Insert_Formulas
Edit to respond to comment:
Given you don't know how many sheets are being added I have one suggestion you may try.
High level, add text to a cell in each sheet to indicate if it is new or not. When new sheet is created cell should say "new". When not new it should say "existing". Then in the
If you want to give this a try, and let me know what doesn't work I can check back and help update the code.
In the Copy_To_Worksheets macro you'll need to add a line to set all existing sheets to "existing"
In then Copy_To_Worksheets macro add a line so that new sheets get set to "new"
In Insert_Formulas macro, still loop through all sheets, but check to see if the sheet is "new", and if so, run the code to add the formulas.
A cleaner (but slightly more difficult) option would be to define a name on each sheet (use the same name for each and limit the scope to each individual sheet) and use that instead of a cell on each sheet.
If you parametrize the function so that it takes the worksheet which needs the formulas as a parameter
Sub Insert_Formulas_Into_WorkSheet(ws As Worksheet)
ws.Activate
'Start
NxtRw = Cells(Rows.Count, "B").End(xlUp).Row + 1
With Cells(NxtRw, "B")
.Value = "Total Open Cases"
End With
With Cells(NxtRw, "C")
.EntireRow.Insert
.Value = "Total Closed Cases"
End With
'Next Row below
NxtRw = Cells(Rows.Count, "B").End(xlUp).Row + 1
With Cells(NxtRw, "B")
.Formula = "=CountIf(B2:B" & NxtRw - 1 & ", ""Open*"")"
End With
With Cells(NxtRw, "C")
.Formula = "=CountIf(B2:B" & NxtRw - 1 & ", ""Closed*"")"
End With
End Sub
Then you can add the formulas after each new worksheet is created by calling
Insert_Formulas_Into_WorkSheet WSNew