VBA Excel - Copy Rows to Another Workbook Sheet with conditions - vba

Newbie trying to mix and match codes on an excel workbook that is configured to prompt a login and to allow diff Id and PW to see different sheets.
If Me.userid.Value = "admin" And Me.userpw.Value = "admin" Then
MsgBox "Login Successful!", vbInformation, "Login Alert"
MsgBox "Entry and use data with caution and exercise confidentiality at all times!", vbExclamation, "Critical Reminder"
Unload Me
Sheets("Summary Report View").Visible = True
Sheets("Summary Report View").Select
Sheets("Data Validation").Visible = True
Sheets("Data Entry 1").Visible = True
Sheets("Data Entry 2").Visible = True
Sheets("Data Entry 3").Visible = True
I have this challenge not being able to copy data from other workbook (a particular worksheet called 6-9months) to this workbook that I'm working on into Data Entry 1. The condition is to pick up all rows with the name "John" in Column I and paste to my active workbook sheet named "data entry 1". I attempted to activate the codes through a button click to pick up all the rows but it doesn't seem to work.
Confirmation = MsgBox("Are you sure to removal all contents? This is not reversible", vbYesNo, "Confirmation")
Select Case Confirmation
Case Is = vbYes
Sheets("Data Entry 2").Cells.ClearContents
MsgBox "Information removed", vbInformation, "Information"
Dim GCell As Range
Dim Txt$, MyPath$, MyWB$, MySheet$
Dim myValue As String
Dim P As Integer, Q As Integer
Txt = "John"
MyPath = "C:\Users\gary.tham\Desktop\"
MyWB = "Book1.xlsx"
'MySheet = ActiveSheet.Name
Application.ScreenUpdating = False
Workbooks.Open Filename:=MyPath & MyWB
lastrow = ActiveSheet.Range("A" & Rows.Count).End(x1Up).Row
For i = 2 To lastrow
If Cells(i, 11) = txt Then
Range(Cells(i, 1), Cells(i, 13)).Select
Selection.Copy
P = Worksheets.Count
For Q = 1 To P
If ThisWorkbook.Worksheets(Q).Name = "Data Entry 2" Then
Worksheets("Data Entry 2").Select
ThisWorkbook.Worksheets(Q).Paste
End If
Next Q
End If
Next i
Case Is = vbNo
MsgBox "No Changes Made", vbInformation, "Information"
End Select

