Wait until application.run is completed - vba

I'm trying to write a macro on one file that opens another workbook, runs a macro to get some data, copies the data into the first workbook and closes the second workbook.
I have however encountered a problem because it seems like the selection of the range is executed while the macro in the second workbook is still running and therefore selects the entire column:
i.e. the ws2.Range(StartCell, ws2.Cells(LastRow, "A")).Select string in the code below selects the whole column, and not just the cells where there is data.
'run macro in IB API file to get portfolio data
Application.Run "TwsDde.xls!Sheet15.subscribeToPorts"
'select data in column A from IB API file
Dim LastRow As Long
Dim StartCell As Range
Set StartCell = Range("A8")
LastRow = ws2.Cells(ws2.Rows.Count, StartCell.Column).End(xlDown).Row
ws2.Range(StartCell, ws2.Cells(LastRow, "A")).Select
Has anyone encountered this problem before or has any ideas on how to solve it?

Your calculation of LastRow is wrong. It should be xlUp instead of xlDown.

Related

VBA code executes with message box, but not without

I have a Sub in VBA that I need to be able to run multiple times. When I execute this piece of code to find the last row of the sheet:
Dim lastRow As Long
With ActiveSheet
lastRow = xlWs.Cells(.Rows.count, "A").End(xlUp).Row
End With
I get
object variable or with block variable not set
when I execute it a second time, but if I add a message box before the variable set:
Dim lastRow As Long
With ActiveSheet
MsgBox xlWs.Cells(.Rows.count, "A").End(xlUp).Row
lastRow = xlWs.Cells(.Rows.count, "A").End(xlUp).Row
End With
It works no matter how many times I run it. Has anyone run into this issue before? What is wrong with the first set of code, that is corrected with the second set?
You are using xlWs as a worksheet object inside the block of code that you are defining lastrow and I cannot see where you have defined it prior to calling it and that is why you get an error. If by any chance you have set that object in prior code executions it and somehow that worksheet is active you will not get any error message. We just don't have more information on what the other parameters are when you are running the code or how you are running it, but the best question for you here is how to follow a clean and standard definition of objects and using them inside a good code line. I would write a function for last row and make the worksheet and column number optional:
Function LastRowInColumn(Optional sh As Worksheet, Optional colNumber As Long = 1) As Long
'Finds the last row in a particular column which has a value in it
If sh Is Nothing Then
Set sh = ActiveSheet
End If
LastRowInColumn = sh.Cells(sh.Rows.Count, colNumber).End(xlUp).row
End Function
This function will manage setting the proper sheet for you, if you enter the sheet object it will use it, if not then it will use the active sheet. The same goes for the column you want to define the last row, if you enter any column number (e.g. colNumber=3 will be column C) it will find the last row in that column, if you leave it blank, it will consider column A.
I have not tested this, but I've had this problem before.
I think the issue is that you are not qualifying .Rows.count. Try changing it to xlWs.Rows.count. Without it you are getting the row count of the active worksheet.
The dot operator without a preceding object points to the object defined in the With statement, even when it's inside the cells() statement, which is qualified.)
' Try This
Dim lastRow As Long
With ActiveSheet
lastRow = xlWs.Cells(xlWs.Rows.count, "A").End(xlUp).Row
End With
I'm not sure why you are using "With Active Sheet". It looks like you are trying to get the last row in worksheet xlWs, which I assume is a worksheet object.
So, the code could be simplified to this:
' Better yet, try this
Dim lastRow As Long
lastRow = xlWs.Cells(xlWs.Rows.count, "A").End(xlUp).Row
The reason the message box fixed it is because the active worksheet was changed to xlWs by doing .End(xlUp)
' This code changes the active worksheet to worksheet xlWs
' by doing .End(xlUp)
MsgBox xlWs.Cells(.Rows.count, "A").End(xlUp).Row

Copying Data from External workbook based on headers not in order to another workbook

