Using data from another workbook (other than the active one) - excel

Ignoring what my code actually does (it's not important to my question):
I want to be able to open my excel file, press a button, have the code use data in that workbook and another opened workbook (so I would have two workbooks opened at the same time, the macro runs in one of them but can take data from both of them).
The trick here is that I can't seem to find code to access the other workbook that I've opened up, so I can only take info from the active workbook.
For example,
Private Function GetLastRow() As Integer
Dim myLastRow As Integer
Set ws = ThisWorkbook.Sheets("Sheet1")
myLastRow = Range("C" & Rows.count).End(xlUp).Row
GetLastRow = myLastRow
End Function
This code lets me access the active workbook (the one running the code), using ThisWorkbook.
Is there another function capable of allowing me to access another opened workbook?

You could change your function to be more flexible.
Private Function GetLastRow(InWorksheet As Worksheet, InColumn As Variant) As Long
GetLastRow = InWorksheet.Cells(InWorksheet.Rows.Count, InColumn).End(xlUp).Row
End Function
So you can call it …
Sub Test()
Dim LastRow As Long
LastRow = GetLastRow(ThisWorkbook.Worksheet("Sheet1"), "C") 'column as letter
'or
'LastRow = GetLastRow(ThisWorkbook.Worksheet("Sheet1"), 3) 'column as number
End Sub
So you can even run this on another workbook using:
LastRow = GetLastRow(Workbooks("OtherWorkbook.xlsx").Worksheet("Sheet1"), "C") 'column as letter

There is a Workbook object built into VBA that you can use. This documentation should give you the information that you need https://learn.microsoft.com/en-us/office/vba/api/excel.workbook
You would simply put the name of your other workbook in quotes, in parentheses after using the Workbook object (see example on page I hyperlinked). Good luck!

I guess this is what you looking for.
When you have more then one Workbook active you can switch between then.
Sub GetLastRow()
Dim myLastRow As Integer
'Active Workbook
Set ws = ThisWorkbook.Sheets("Plan1")
myLastRow = ws.Range("C" & Rows.Count).End(xlUp).Row
MsgBox myLastRow
'Way when you know workbook name
Workbooks.Open Filename:=ActiveWorkbook.Path & "\Teste1.xlsx"
Set ws1 = Application.Workbooks("Teste1.xlsx").Sheets("Plan1")
myLastRow1 = ws1.Range("C" & Rows.Count).End(xlUp).Row
MsgBox myLastRow1
Dim myLastRow As Integer
'If you don't know the name but, opened after your main Workbook
Set ws3 = Application.Workbooks(2).Sheets("Plan1")
myLastRow3 = ws1.Range("C" & Rows.Count).End(xlUp).Row
MsgBox myLastRow3
End Sub

Related

Want to Activate previous workbook in VBA

I have 1 active workbook and adding another multiple workbooks basis on column data
and same saving with Basis on Next function and giving value of column
Need to activate new added workbook but problem is every time workbook name is different basis on column hence unable to activate the same basis on name
Help to activate another workbook
Activate the another workbook (previous workbook) without declaring name
If you're adding new workbooks, they're normally activated, then you can use ActiveWorkbook.Name, if you're however opening workbooks depending on a cell's value, you can use that when setting it to a Workbook object.
In this example, the paths to workbooks are in the A-column:
Sub openWorkbooksFromVariables()
Dim Lastrow As Long, i As Long
Dim wbQ As Workbook, wbB As Workbook
Dim qPath As String
Lastrow = Range("A" & Rows.Count).End(xlUp).Row
Set wbB = ActiveWorkbook 'current workbook is now accessible through wbB
For i = 2 To Lastrow
qPath = "" & Range("A" & i).Value & ""
Set wbQ = Workbooks.Open(qPath)
wbQ.Activate
wbQ.Save
wbQ.Close
Next i
End Sub
If there's anything more needed, please update your question with an example.

VBProject.VBComponents(wsTarget.CodeName).Name = code fail EVERY OTHER time. Why?

I have a small app containing about 20 subs which runs perfectly... Every other time.
It fails, in the sub ImportData, the first time I add a code name to a newly created sheet. on the following line:
ThisWorkbook.VBProject.VBComponents(wsTarget.CodeName).Name = "Customers"
The code below contains three subs which which can recreate the issue. Important note: I can run the sub ImportData as many times in a row without any issues but if I call the sub "Sync()" twice in a row it will fail on the second attempt but works fine on the third attempt and so on (Maybe it doesn't like odd numbers..)
Any ideas as to why this is happening would be greatly appreciated.
FYT: I am running this code in Excel for Mac
Public LastRow As Long
Private wks As Worksheet
Sub Sync()
Call ImportData
Call SyncBoth
End Sub
Public Sub ImportData()
'++++++++++++++++++++++++++++++++++++++++++++++++++++++
'+++++ 1. ImportData will allow user to select file to import data from
'+++++ 2. Copy both the Customers and Vendors data to their respective sheets
'++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dim wsSource As Worksheet
Dim wsTarget As Worksheet
Dim LastRow As Long
Dim MaxDate As Date
Dim ShCount As Integer
Dim SourceFile As String
SourceFile = "/Users/phild/Documents/RTPro/Customer and Vendor Raw Sync.xlsm"
Dim SourceWb As Workbook
Set SourceWb = Workbooks.Open(SourceFile)
Dim TargetWb As Workbook
Set TargetWb = ThisWorkbook
Dim sheet As Worksheet
For ShCount = 1 To 2
Select Case ShCount
Case 1
Set wsSource = SourceWb.Worksheets("Sheet1") 'Set Worksheet to copy data from
ThisWorkbook.Sheets("Customers").Delete 'Delete old Customer worksheet in this worksheet
Set sheet = ThisWorkbook.Sheets.Add 'Create New Customer woeksheet in this woekbook
sheet.Name = "Customers" 'Name new Customer worksheet
Set wsTarget = ThisWorkbook.Worksheets("Customers") 'Set Customers ws as the target ws
Debug.Assert ThisWorkbook.VBProject.Name <> vbNullString '<--Force the VBE to exist. Don't pollute the Immediate window
ThisWorkbook.VBProject.VBComponents(wsTarget.CodeName).Name = "Customers" 'Give Customers a Code name
'THE LINE OF CODE ABOVE RESULTS IN A Runtime error '32813"
' Method 'Name' of object '+VBComponent' failed
' EVERY OTHER TIME I RUN THE SUB Sync()
Case 2
Set wsSource = SourceWb.Worksheets("Sheet3") 'Set Worksheet to copy data from
ThisWorkbook.Sheets("Vendors").Delete 'Delete old Vendors worksheet in this worksheet
Set sheet = ThisWorkbook.Sheets.Add 'Create New Vendor worksheet in this woekbook
sheet.Name = "Vendors" 'Name new Vendor worksheet
Set wsTarget = ThisWorkbook.Worksheets("Vendors") 'Set Customers ws as the target ws
Debug.Assert ThisWorkbook.VBProject.Name <> vbNullString '<--Force the VBE to exist. Don't pollute the Immediate window '
ThisWorkbook.VBProject.VBComponents(wsTarget.CodeName).Name = "Vendors" 'Give Vendors a Code name
End Select
Call CleanTarget(wsTarget)
LastRow = Find_LastRow(wsSource)
wsSource.Range("A1:Z" & LastRow).Copy Destination:=wsTarget.Range("A1")
Next ShCount
SourceWb.Close
End Sub
Sub SyncBoth()
Dim ShCount As Integer
For ShCount = 1 To 2
Select Case ShCount
Case 1
Set wks = Customers 'Work in sheet("Customers")
LastRow = Find_LastRow(wks) 'Last row of "Customers"
Case 2
Set wks = Vendors 'Work in sheet("Vendors")
LastRow = Find_LastRow(wks) 'Last row of "Vendors"
End Select
Debug.Print wks.Name
Next ShCount
'Normally I have about 10 subs here that are called sequentially. But this is enough the cause the errorw
End Sub```
You're modifying the host VBA project at run-time - the code name identifier for Sheet1 is a compile-time, project-global scope object: even if it's not used anywhere, there's a legitimate chance that changing it requires recompiling the project.
So the code runs fine, up until it shoots its own foot off by renaming a global object; the next run now runs fine because now the compiled code matches the actual VBComponent in the project.
Consider having the macro in a separate VBA project, that prompts for which macro-enabled workbook to rename components in: because that VBA project won't be the VBA code that's compiled & running, it should "just work" then.

How to move rows from one workbook to another (no select)

My goal is to take an excel document with variable row size, copy it and then paste it onto the bottom row of a new document.
Longer story, I need to take monthly sales reports and stack them into a larger excel file. Each month we make a variable number of sales. I need to aggregate all of these months together so we can process them.
I have some code that I thought worked below. It was able to move variable rows within different work sheets, but could not do the same for different work books.
Private Sub MoveRowToEndOfTable()
Dim LastRow As Long
LastRow = Cells.Find(What:="*", SearchOrder:=xlByRows,SearchDirection:=xlPrevious).Row
Sheets(1).Range("A2:A" & LastRow, "G2:G" & LastRow).Copy
Workbooks("BRN report Aggregator.xlsx").Worksheets("New shares EOM").Range("a6000").End(xlUp).Offset(1, 0).Cells.Insert
End Sub
I guess that your workbook is closed, check it before paste values (if workbook is closed ~> open it) :
Private Sub MoveRowToEndOfTable()
Dim LastRow As Long
LastRow = Cells.Find(What:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Sheets(1).Range("A2:A" & LastRow, "G2:G" & LastRow).Copy
Dim wb As Workbook, wb_target As Workbook
'check if workbook is open already
For Each wb In Workbooks
If wb.Name = "BRN report Aggregator.xlsx" Then
Set wb_target = Workbooks("BRN report Aggregator.xlsx")
Exit For
End If
Next wb
'if not then open it
If wb_target Is Nothing Then
Set wb = Workbooks.Open("Path_to_file/BRN report Aggregator.xlsx")
End If
wb.Worksheets("New shares EOM").Range("a6000").End(xlUp).Offset(1, 0).PasteSpecial xlPasteAll 'or xlPasteValues --depends on your needs
wb.Close True 'save and close if required
End Sub

VBA taking too long to execute

I have a macro written that clears contents of the active cell row then calls a module to shift the remaining rows up. I am experiencing a long wait time for the macro to finish running. Not sure if this could be written better to execute quicker. The first module is called when a user clicks "Remove Client" on a User Form. Any help would be appreciated. Thank you!
'Called when user clicks Remove Client on User Form
Sub letsgo()
Dim ws As Worksheet
Dim wb As Workbook
Set wb = ThisWorkbook
Set ws = wb.Sheets("contactunder")
ws.Range("C" & ActiveCell.Row & ":BJ" & ActiveCell.Row).ClearContents
Call shiftmeup
End Sub
Sub shiftmeup()
Dim ws As Worksheet
Dim wb As Workbook
Set wb = ThisWorkbook
Set ws = wb.Sheets("contactunder") '/// The underhood of my contacts
With ws.Range("D11:BJ392")
For i = .Rows.Count To 1 Step -1
If IsEmpty(.Cells(i, 1)) Then .Rows(i).Delete Shift:=xlUp
Next
End With
End Sub
Why not change this line:
ws.Range("C" & ActiveCell.Row & ":BJ" & ActiveCell.Row).ClearContents
To this:
ws.Range("C" & ActiveCell.Row & "BJ" & ActiveCell.Row).EntireRow.Delete
This way you can avoid your second sub all together (or keep this as an occasional cleaner rather run it every time you simply need to delete 1 row.)
If you really do need both subs, a common first step for efficiency is to disable screen updating before entering your loop with Application.ScreenUpdating = False and then re-activate it when your loop ends by changing False to True.
This is the followup to urdearboy's answer...
The issue was in your second function and the static range used. You were deleting all the rows at the end, past your data (up to ~380 extra delete row calls). To fix it you should do two things
Only loop to the last row of data
Limit calls to the front end; put all the cells you want to delete into one range and delete it once
Sub ShiftMeUp()
Dim wb As Workbook
Dim ws As Worksheet
Dim DeleteRowRange As Range
Set wb = ThisWorkbook
Set ws = wb.Sheets("contactunder") '/// The underhood of my contacts
For i = 1 To GetLastRow(1, ws)
If IsEmpty(ws.Cells(i, 1)) Then Set DeleteRowRange = MakeUnion(ws.Rows(i), DeleteRowRange)
Next
If Not DeleteRowRange Is Nothing Then DeleteRowRange.EntireRow.Delete Shift:=xlUp
End Sub
I used 2 on my commonly used functions to keep the code clean...
MakeUnion
Public Function MakeUnion(Arg1 As Range, Arg2 As Range) As Range
If Arg1 Is Nothing Then
Set MakeUnion = Arg2
ElseIf Arg2 Is Nothing Then
Set MakeUnion = Arg1
Else
Set MakeUnion = Union(Arg1, Arg2)
End If
End Function
GetLastRow
Public Function GetLastRow(Optional Col As Integer = 1, Optional Sheet As Excel.Worksheet) As Long
If Sheet Is Nothing Then Set Sheet = Application.ActiveSheet
GetLastRow = Sheet.Cells(Sheet.Rows.Count, Col).End(xlUp).Row
End Function

Delete Macro from a Workbook the Macro has just created

I am having trouble with a macro I have created. I am creating multiple workbooks based on a selection made by the user. However, I want to remove the macro, and a worksheet from each file I have created. The code I have tried stops the code looping through the range as it seems maybe it is deleting the macro from the original file I am working from. Below is the code as it stands now which creates the workbooks as I want them fine but still with the worksheet containing a command button used to trigger the macro and the macro still in them. I need each file saved without them and the original template file to be unaffected.
Sub Button3_Click()
Dim MyCell As Range, MyRange As Range
Dim currentSheet As Excel.Worksheet
Dim LR As Long
Set currentSheet = ActiveSheet
LR = Range("A" & Rows.Count).End(xlUp).Row
Set MyRange = Range("A2:A" & LR).SpecialCells(xlCellTypeVisible)
For Each MyCell In MyRange
Worksheets("Front Sheet").Cells(5, 5) = MyCell.Value
ActiveWorkbook.RefreshAll
ActiveWorkbook.SaveAs Filename:="P:\Informatics\S&L scorecards\Clinical Scorecard Template\test\" & MyCell.Value & ".xls"
'here I want it to then delete the macro and a sheet called "Member"
Next MyCell
End Sub
Please note, I have tried the .xlsx file type but that also stops the loop - even if I use Application.DisplayAlerts = False

Resources