The essential problem with your code is that you are working with multiple Excel files at the same time (1) the file you are opening and searching for "John" and the (2) current file from which the macro is being called and to which we are importing the data. Yet, your code does not reference the two files but merely states to search for "john" in the ActiveSheet. Furthermore, you are not telling VBA in which of the two files you want to search the currently active sheet.
So, if you are working with multiple files then you should specifically address everything and don't ask VBA to make assumptions which file or which sheet or which cell on which sheet in which file you mean. Confused? If VBA would be a person then he/she would probably also be confused. Yet, VBA just makes assumptions and you are left to wonder why the code doesn't do what you expect it to do. Hence, when working with multiple files you should use the following explicit (!) references and tell VBA exactly what you want:
Workbooks("Book1.xlsx").Worksheets("Sheet1").Cells(1, 1).Value2
or
Workbooks("Book1.xlsx").Worksheets("Sheet1").Range("A1").Value2
Having said that, I changed your code to make use of the above.
Option Explicit
Sub CopyDataFromAnotherFileIfSearchTextIsFound()
Dim strPath As String
Dim wbkImportFile As Workbook
Dim shtThisSheet As Worksheet
Dim shtImportSheet As Worksheet
Dim lngrow As Long
Dim strSearchString As String
Dim strImportFile As String
'uPPer or lOwEr cases do not matter (as it is currently setup)
strSearchString = "jOHn"
strImportFile = "Book1.xlsx"
Set shtThisSheet = ThisWorkbook.Worksheets("Data Entry 2")
'If the import file is in the same folder as the current file
' then you could also use the following instead
'strPath = ThisWorkbook.Path
strPath = "C:\tmp" '"C:Users\gary.tham\Desktop"
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
Set wbkImportFile = Workbooks.Open(Filename:=strPath & "\" & strImportFile)
'To speed up things you could also (if acceptable) open the file
' read-only without updating links to other Excel files (if there are any):
'Set wbkImportFile = Workbooks.Open(Filename:=strPath & "\" & strImportFile, ReadOnly:=True, UpdateLinks:=False)
Set shtImportSheet = wbkImportFile.Worksheets("6-9months")
shtThisSheet.Cells.ClearContents
For lngrow = 2 To shtImportSheet.Cells(shtImportSheet.Rows.Count, "I").End(xlUp).Row
If InStr(1, shtImportSheet.Cells(lngrow, "I").Value2, strSearchString, vbTextCompare) > 0 Then
shtImportSheet.Range(shtImportSheet.Cells(lngrow, 1), shtImportSheet.Cells(lngrow, 13)).Copy
shtThisSheet.Range("A" & shtThisSheet.Cells(shtThisSheet.Rows.Count, "A").End(xlUp).Row + 1).PasteSpecial xlPasteAll, xlPasteSpecialOperationNone
End If
Next lngrow
wbkImportFile.Close SaveChanges:=False
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
.EnableEvents = True
End With
End Sub
Note, that the above code is not an exact copy of yours. There are two changes:
(1) The sheet "Data Entry 2" in the current file (the file you are importing to) will be cleared without asking the user.
(2) The sheet "Data Entry 2" is directly referenced without the above check: if there actually is a sheet by that name in the current file.
So, don't forget to make the proper adjustments to suit your needs.
Do let me know if this solution works for you or if you have any more questions.

Related

Code to merge data in multiple documents by column

Is there a way to merge the data in multiple excel spreadsheets together by column?
I have 200 spreadsheets, each with text in the first 100 columns (A-CV).
I would like to merge all the "A" columns from these 200 documents together, all the "B" columns together, all the "C" columns together, and so on.
As for the merging, no particular order is required. As long as the cells themselves don't get merged.
Due to the large amount of text the code would be merging, it would be more practical to be able to merge one column at a time across all spreadsheets into a unique file, then repeat that with all other columns (A-CV), instead of attempting to merge all the columns (from all spreadsheets) together into one single file.
I found a code that merges columns, but it's not quite what I need. Is there a way to modify this code to help with what I described above?
Sub Macro1()
'
' Macro1 Macro
'
Dim cell As Range
For i = 1 To 50
Sheets("Sheet1").Select
If Cells(1, i).Value = "Cat 2" Then
Columns(i).Select
Selection.Copy
Sheets("Sheet2").Select
Range("A1").Select
ActiveSheet.Paste
End If
If Cells(1, i).Value = "Cat 6" Then
Columns(i).Select
Selection.Copy
Sheets("Sheet2").Select
Range("B1").Select
ActiveSheet.Paste
End If
If Cells(1, i).Value = "Cat 4" Then
Columns(i).Select
Selection.Copy
Sheets("Sheet2").Select
Range("C1").Select
ActiveSheet.Paste
End If
Next i
End Sub
If you need more information, please let me know. And if I need to rename the documents a certain way to help with the process, I'm definitely willing to do that.
The merged data can be sent to a spreadsheet, word document, or notepad. I'm fine with any of these options.
UPDATE: This is the new code with modifications. The issues I am having are in the comment below.
Sub copydocument()
Dim wb As Workbook
Dim wb1 As Workbook
Application.ScreenUpdating = False
Application.DisplayAlerts = False
On Error GoTo resetsettings
Set wb = ThisWorkbook
MyPath = "C:\Users\HNR\Desktop\A\" 'Path of folder with \ at the end
MyExtension = "*.xlsx"
Myfile = Dir(MyPath & MyExtension)
While Myfile <> vbNullString
Set wb1 = Workbooks.Open(MyPath & Myfile)
lr = wb.Sheets(1).Range("A" & Rows.Count).End(xlUp).Row
lr1 = wb1.Sheets(1).Range("A" & Rows.Count).End(xlUp).Row
wb1.Sheets(1).Range("A" & lr1).Copy Destination:=wb.Sheets(1).Range("A" & (lr + 1))
wb1.Close
Myfile = Dir
Wend
resetsettings:
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
While there are many ways to do what you want, I would recommend looking into Power Query. It gives you a great GUI to work with to accomplish this. Depending on your version of excel it is either a free add-on or part of the shipped product(for new versions of office).
You do not need to know how to code to use this, you just need to understand the concepts.
While its not exactly the answer you are after i have successfully taught several people at my work place how to use this application that would have previously been reliant on me or someone else with VBA skills.
Sub copydocument()
Dim wb As Workbook
Dim wb1 As Workbook
Application.ScreenUpdating = False
Application.DisplayAlerts = False
On Error GoTo resetsettings
Set wb = ThisWorkbook
MyPath = "c:\Users\foo\" 'Path of folder with \ at the end
MyExtension = "*.xlsx"
Myfile = Dir(MyPath & MyExtension)
While Myfile <> vbNullString
Set wb1 = Workbooks.Open(MyPath & Myfile)
lr = wb.Sheets(1).Range("A1:A" & Rows.Count).End(xlUp).Row
lr1 = wb1.Sheets(1).Range("A1:A" & Rows.Count).End(xlUp).Row
wb1.Sheets(1).Range("A1:CV" & lr1).Copy Destination:=wb.Sheets(1).Range("A" & (lr + 1))
wb1.close
Myfile = Dir
Wend
resetsettings:
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
This macro will go through all the files in the folder and copy the sheet1 range and paste it in the active workbook sheet1. if you have headers and dont want them to repeat you can copy the header to the sheet1 of activeworkbook then copy range from (A2:CV &lr1).

runtime error 9 - subscript out of range when copying a seet to a new wrokbook

I have a simple bit of code as shown below:
Private Sub btn_conact_Click()
Dim projectref As String
Dim savelocation As String
Dim projectSearchRange As Range
Dim LastRow As Integer
'set search value (porject key - unique)
projectref = cmb_Project.Value
Application.ScreenUpdating = False
'find the project reference in the tracking spreadsheet
Sheets("Project Tracking").Activate
Set projectSearchRange = Range("A:A").Find(projectref, , xlValues, xlWhole)
LastRow = projectSearchRange.Row
'file directory to save the new workbook in
savelocation = Cells(LastRow, 5).Value
'template for the contact list
Sheets("Contact List").Activate
Cells(7, 3).Value = projectref
'create new workbook
Set newWorkbook = Workbooks.Add
With newWorkbook
.Title = "Contact List for Project" & projectref
.SaveAs Filename:=savelocation & "/" & projectref & "Contact_List.xlsx"
End With
'Windows("Project tracker spreadsheet VBA").Activate
Sheets("Contact List").Copy Before:=Workbooks(projectref & "Contact_List.xlsx").Sheets("Sheet1") 'runtime error 9: subscript out of range
Windows(projectref & " Contact_List.xlsx").Activate
Application.ScreenUpdating = True
End Sub
As can be seen, I am getting a runtime error on the 4th last line of code, which is really a rather important line...
My question is, can anybody see where I could have possibly made a mistake which would cause this error? is successfully creates and saves the new workbook in the specified location, but is just falling over when it tries to copy the required sheet from the old workbook(Project tracker spreadsheet VBA) to the new one created by this code.
First, regarding your error, you are already defining and setting the new workbook with Set newWorkbook = Workbooks.Add, so why not use it when you "Contact List" sheet between workbooks.
To copy a worksheet between workbooks, you need to fully qualify the Worksheet object, ThisWorkbook.Sheets("Contact List").Copy Before:=NewWorkbook.Sheets("Sheet1")
Second, it's better to avoid using Activate when you can work directly with fully qualified Ranges and Worksheets.
Full Edited Code:
Option Explicit
Private Sub btn_conact_Click()
Dim projectref As String
Dim savelocation As String
Dim projectSearchRange As Range
Dim LastRow As Integer
Dim NewWorkbook As Workbook
'set search value (porject key - unique)
projectref = cmb_Project.Value
Application.ScreenUpdating = False
'find the project reference in the tracking spreadsheet
With Sheets("Project Tracking")
Set projectSearchRange = .Range("A:A").Find(projectref, , xlValues, xlWhole)
If Not projectSearchRange Is Nothing Then '<-- verify that find was successful
LastRow = projectSearchRange.Row
'file directory to save the new workbook in
savelocation = .Cells(LastRow, 5).Value
Else '<-- find was unsuccessful
MsgBox "Unable to find " & projectref
Exit Sub
End If
End With
'template for the contact list
Sheets("Contact List").Cells(7, 3).Value = projectref
'create new workbook
Set NewWorkbook = Workbooks.Add
With NewWorkbook
.Title = "Contact List for Project" & projectref
.SaveAs Filename:=savelocation & "/" & projectref & "Contact_List.xlsx"
End With
' ===== Fixed the error on thie line =====
ThisWorkbook.Sheets("Contact List").Copy Before:=NewWorkbook.Sheets("Sheet1")
NewWorkbook.Activate '<-- not sure why you want to Activate, but here you go
Application.ScreenUpdating = True
End Sub
I don't know how to insert code in comments so using answers space to guide you.
It appears Windows("Project tracker spreadsheet VBA") is not available. Could be the window text is incorrect. To confirm this. Please insert below code lines where the line has been commented out. This may give you some clue.
found = False
For Each Item In Windows
Debug.Print Item.Caption
If Item.Caption = "Project tracker spreadsheet VBA" Then
found = True
Exit For
End If
Next
If Not found Then
MsgBox "Window(Project tracker spreadsheet VBA) - Not found"
End If

Open save window in file path from a cell well also populateing filename from cell

i have a workbook that i use as a template to make estimates that when i'm done filling out the template there is a macro that creates a new workbook and copies all the sheets of the template workbook to the new one and then removes all the formulas and info i don't want the customer to see.
Here's part of my code that creates the new workbook and copies all the sheets from the template to the new one and then cleans it up
Sub TestConvert()
'Disabling the following to speed up the vba code, must re-enable at end of code
ActiveSheet.DisplayPageBreaks = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayAlerts = False
'adds file name and path to all sheets
Dim WSfn As Worksheet
For Each WSfn In ThisWorkbook.Sheets
'Adds formula to show file path
WSfn.Range("A2") = "=LEFT(CELL(""filename"",RC),FIND(""["",CELL(""filename"",RC),1)-1)"
'Adds formula to show file name
WSfn.Range("A3") = "=MID(CELL(""filename""),FIND(""["",CELL(""filename""))+1,(FIND(""]"",CELL(""filename""))-FIND(""["",CELL(""Filename""))-16))"
WSfn.Calculate 'Calculate sheet
WSfn.Range("A2") = WSfn.Range("A2") 'this will remove the formula from the cell making it text only
WSfn.Range("A3") = WSfn.Range("A3") 'this will remove the formula from the cell making it text only
Next
'************************************************************************************************
'copies all the sheets of the open workbook to a new one
Dim thisWb As Workbook, wbTemp As Workbook
Dim ws As Worksheet
Set thisWb = ThisWorkbook
Set wbTemp = Workbooks.Add 'creates new workbook dimmed as WbTemp
On Error Resume Next 'if there is in error when deleting will not stop the macro from continuing...
'.. deletes the extra sheets 2 sheets if on an older versions of excel
For Each ws In wbTemp.Worksheets
ws.Delete 'deletes all but one sheet in new workbook
Next
On Error GoTo -1 'clears the error handling and sets it to nothing which allows you to create another error trap.
'copys all the sheets from the original to the new workbook dimmed as wbTemp
For Each ws In thisWb.Sheets
ws.Copy After:=wbTemp.Sheets(wbTemp.Worksheets.Count)
Next
wbTemp.Sheets(1).Delete 'deletes the the first sheet in the list in the new workbook which is a black sheet from creating a new workbook
'put vba code to be ran in new book here
'makes all formulas in new workbook values only
wbTemp.Sheets.Select 'selects all sheets in new workbook
Cells.Select 'selects all cell
Selection.Copy 'copies everything selected
Selection.PasteSpecial Paste:=xlPasteValues 'pastes as values only in selected cells
wbTemp.Application.CutCopyMode = False 'clears the clipbored
'removes all defind names from new workbook / submittal
Dim xName As Name
For Each xName In wbTemp.Names
xName.Delete
Next
'removes all dropdowns from new workbook / submittal
Dim DD As Worksheet
For Each DD In wbTemp.Worksheets
Cells.Select
DD.Cells.Validation.Delete
Range("A1").Select
Next
'removes all vba buttons from all sheets
Dim i As Integer
On Error Resume Next
For i = 1 To 1000
wbTemp.Sheets(i).Buttons.Delete
Next i
'All sheets scroll to top left and select "A1"
Dim Sht As Worksheet
'****************************
'change A1 to suit your preference
Const TopLeft As String = "A1"
'****************************
'loop thru all the sheets in the workbook
For Each Sht In Worksheets
'scroll:=True takes cell to the top-left of window
Application.Goto Sheet.Range(TopLeft), scroll:=True
Next
'Hides the following from all sheets
wbTemp.Sheets.Select 'selects all sheets in new workbook
ActiveWindow.DisplayGridlines = False
ActiveWindow.DisplayHeadings = False
'selects the first sheet in the list
Sheets(1).Select
ActiveSheet.DisplayPageBreaks = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayAlerts = True
'save vba code here
'works to only add the filename would like it to also open in file path from cell A2
Application.Dialogs(xlDialogSaveAs).Show Range("A3").Text & "- (Submittal) " & Format(Date, "mm-dd-yy") & "_" & Format(Time, "hhmm") & ".xlsx"
End Sub
im wanting to make it so when the save window opens it opens in the file path from cell A2 and populates the filename from cell A3
i can also send/post the full excel file if that helps any.
The Application.GetSaveAsFilename method is a good choice for this. Pass the return value to a variant type var so you can test for Cancel or Close.
Dim sFN As Variant
With Worksheets("Sheet6")
sFN = .Range("A1") & Chr(92) & .Range("A2") & Format(Date, "_mm-dd-yy") '<~~ no extension yet
End With
With Application
sFN = .GetSaveAsFilename(InitialFileName:=sFN, _
FileFilter:="Excel Workbook (*.xlsx), *.xlsx," & _
"Macro Workbook (*.xlsm), *.xlsm," & _
"Binary Workbook (*.xlsb), *.xlsb")
End With
Select Case sFN
Case False
'user clicked Cancel or Close (×)
Debug.Print sFN
Case Else
With ThisWorkbook
Select Case Right(sFN, 5)
Case ".xlsx"
.SaveAs Filename:=sFN, FileFormat:=xlOpenXMLWorkbook
Case ".xlsm"
.SaveAs Filename:=sFN, FileFormat:=xlOpenXMLWorkbookMacroEnabled
Case ".xlsb"
.SaveAs Filename:=sFN, FileFormat:=xlExcel12
Case Else
'there really shouldn't be a case else
End Select
End With
End Select
I've added a Select Case statement statement for a Workbook.SaveAs method to three msot common types of Excel workbooks.
You can use the .InitialFileName property of the dialog.
Dim ws As Excel.Worksheet
Set ws = ActiveWorkbook.Sheets("Sheet1")
Dim oFileDialog As FileDialog
Set oFileDialog = Application.FileDialog(msoFileDialogSaveAs)
With oFileDialog
.Title = "Save File"
.ButtonName = "Ok"
.InitialFileName = ws.Range("A2").Value & "\" & ws.Range("A3").Value
.Show
End With
If you need to get back the name that it was saved as you can use .SelectedItems after .Show
MsgBox (oFileDialog.SelectedItems(1))
NOTE:
You probably want to do a quick verification that the directory in A2 exists before doing this. If it does not exist it will throw this into some users folder.
EDIT I'm not sure why yours isn't saving, could be excel version or some other variable in your code.
Since you have the path and name, do you really need the saveas dialog? You could just do
Workbooks.Add
'Then your code in your template that is modifying the active workbook
'Then save it without the dialog
ActiveWorkbook.SaveAs ws.Range("A2").Value & "\" & ws.Range("A3").Value
'OR
ActiveWorkbook.SaveAs Filename:= ws.Range("A2").Value & "\" & ws.Range("A3").Value

Excel VBA: How to copy entire range including hidden columns

I'm looking for a VBA Macro to export data to a csv. I found this code
which after some tweaking does a great job. However, when copying from a range, Excel seems to ignore hidden columns while I want the CSV to contain all the columns. Has anyone discovered concise way to code this?
Here is the code I have so far:
Sub ExportListOrTable(Optional newBook As Boolean, Optional willNameSheet As Boolean, Optional asCSV As Boolean, Optional visibleOnly As Boolean)
'Sub CopyListOrTable2NewWorksheet()
'Works in Excel 2003 and Excel 2007. Only copies visible data.
'code source: https://msdn.microsoft.com/en-us/library/dd637097%28v=office.11%29.aspx
'improved by: Tzvi
' - replaced new worksheet with new workbook
'params:
' newBook: To create a new new sheet in the current workbook or (default) in a new workbook
' willNameSheet: To offer the user to name the sheet or (default) leave the default names
' asCSV: not implemented - will always save as CSV
' visibleOnly: to filter out any hidden columns - default false
'TODO
' -add parameter list for following options:
' - if table was not selected, copy activesheet.usedRange
' - optional saveFileType
' -
Dim New_Ws As Worksheet
Dim ACell, Data As Range
Dim CCount As Long
Dim ActiveCellInTable As Boolean
Dim CopyFormats, retrySave As Variant
Dim sheetName, user, defaultFileName, fileSaveName As String
Dim userChoice As Boolean
'Check to see if the worksheet or workbook is protected. TODO this may not be necessary anymore
If ActiveWorkbook.ProtectStructure = True Or ActiveSheet.ProtectContents = True Then
MsgBox "This macro will not work when the workbook or worksheet is write-protected."
Exit Sub
End If
'Set a reference to the ActiveCell. You can always use ACell to
'point to this cell, no matter where you are in the workbook.
Set ACell = activeCell
'Test to see if ACell is in a table or list. Note that by using ACell.ListObject, you
'do not need to know the name of the table to work with it.
On Error Resume Next
ActiveCellInTable = (ACell.ListObject.Name <> "")
On Error GoTo 0
'TODO here we will select the fields to export
'If the cell is in a list or table run the code.
If ActiveCellInTable = True Then
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
If visibleOnly = True Then
'Test if there are more than 8192 separate areas. Excel only supports
'a maximum of 8,192 non-contiguous areas through VBA macros and manual.
On Error Resume Next
With ACell.ListObject.ListColumns(1).Range 'TODO remove this "with"
CCount = .SpecialCells(xlCellTypeVisible).Areas(1).Cells.Count
End With
On Error GoTo 0
If CCount = 0 Then
MsgBox "There are more than 8192 individual areas, so it is not possible to " & _
"copy the visible data to a new worksheet. Tip: Sort your " & _
"data before you apply the filter and try this macro again.", _
vbOKOnly, "Copy to new worksheet"
Exit Sub
Else
'Copy the visible cells.
ACell.ListObject.Range.Copy
End If
Else
'The user indicated he wants to copy hidden columns too.
'**********************************************************
'HOW DO I PROPERLY IMPLEMENT THIS PART?
'**********************************************************
MsgBox ("You wanted to copy hidden columns too?")
ActiveSheet.UsedRange.Copy
End If
Else
' MsgBox "Select a cell in your list or table before you run the macro.", _
' vbOKOnly, "Copy to new worksheet"
userChoice = MsgBox("A Table/Table protion is not selected. Do you want to export the entire page?", vbYesNo)
If userChoice = False Then Exit Sub
ActiveSheet.UsedRange.Copy
'Exit Sub
End If
'Add a new Worksheet/WorkBook.
If newBook = False Then
Set New_Ws = Worksheets.Add(after:=Sheets(ActiveSheet.Index))
Else
Set New_Ws = Workbooks.Add(xlWBATWorksheet).Worksheets(1)
End If
'Prompt the user for the worksheet name.
If willNameSheet = True Then
sheetName = InputBox("What is the name of the new worksheet?", _
"Name the New Sheet")
On Error Resume Next
New_Ws.Name = sheetName
If Err.Number > 0 Then
MsgBox "Change the name of sheet : " & New_Ws.Name & _
" manually after the macro is ready. The sheet name" & _
" you typed in already exists or you use characters" & _
" that are not allowed in a sheet name."
Err.Clear
End If
On Error GoTo 0
End If
'Paste the data into the new worksheet.
With New_Ws.Range("A1")
.PasteSpecial xlPasteColumnWidths
.PasteSpecial xlPasteValuesAndNumberFormats
.Select
Application.CutCopyMode = False
End With
Application.ScreenUpdating = False
'If you did not create a table, you have the option to copy the formats.
If ActiveCellInTable = False Then
Application.Goto ACell
CopyFormats = MsgBox("Do you also want to copy the Formatting?", _
vbOKCancel + vbExclamation, "Copy to new worksheet")
If CopyFormats = vbOK Then
ACell.ListObject.Range.Copy
With New_Ws.Range("A1")
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
End With
End If
End If
'Select the new worksheet if it is not active.
Application.Goto New_Ws.Range("A1")
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
'Now we're ready to save our new file as excel format
defaultFileName = ActiveWorkbook.Name
user = Environ("userprofile")
'marker getfilename: to return to if we need to look for a new filename
getfilename:
ChDir user & "\Desktop"
fileSaveName = Application.GetSaveAsFilename(defaultFileName & ".csv", "Comma Delimited Format (*.csv), *.csv")
If fileSaveName <> "False" Then
'error handling for 'file already exists and the user clicks 'no'
On Error Resume Next
ActiveWorkbook.SaveAs fileName:=fileSaveName, FileFormat:=xlCSV, ReadOnlyRecommended:=True, CreateBackup:=False, ConflictResolution:=xlUserResolution
If Err.Number = 1004 Then
'Offer user two options: To try a different filename or cancel the entire export
retrySave = MsgBox(Err.Description, vbRetryCancel, "Error creating file")
If retrySave = vbRetry Then
GoTo getfilename
Else
GoTo cancelprocedure
End If
End If
On Error GoTo 0
Else
GoTo cancelprocedure
End If
Exit Sub
cancelprocedure:
ActiveWorkbook.Close saveChanges:=False
Exit Sub
End Sub
Update:
In response to shagans concern. The parameter list on line one is intended to be set by another Macro as such:
Sub ExportVisibleAsCSV
Call ExportListOrTable(newBook:=True, willNameSheet:=False, asCSV:=True, visibleOnly:=True)
End Sub
Updating now that example code is available:
Ok looking at the code you posted, I see a bool named visibleOnly but I don't see where it gets set. Your ability for the logic to reach UsedRange.Copy entirely depends on that being set to false. The comment above ACell.ListObject.Range.Copy indicates that if you reach that statement you are only copying visible cells. In order to copy the hidden cells, visibleOnly would need to be set to false (bypassing the rest of the CCount stuff). So I would be interested in knowing how that bool is set and checking to see what its value is set to when you are running your code.
Update 2:
You need to set the value of your visibleOnly boolean somehow.
here's some code I edited that creates a message box that allows the user to say "yes" or "no" to "do you want to copy hidden data too?" that answer will dictate the value of visibleOnly which in turn dictates which flow they enter.
In addition to that, your assumption that ACell.ListObject.Range.Copy would only copy visible cells appears to have been incorrect. Instead that is being replaced with the specialcell type for visible cells.
Finally, vbYesNo does not actually return a boolean value. Instead it returns vbYes or vbNo which are vb type enumerators (value 6 and 7 respectively). So setting a bool to the value of a vbYesNo will always return True (as a value exists and essentially it just evaluates iferror).
So I changed that bit as well so it now properly checks the Yes/No condition on your userchoice (which is no longer a bool).
here's the code:
Dim ACell, Data As Range
Dim CCount As Long
Dim ActiveCellInTable As Boolean
Dim CopyFormats, retrySave As Variant
Dim sheetName, user, defaultFileName, fileSaveName As String
'Check to see if the worksheet or workbook is protected. TODO this may not be necessary anymore
If ActiveWorkbook.ProtectStructure = True Or ActiveSheet.ProtectContents = True Then
MsgBox "This macro will not work when the workbook or worksheet is write-protected."
Exit Sub
End If
'Set a reference to the ActiveCell. You can always use ACell to
'point to this cell, no matter where you are in the workbook.
Set ACell = ActiveCell
'Test to see if ACell is in a table or list. Note that by using ACell.ListObject, you
'do not need to know the name of the table to work with it.
On Error Resume Next
ActiveCellInTable = (ACell.ListObject.Name <> "")
On Error GoTo 0
'TODO here we will select the fields to export
'If the cell is in a list or table run the code.
If ActiveCellInTable = True Then
CopyHidden = MsgBox("Would you like to copy hidden data also?", vbYesNo, "Copy Hidden Data?")
If CopyHidden = vbYes Then
visibleOnly = False
ElseIf CopyHidden = vbNo Then
visibleOnly = True
End If
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
If visibleOnly = True Then
'Test if there are more than 8192 separate areas. Excel only supports
'a maximum of 8,192 non-contiguous areas through VBA macros and manual.
On Error Resume Next
With ACell.ListObject.ListColumns(1).Range 'TODO remove this "with"
CCount = .SpecialCells(xlCellTypeVisible).Areas(1).Cells.Count
End With
On Error GoTo 0
If CCount = 0 Then
MsgBox "There are more than 8192 individual areas, so it is not possible to " & _
"copy the visible data to a new worksheet. Tip: Sort your " & _
"data before you apply the filter and try this macro again.", _
vbOKOnly, "Copy to new worksheet"
Exit Sub
Else
'Copy the visible cells.
ACell.ListObject.Range.SpecialCells(xlCellTypeVisible).Copy
' Only visible cells within the table are now in clipboard
End If
Else
'The user indicated he wants to copy hidden columns too.
MsgBox ("You wanted to copy hidden columns too?")
ACell.ListObject.Range.Copy
' All table data cells including hidden are now in clipboard
End If
Else
' MsgBox "Select a cell in your list or table before you run the macro.", _
' vbOKOnly, "Copy to new worksheet"
userChoice = MsgBox("A Table/Table protion is not selected. Do you want to export the entire page?", vbYesNo)
If userChoice = vbNo Then Exit Sub
ActiveSheet.UsedRange.Copy
'Entire sheet range is now in clipboard (this is not always accurate)
'Exit Sub
End If
Assign the Value of the range to your target range instead of using the .Copy method:
Sub ExportCSV(source As Range, filename As String)
Dim temp As Workbook
Set temp = Application.Workbooks.Add
Dim sheet As Worksheet
Set sheet = temp.Worksheets(1)
Dim target As Range
'Size the target range to the same dimension as the source range.
Set target = sheet.Range(sheet.Cells(1, 1), _
sheet.Cells(source.Rows.Count, source.Columns.Count))
target.Value = source.Value
temp.SaveAs filename, xlCSV
temp.Close False
End Sub
This also has the benefit of not nuking whatever the user might have on the clipboard.

MS Excel : update multiple Workbooks using a macro in a master spreadsheet

I have 30 + workbooks on SharePoint that are all copies of each other except for the filename and the folder they are in. We use these to record progress on projects as we can't put an access DB on our sharepoint (don't ask!)
I need to update the data validation on all of the spread sheets and based on history may well have other changes that need making in all spread sheets again in the future.
I've got a macro that will change the validation in the spreadsheet its in.
In a "master" spreadsheet I can create the paths to the other spreadsheets and get them opened.
But when I try to run the code (in the master) against each spreadsheet using the macro copied from the single sheet I get an error:
runtime error 9 subscript out of range
on the line
Set myDestinationWorkSheet = Workbooks(myFile).Sheets("Data")
I've checked and the "data" sheet is definitely there. I've even copy and pasted the sheet name from the worksheet just in case. I'm confident that the filename is correct as the line before it opens the file. I've tried referencing the sharepoint files directly with http:// in the constructed filename and via a mapped drive (in case it was a "path too long" type error).
Here's the code (mostly a combination of stuff from other Stackoverflow answers!)
Sub Macro1()
'
' Macro1 Macro
'
Dim namedRange As Range
Set namedRange = Range("Schools")
Dim i As Long
Dim rng As Range
Dim mySchool As String
Dim myFile As String
Dim myDestinationWorkSheet As Worksheet
' step through list of schools
For i = 5 To Range("D" & Rows.Count).End(xlUp).Row
' build file name
mySchool = Range("D" & i).Value
Set rng = Range("e" & i)
'record filename in ppm master for bug tracking
myFile = Range("b5").Value & mySchool & Range("b6").Value & Range("b7").Value & mySchool & Range("b8").Value
'get filename
rng.Value = myFile
' open file
Workbooks.Open (myFile)
Set myDestinationWorkSheet = Workbooks(myFile).Sheets("Data")
myDestinationWorkSheet.Range("C2:J200").Select
' do stuff to file
With Selection.Validation
.Delete
.Add Type:=xlValidateDate, AlertStyle:=xlValidAlertStop, Operator:= _
xlBetween, Formula1:="=$L$13", Formula2:="=$L$15"
.IgnoreBlank = True
.InCellDropdown = True
.InputTitle = ""
.ErrorTitle = "Date out of range"
.InputMessage = ""
.ErrorMessage = _
"Please enter a date in between the values shown in Cell L13 and cell L15"
.ShowInput = True
.ShowError = True
End With
'mark as done
myDestinationWorkSheet.Range("L30").Value = "date validation updated 2/7/2014 CJ)"
'save and close file
Workbooks(myFile).Save
Workbooks(myFile).Close
'move to next in the list
Next i
'
Range("D5:D27").Select
End Sub
Any help appreciated.
I think the problem is probably coming from your MyDestinationWorksheet variable. It's set as a Worksheet and worksheets do not need to have the workbook name.
I'd try replacing Set myDestinationWorkSheet = Workbooks(myFile).Sheets("Data") with Set myDestinationWorkSheet = ActiveWorkbook.Sheets("Data") being as you've already opened the workbook you wish to set the myDestinationWorksheet variable to.
While your worksheet does not need a workbook name, it can be good practice to specify which workbook you're intending to work in. The problem is that your myFile is a file path and workbook name that works for Workbooks.Open (myFile) because those are necessary for the .Open method, but Workbooks(myFile) should have only the workbook name. You need to trim off the file path.