VBA Macro: Designating Sheets() with vlaues in a worksheet - vba

I am trying to do the following:
There is a group of files of the same layout, and I would like to extract a specific page of each file and compile them together in one file.
I have manually created the compilation workbook, with each sheet named after the corresponding data source workbook.
I have created a loop that opens all the files and copy the specific sheet ("Target sheet"), but I am not sure how to paste them correctly.
Here is what I have so far:
Dim rw As Integer, prop As String
rw = 5
Do While rw < 44
prop = wb1.Sheets("summary sheet").Cells(rw, 3).Value
Workbooks.Open Filename:= "(file address)", UpdateLinks:=False
Set wb2 = ActiveWorkbook
Set ws1 = wb2.Sheets("Target sheet")
ws1.Range("A1").Copy Range("BA150")
wb1.Sheets(prop).Cells(1, 1).Paste
wb2.Close SaveChanges:=False
rw = rw + 1
Loop
Note: the file address contains some sensitive info so I did not post it here, but it is coded as such that it can be looped.
Since the sheet names can be found in a certain location in the summary sheet in the compilation file, I tried to code the "Sheets()" with dynamic reference to that list, but I am getting error messages "Run-time error '438' Object doesn't support this property or method", with the wb1.Sheets(prop).Cells(1,1).Paste being highlighted as the faulty line. Can someone suggest a solution to this? Thank you very much in advance!
======EDIT======
I found a solution thanks to the two comments - for future reference:
ws1.Cells.Copy Destination:=wb1.Sheets(wsn).Range("A1")
I used numerical sheet reference instead. Probably needed to amend the original code to: (not tested)
ws1.Cells.Copy Destination:=wb1.Sheets(""" & prop & """).Range("A1")
To make it work.

If you are copying the entire sheet, then you can use
wb2.Sheets("Target Sheet").Copy After:=wb2
Rather than copy/paste.
It basically copies the entire sheet to the other workbook.

Related

Locating sheets by Name not by Codename

Using the Name of the sheet Not the Codename, I'm trying to make an update/Import kind of routine. Copy from Source (wb_From.sheet) and paste into Target (wb_To.sheet) with the same Name.
My code is locating each tab name in the Target BUT not Finding the same sheet name in the source (with different Codename). Any Suggestions?
Dim WB_To as String 'Target - WB to copy into
Dim WB_From as String 'Source- WB to copy from
For x = 1 To Workbooks(WB_To).Worksheets.Count
'Select Target sheet
Workbooks(WB_To). Sheets(x).Activate
tabName = Sheets(x).Name
'activate Source WB with same tab name
Workbooks(WB_From).Sheets(tabName).Activate
A typical referencing problem scenario so you might want to check on this post which explains the benefits of not using Activate, Select and the like. Now going to your problem, this line:
Workbooks(WB_To).Sheets(x).Activate
activates Sheet(x) yes, and you can actually get it's name by:
tabName = Sheets(x).Name
But you are looping, and who knows which workbook you are actually working on (although you made sure you inserted the Activate method on the right places). Might as well abandon Activate. Refactoring your loop:
For x = 1 To Workbooks(WB_To).Worksheets.Count
With Workbooks(WB_To).Sheets(x)
'/* do what you need to do */
Msgbox "Sheet " & .Name & " being processed from destination WB."
With Workbooks(WB_From).Sheets(.Name)
'/* your code here */
MsgBox "Sheet " & .Name & " found on source WB."
End With
End With
Next
Didn't change much, just eliminated the use of activate.
Not the most elegant solution, you can actually adopt to any alternative outlined in the link posted above, but I hope this gets you started.
Important: This solution doesn't account the possibility of mismatch sheet names or sheets that doesn't really exist on the source workbook. You will need to add a check to cover that too.
Instead of looping through sheet indexes (which will almost definitely not line up across workbooks) loop through the worksheets collection and get the name (not the codename). It's not guaranteed to fix your issue, but it will make the locals window easier to navigate and your issue a little easier to trace.
Dim WB_To as String ‘Target - WB to copy into
Dim WB_From as String ‘Source- WB to copy from
Dim ws as worksheet
For Each ws in Workbooks(WB_To).Worksheets
'Select Target sheet (which will be the ws)
ws.Activate
'activate Source WB with same tab name
WB_From.Sheets(ws.name).Activate