I hope this is the right place to ask this question as I am on the verge of going crazy. I am so rusty and I have zero experience with VBA (only with C++, java)
The problem:
I am trying to copy data from one workbook to another.
Lets say I have a workbook (called DATA) with several worksheets filled with data. Each column of data has a unique heading (all headings on the same row).
On the other hand I have another workbook (called REPORT) with one worksheet that contains only the heading of the data (in one row). They are not in the same order as in DATA workbook. For example I have 3 headings in REPORT worksheet that can be found in different worksheets in DATA workbook.
I need to loop through all the worksheets in the DATA workbook and copy paste the whole column to the REPORT worksheet when the same heading is found.
This image may help to understand. Explanation
Thanks ALOT for your help in advance. I have searched alot for this code but found similar stuff but didnt manage to understand any .
First attempt at doing it, but getting an error of Run-time error '1004'.
Any help?
Dim MyFile As String
Dim ws As Worksheet
''Workbook that contains one worksheet with all the headings ONLY NO DATA
Dim TargetWS As Worksheet
Set TargetWS = ActiveSheet
Dim TargetHeader As Range
''Location of Headers I want to search for in source file
Set TargetHeader = TargetWS.Range("A1:G")
''Source workbook that contains multiple sheets with data and headings _
not in same order as target file
Dim SourceWB As Workbook
Set SourceWB = Workbooks("Source.xlsx")
Dim SourceHeaderRow As Integer: SourceHeaderRow = 1
Dim SourceCell As Range
''Stores the col of the found value and the last row of data in that col
Dim RealLastRow As Long
Dim SourceCol As Integer
''Looping through all worksheets in source file, looking for the heading I want _
then copying that whole column to the target file I have
For Each ws In SourceWB.Sheets
ws.Activate
For Each Cell In TargetHeader
If Cell.Value <> "" Then
Set SourceCell = Rows(SourceHeaderRow).Find _
(Cell.Value, LookIn:=xlValues, LookAt:=xlWhole)
If Not SourceCell Is Nothing Then
SourceCol = SourceCell.Column
RealLastRow = Columns(SourceCol).Find("*", LookIn:=xlValues, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
If RealLastRow > SourceHeaderRow Then
Range(Cells(SourceHeaderRow + 1, SourceCol), Cells(RealLastRow, _
SourceCol)).Copy
TargetWS.Cells(2, Cell.Column).PasteSpecial xlPasteValues
End If
End If
End If
Next
Next
Your question didn't specify what part of the problem you're actually stuck on, so I'll assume you don't know how to start. Note that nobody on here is going to provide you with the full working solution to your problem - that's upto you to figure out.
A few tips to get you to start working:
The first question you're going to ask yourself with problems involving multiple workbooks is typically going to be which workbook am i going to attach my macro to?
In your case, the REPORT Workbook looks like a saner option, since you probably want someone to be clicking on something in the report in order to generate it. You could also argue the other way around though.
Once you have chosen where to put your VBA, you have to establish a reference to the other workbook.
You either have to load the other Excel file from disk using Workbooks.Open, or have both Workbooks be open at the same time in your Excel Instance, which I'd recommend for you because it's easier. In this case simply establish the reference using the Workbooks object.
Dim exampleRefToDATA As Workbook: Set exampleRefToDATA = Workbooks("data.xlsx") ' or index
Then, cycle through each Worksheet
using something like For Each ws As WorkSheet In exampleRefToDATA.WorkSheets as your For Loop
In that Loop, loop through the first column using something like
For Each allName As Range In ws.Range(... for you to figure out ...)
In this Loop, you'll have to look if that name is in your REPORTS sheet by doing another loop like
For Each thisName As Range in Range(... seriously, there's enough on stackoverflow on how to properly iterate over the used range of a row ...)
Note how this Range() call is Equivalent to ActiveWorkbook.ActiveWorkSheet.Range, which is your Reports sheet.
Then just check for equality and copy the row over if necessary. Again, copying a row has also been covered here before.
Hope this was helpful for you.

Copying until last row

I have multiple sheets and I want to copy their data to another workbook on the last empty row of it. The data has a random number of rows but a defined number of columns, and I am having trouble copying it (not all the sheet, only the rows that have data) and then pasting it to the last row of the final workbook. How should I do it? I have no clue at all...
Disclaimer: I don't want to use select of activesheet as I will have multiple workbooks open and this has brought me problems. Also, the data is continues, and in this I mean that there are no empty rows in the middle of it. Also, I don't think this will change anything but this code is running inside a loop so it copies all sheets from multiple files.
Thanks!
col is the column number you're trying to copy
For Each ws in Activeworkbook.worksheets
lastrow = ws.cells(ws.rows.count, col).end(xlup).row
'code to copy and paste
Next ws
I asume you have a handle to the worksheets, so you can use this code to copy the data:
Public Sub CopyAcross(oSheetFrom As Worksheet, oSheetTo As Worksheet)
Const DATA_START_ADDRESS As String = "$A$1"
oSheetFrom.Range(DATA_START_ADDRESS).CurrentRegion.Copy
oSheetTo.Range("A" & oSheetTo.Rows.Count).End(xlUp).PasteSpecial xlPasteAll
Application.CutCopyMode = False
End Sub
Assuming that the data to be copied has the same start address on each sheet, which is specified as DATA_START_ADDRESS

Enumerate all current sheet names in Excel VBA

I currently have code that takes user input and uses that to determine which worksheet is the "target" worksheet to be copied, but this takes a long time to run the code over and over for the 30+ sheets in the workbook.
I'm not sure where to begin coding something that would automatically take the data from one worksheet at a time, copy it to a new sheet, run some more code, and then repeat the process until done. Below is the current code.
'Prompt User: Which sheet on Dataworkbook should be copied
Dim mySheet As String
mySheet = Application.InputBox("Enter a sheet name")
'User provides input
'Data copied from source workbook page from "Dataworkbook" specified by user
x.Sheets(mySheet).Range("A1:z28").Copy
y.Sheets("Test").Range("A1").PasteSpecial
'Copy of data appears on new sheet created when running macro
Essentially I want this process to be automated, and to execute the same set of commands for every sheet currently in Dataworkbook, but I want it to stop when it reaches the end (no duplicates).
I'm very new at VBA (just started messing around with it yesterday) and would really appreciate any and all help.
You just need a simple iteration which can be acheived with the For Each ... Next structured loop.
Iterate over the Worksheets collection in the x Workbook:
'Prompt User: Which sheet on Dataworkbook should be copied
' Dim mySheet As String
' mySheet = Application.InputBox("Enter a sheet name")
'User provides input
Dim ws as Worksheet
For each ws in x.Worksheets
'Data copied from source workbook page from "Dataworkbook" specified by user
ws.Range("A1:z28").Copy
y.Sheets("Test").Range("A1").PasteSpecial
'### Put the rest of your code here to manipulate the data on ws
'
MsgBox ws.Range("A1").value 'etc.
'
'
'###
Next
Documentation on the For Each... Next:
http://msdn.microsoft.com/en-us/library/office/gg264596(v=office.15).aspx
A full list of VBA statements documentation which is also very useful:
http://msdn.microsoft.com/en-us/library/office/jj692812(v=office.15).aspx

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.