Copy/Paste variable dataset between workbooks without clipboard

I've been trying to optimize some of my coding and managed to cut and speed it up a lot. However there are some things that are still quite clunky (and me still a noob). Backstory is that my code is opening source and target files, copies a lot of data of variable length, closes source and then does a lot of operations and finally saves target file.
One of the things Id like is to do if possible is a direct copy of data without using clipboard, activating workbooks, selecting sheets (whatever of this is possible to pack into more efficient code that I am currently having)
Windows("SOURCE.xlsm").Activate
Sheets("Data").Select
Range("A2:AX10").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Windows("TARGET.xlsm").Activate
Range("A2").Select
ActiveSheet.Paste
Application.CutCopyMode = False
Is it possible to do a selection (A2:AX10 and all the way down to last row) in SOURCE file-Data sheet and directly copy it to TARGET file-Data sheet cell A2 without using clipboard.
The reason why I use A2:AX10 and then selection down is because I have a lot of blank cells in the whole data set and this way I get entire data.
I would like to be able to to that selection and use it as a range in this line
Workbooks(“SOURCE”).Worksheets("Data").Range(“A2:AX10 & ALLTHEWAYDOWN”).Copy _Workbooks(“TARGET”).Worksheets("Data").Range(“A2")
I was trying to solve this but I dont end up with desired result. When I try doing selection and setting as range then both trying copy range with activitng workbooks and in the direct copy mode I get 1004 error.
Is it possible to optimize this chunk and make it work. It would improve a lot of my VBA.
Thanks,
You need something like this:
With Workbooks("SOURCE.xlsm").Sheets("Data")
.Range("A2:AX10", .Range("A2:AX10").End(xlDown)).Copy Workbooks("TARGET.xlsm").ActiveSheet.Range("A2")
End With
You could probably also use CurrentRegion rather than End(xlDown
You can set one range's values (the range where you would want to paste values) equal to a source range's values (the range which you would previously have copied).
Sub paste_values()
Dim wb_A As Workbook, ws_A As Worksheet
Dim wb_B As Workbook, ws_B As Worksheet
Dim last_row As Long
Set wb_A = ThisWorkbook
Set ws_A = wb_A.Sheets(1)
Set wb_B = Workbooks("WorkbookB")
Set ws_B = wb_B.Sheets(1)
With ws_A
last_row = .Range("A" & .Rows.Count).End(xlUp).Row
End With
ws_B.Range("A2:AX" & last_row).Value = ws_A.Range("A2:AX" & last_row).Value
End Sub
This code is setting the new range's values equal to the original range. It prevents the need to activate sheets or workbooks, whilst also copying data to a range without filling the clipboard.
I would also recommend using last_row = .Range("A" & .Rows.Count).End(xlUp).Row to find the last row of your data. Although you do need to ensure you use this on a column which you know contains continuous data.

Copying row from a sheet to another on clicking a button getting an error

After Clicking on a button some rows are copying from a sheet to another but I am getting an error:
This workbook contains links to one or more external sources that could be unsafe.
If you trust the links,update them to get the latest data. Otherwise, you can keep working with the data you have.
[Update] [Don't Update] [Help]
I have tried clicking Update & Don't Update but it is not copying the data in either manner.
I am putting the value from a cell to wbLocationPath :
Set wbLocationPath = WSheet.Range("A2")
While wbLocationPath.Value <> ""
If IsWorkBookOpen(wbLocationPath.Value) Then
For Each wks In Workbooks
If (wks.Path & "\" & wks.Name) = wbLocationPath Then
Set wb = wks
Exit For
End If
Next wks
Else
Set wb = Application.Workbooks.Open(wbLocationPath.Value, ReadOnly:=False)
End If
so during debugging I got the error on setting the wb value.
wbLocationPath.value is having the path what is in A2 as c\users\me\desktop\project\XYZ.xlsx.
IsWorkBookOpen(wbLocationPath.Value) is returning false so control comes on else part which gives error.
I don't have any problem in pasting the value . My code is working fine for other thousands of filepath but it's not working just for some filepath .
I have some folders, I copied the every file's data from every folder. Getting the problem in a specific folder's("FOOD ADD") some specific files.
As I have some file name like "10024125 (01-0RD)" so all these type of file's datails copying but where the filename is "10016818 (03-1RD) FOOD ADD" is not copying & through the error what I have mentioned.
If you have the workbook open and it comes from a trusted location (see Excel Options ► Trust Center ► Trust Center Settings ► Trusted Locations), then you should not have a problem using a Paste Special, Values since you are only concerned with the values, not the original formulas. However, a direct value transfer may be sufficient to bypass the warnings. There are also some implicit cell parent worksheet references that should be made explicit.
with tmpSheet
lastrow = .Cells(Rows.Count, "A").End(xlUp).Row
Set rng = .Range(.Cells(1, 1), .Cells(lastrow, 1)) '<~~ note all parts belong to tmpSheet
end with
with pasteSheet
For Each c In rng.Cells
If c = "ABC" Then
'the following is an alternative method of direct value transfer
.Cells(Rows.Count, "A").End(xlUp).Offset(1).Resize(1, columns.count) = _
c.Resize(1, columns.count).Value
End If
Next c
end with
Note that when you were defining rng. the parent worksheet was explicit for the .Range but not the .Cells that made up the range. Also, the direct value transfer is a more efficient method of getting the values across without the clipboard.
I strongly suspect that the IsWorkBookOpen is not expecting the full path and workbook name that you are sending it. If it was only expecting the Workbook.Name property then you are never going to get a match when you send it the Workbook.FullName property.
Function IsWorkBookOpen2(sWS As String)
Dim b As Long
For b = 1 To Workbooks.Count
'Debug.Print Workbooks(b).FullName
If lcase(Workbooks(b).FullName) = lcase(sWS) Then _
Exit For
Next b
IsWorkBookOpen2 = CBool(b <= Workbooks.Count)
End Function
This replacement routine performs a non-case-sensitive comparison on the full path and filename (aka Workbook.FullName property) of the open workbooks to the value which you have stored in WSheet.Range("A2").
Note that it is called IsWorkBookOpen2 and not IsWorkBookOpen.
Actually there is no problem in VBA code. The problem is in the excel file . The excel was "linked WorkSheet" i.e it was linked to any server to update the value in excel whenever you want to open. So You can change the Link properties from "Data" menu by selecting "EditLink".
there we can change the prompt properties on the basis of your requirement.
please see the following solution from MS support to suppress the message:
https://support.microsoft.com/en-us/kb/826921

VBA runtime error - 1004 when trying to format or delete cells after pasting values

I am trying to use VBA to make my life easier but I keep getting a problem which I can't work around. Basically what I want is to copy some values from several output csv files I've got, to a nice formatted excel file. Then according to some bases numbers delete values or format the cells.
However I keep getting the same error message Run-time error '1004' application-defined or object defined error. I am doing that using many output files and pasting values at the same table file but on different sheets (10.2a, 10.2b, 10.2c, ...) by having macros for each sheet. I run all the macros in one using another macro that contains all the other macros
I looked a lot in other posts but don't understand where the error comes from. Any help would be much appreciated. The code I use for one sheet is below as an example.
Sub Table_10_2a()
'
' Copy Data from one file to another
'
Dim Output As Workbook
Dim Table As Workbook
Dim i As Integer
'Open workbooks
Set Output = Workbooks.Open("O:\...\Output.csv")
Set Table = Workbooks.Open("O:\...\Table.xlsx")
'Copy paste data from output file to Table
Output.Sheets("Output1").Range("B3:E7").Copy
Table.Sheets("10.2a").Range("B11").PasteSpecial xlValues
Output.Sheets("Output1").Range("B9:E13").Copy
Table.Sheets("10.2a").Range("B17").PasteSpecial xlValues
Output.Sheets("Output1").Range("B15:E15").Copy
Table.Sheets("10.2a").Range("B23").PasteSpecial xlValues
Output.Sheets("Output1").Range("B17:E21").Copy
Table.Sheets("10.2a").Range("B26").PasteSpecial xlValues
Output.Sheets("Output1").Range("B23:E27").Copy
Table.Sheets("10.2a").Range("B32").PasteSpecial xlValues
Output.Sheets("Output1").Range("B29:E29").Copy
Table.Sheets("10.2a").Range("B38").PasteSpecial xlValues
Output.Sheets("Output1").Range("B30:E30").Copy
Table.Sheets("10.2a").Range("B40").PasteSpecial xlValues
For i = 2 To 5
'Delete cells for values below 30
If Table.Sheets("10.2a").Cells(40, i).Value < 30 Then
Table.Sheets("10.2a").Range(Cells(26, i), Cells(36, i)).ClearContents
Table.Sheets("10.2a").Cells(38, i).NumberFormat = """[""0""]"""
Table.Sheets("10.2a").Cells(40, i).NumberFormat = """[""0""]"""
End If
'Format cells for values below 50
If Table.Sheets("10.2a").Cells(40, i).Value < 50 And Table.Sheets("10.2a").Cells(40, i).Value > 30 Then
Table.Sheets("10.2a").Range(Cells(26, i), Cells(38, i)).NumberFormat = """[""0.0""]"""
Table.Sheets("10.2a").Cells(40, i).NumberFormat = """[""0""]"""
End If
Next i
'Save file
Table.Save
'Close files
Output.Close
Table.Close
End Sub
This usage of Cells inside Range to build a block of cells commonly falls victim to an unqualified reference. In this case, you are using Table.Sheets("10.2a") to specify the sheet for Range but are not using the same qualifier on Cells. This means that Cells will use the default context available which varies with where the code is executing. Possibilities:
Inside a code module or ThisWorkbook, Cells refers to the ActiveSheet
Inside a Worksheet code behind, Cells refers to that Worksheet regardless of the ActiveSheet
Use Address to get around the different sheets
One approach is to follow the call to Cells with Address. This resolves the problem because Address returns the cell address without the sheet name. This is then interpreted by Range within its context which is Sheets("10.2a").
Range(Cells(26, i).Address, Cells(36, i).Address).ClearContents
Qualify the reference (generally preferred)
Another way to resolve this error is to qualify the reference by adding a sheet name before Cells: Table.Sheets("10.2a").Cells. Full line:
Range(Table.Sheets("10.2a").Cells(26, i), Table.Sheets("10.2a").Cells(36, i)).ClearContents
This type of code looks better within a With... End With block.

VBA Excel error when code is moved from worksheet to module

I have a Excel file with multiple tabs. I have a worksheet with some code which is working fine. This code also refers to data on some "master" tabs. I need to duplicate this sheet so I moved the common functions from there to a module. Now I get a VBA 1004 error when trying to access a range on the same worksheet.
Dim selectedRange As Range
Set selectedRange = Worksheets(name).Range("A1", _
Range("A" & Rows.count).End(xlUp)) 'Error Line
This code worked fine till I moved it to a module. It works if I put a
Worksheets(name).Select
before it, but I will have to do it too many times. Based on this query: VBA error 1004 - select method of range class failed
the code should work fine without a .Select. And it does as long as the code is within the worksheet. Why does moving code to a module create a problem?
U use Range and Rows properties without an object qualifier. When used without an object qualifier, this properties are a shortcut for ActiveSheet.Range / ActiveSheet.Rows.
So the code does this:
Worksheets(Name).Range("A1", ActiveSheet.Range("A" & ActiveSheet.Rows.Count).End(xlUp))
But Worksheets(name) could be different from active sheet so better is:
Worksheets(Name).Range("A1", Worksheets(Name).Range("A" & Worksheets(Name).Rows.Count).End(xlUp))
In With-End With block:
With Worksheets(name)
Set selectedRange = .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
End With
So it is ensured that the Range/Rows properties are applied on Worksheets(name) worksheet object.
When you do things on a sheet, you do not really need explicit declarations to that sheet.
However when working on a module and interacting with other sheets, you need to specify which Sheet you want to work with. So select the sheet before you can select the range. To say, SELECT PARENT BEFORE YOU SELECT THE CHILDREN :) Please note, following is just the logic explanantion. Not the exact code syntax.
So I suggest you create the following worksheet variable and set your worksheet object that you need into that.
e.g.
Dim WS as Worksheet
Dim selectedRange As Range
Set WS = Sheets("Shee1")
Set selectedRange = WS.Range("A1", _
Range("A" & Rows.count).End(xlUp)) 'Error Line
Or else if you want to refer to all sheets, you may use each sheet's index
E.g. ThisWorkBook.Sheets(i) 'i is an integer
Then loop or whatever as it deems to your programme structure.
Further you do not have to use Select on the worksheet to point to a range in that worksheet. As per above code you could set the worksheet and set the range you need to process. When optimizing VBA execution, select is usually a taboo. Also Excel 2007 does not retain the active sheet the way older versions used to.