Éxcel-VBA Open Workbook in macro that have been called in a cell - vba

I have some trouble with a function that I call from a cell in Excel.
The macro shall open a workbook get some data and then return a mathematical results to the cell.
But when I use the the following code it does not open the wordbook, just return #VALUE! to the cell. It break out of the code right after I tried to open the workbook.
' This Interpolation function is used to get data from other Excel sheets
Public Function DatasheetLookup(ExcelFile As String, ExcelSheet As String, xVal As Double, Optional isSorted As Boolean = True) As Variant
' abosolute or relative path?
If Not (Left(ExcelFile, 3) Like "[A-Z]:\") Or (Left(ExcelFile, 2) = "\\") Then
ExcelFile = ThisWorkbook.path & "\" & ExcelFile
End If
' does file exits?
If Dir(ExcelFile, vbDirectory) = vbNullString Then
DatasheetLookup = "No such file!"
Exit Function
End If
' open the source workbook, read only
Dim Wbk As Workbook
Dim WS As Worksheet
' Application.ScreenUpdating = False ' turn off the screen updating
Set Wbk = Workbooks.Open(ExcelFile)
' Run through all sheets in the source workBook to find "the one"
For Each WS In Wbk.Worksheets ' <-- Here it exit the code and return #VALUE!
If WS.Name <> ExcelSheet Then
DatasheetLookup = "Sheet not found!"
Else
Dim xRange As Range
Dim yRange As Range
xRange = WS.Range("A1", "A" & WS.UsedRange.Rows.Count)
yRange = WS.Range("B1", "B" & WS.UsedRange.Rows.Count)
Dim yVal As Double
Dim xBelow As Double, xAbove As Double
Dim yBelow As Double, yAbove As Double
Dim testVal As Double
Dim High As Long, Med As Long, Low As Long
Low = 1
High = WS.UsedRange.Rows.Count
If isSorted Then
' binary search sorted range
Do
Med = Int((Low + High) \ 2)
If (xRange.Cells(Med).Value) < (xVal) Then
Low = Med
Else
High = Med
End If
Loop Until Abs(High - Low) <= 1
Else
' search every entry
xBelow = -1E+205
xAbove = 1E+205
For Med = 1 To xRange.Cells.Count
testVal = xRange.Cells(Med)
If testVal < xVal Then
If Abs(xVal - testVal) < Abs(xVal - xBelow) Then
Low = Med
xBelow = testVal
End If
Else
If Abs(xVal - testVal) < Abs(xVal - xAbove) Then
High = Med
xAbove = testVal
End If
End If
Next Med
End If
xBelow = xRange.Cells(Low): xAbove = xRange.Cells(High)
yBelow = yRange.Cells(Low): yAbove = yRange.Cells(High)
DatasheetLookup = yBelow + (xVal - xBelow) * (yAbove - yBelow) / (xAbove - xBelow)
Exit For
End If
Next WS
Wbk.Close Savechanges:=False
Set Wbk = Nothing
Application.ScreenUpdating = True
End Function

I am not sure the reason for this specifically, but you cannot open a file in a user defined function. There are many additional actions that cannot be performed in a Function as well. This is also discussed in this Stack Overflow answer here.
However, in your case, you can easily cheat this limitation by opening the file you want to read before you call the function. I prepared a very basic demonstration of this, you will need to modify the code as needed to fit your particular example:
Code in "ThisWorkbook":
' when the workbook opens, also open the companion spreadsheet so it is available to use
Private Sub Workbook_Open()
Set Wbk = Workbooks.Open("C:\Users\lrr\Desktop\Myworkbook.xlsx")
End Sub
Code in "Module1":
Global Wbk As Workbook
Public Function testFunc()
' the workbook is already opened, so you may perform this iteration operation w/o any problems.
For Each WS In Wbk.Worksheets
testFunc = 1
Exit Function
Next WS
End Function
Code in Cell A1:
=testFunc()

Related

Return the Worksheet that an Excel Chart is referencing using VBA

I need to be able to identify the worksheet that an excel chart (on a worksheet) is getting it's data from. I only need the data sheet which series 1 is referencing. I've started trying to extract the sheet name from .SeriesCollection(1).Formula but it gets realy complex. here's what I've got so far:
Sub GetChartDataSheet()
Dim DataSheetName As String
Dim DataSheet As Worksheet
DataSheetName = ActiveChart.SeriesCollection(1).Formula
DataSheetName = Left(DataSheetName, InStr(1, DataSheetName, "!$") - 1)
DataSheetName = WorksheetFunction.Replace(DataSheetName, 1, Len("=series("), "")
If Left(DataSheetName, 1) = "'" And Right(DataSheetName, 1) = "'" Then DataSheetName = Mid(DataSheetName, 2, Len(DataSheetName) - 2)
DataSheetName = Replace(DataSheetName, "''", "'")
Set DataSheet = Sheets(DataSheetName)
End Sub
this works in a lot of cases, but if my users have a strange worksheet name (eg Sh'e e$,,t!3!$) it fails. the same goes if series 1 has been named (eg .SeriesCollection(1).Formula = "=SERIES(**"Hell,o !"**,'Sh''e e$,,t!3!$'!$B$2:$B$18,'Sh''e e$,,t!3!$'!$C$2:$C$18,1)".
Is there a simple way to solve this?
I thought this is an easy one, turns out it's not. One of the cases where Excel has the information but will not give it away for free. I ended up with a function like this - maybe this helps:
Function getSheetNameOfSeries(s As Series) As String
Dim f As String, i As Integer
Dim withQuotes As Boolean
' Skip leading comma if not all parts of series is filled. Check if sheetname is in single quotes
For i = 9 To Len(s.Formula)
If Mid(s.Formula, i, 1) <> "," Then
If Mid(s.Formula, i, 1) = "'" Then
withQuotes = True
f = Mid(s.Formula, i + 1)
Else
withQuotes = False
f = Mid(s.Formula, i)
End If
Exit For
End If
Next i
' "f" now contains a part of the formula with the sheetname as start
' now we search to the end of the sheet name.
' If name is in quotes, we are looking for the "closing" quote
' If not in quotes, we are looking for "!"
i = 1
Do While True
If withQuotes Then
' Sheet name is in quotes, found closes quote --> we're done
' (but if next char is also a quote, we have the case the the sheet names contains a quote, so we have to continue working)
If Mid(f, i, 1) = "'" Then
If Mid(f, i + 1, 1) <> "'" Then
getSheetNameOfSeries = Mid(f, 1, i - 1)
Exit Do
Else
i = i + 1 ' Skip 2nd quote
End If
End If
Else
' Sheet name is quite normal, so "!" will indicate the end of sheetname
If Mid(f, i, 1) = "!" Then
getSheetNameOfSeries = Mid(f, 1, i - 1)
Exit Do
End If
End If
i = i + 1
Loop
getSheetNameOfSeries = Replace(getSheetNameOfSeries, "''", "'")
End Function
You can use the Find function to look for the values of SeriesCollection(1).
In the worksheet that hold the data of SeriesCollection(1), you will be able to find all the values in that array.
More explanations inside the code below.
Code
Option Explicit
Sub GetChartDataSheet()
Dim DataSheetName As String
Dim DataSheet As Worksheet
Dim ws As Worksheet
Dim ValuesArr As Variant, Val As Variant
Dim FindRng As Range
Dim ShtMatch As Boolean
Dim ChtObj As ChartObject
Dim Ser As Series
' if you want to use ActiveChart
Set ChtObj = ActiveChart.Parent
Set Ser = ChtObj.Chart.SeriesCollection(1)
ValuesArr = Ser.Values ' get the values of the Series Collection inside an array
' use Find to get the Sheet's origin
For Each ws In ThisWorkbook.Sheets
With ws
ShtMatch = True
For Each Val In ValuesArr ' loop through all values in array
Set FindRng = .Cells.Find(what:=Val) ' you need to find each value in the worksheet that SeriesCollection data is tied to
If FindRng Is Nothing Then
ShtMatch = False
Exit For
End If
Set FindRng = Nothing ' reset
Next Val
If ShtMatch = True Then
Set DataSheet = ws
Exit For
End If
End With
Next ws
DataSheetName = DataSheet.Name
End Sub

VBA Type missmatch

I have wrote some VBA code which I was fairly happy with. It went through a list on a worksheet, switched to another and set a variable (and thus changed some graphs) and then opened word, copied in the graphs to various bookmarks and saved the document as the variable name.
It worked like a charm and I was a happy boy (saved a good week and a bit of work for someone). I have not touched it since - or the worksheets for that matter - opened it today and it is giving me a type missmatch on the first lot. I would really love some advice as it has left me scratching my head.
Public X As Integer
Public Y As String
Sub Macro2()
'Set up variables that are required
Y = ""
LoopCounter = 2
Do Until Y = "STOP"
'Grab the value from a list
Sheets("CPD data 13-14").Select
Range("A" & LoopCounter).Select
Y = Range("A" & LoopCounter).Value
'Change the chart values
Sheets("Pretty Display (2)").Select
Range("A1").Value = Y
'Open word template
Set wordapp = CreateObject("word.Application")
wordapp.documents.Open "LOCATION"
wordapp.Visible = True
wordapp.Activate
wordapp.ActiveDocument.Bookmarks("InstitutionName").Range = Y
wordapp.ActiveDocument.Bookmarks("Graph1").Range = ActiveSheet.ChartObjects("Chart 3")
'Close document
Mystring = Replace(Y, " ", "")
wordapp.ActiveDocument.SaveAs Filename:="LOCATION" & Mystring & ".docx"
wordapp.Quit
Set wordapp = Nothing
'Increase count and loop
LoopCounter = LoopCounter + 1
Loop
The error hits on the following line:
wordapp.ActiveDocument.Bookmarks("Graph1").Range = ActiveSheet.ChartObjects("Chart 3")
EDIT
As suggested I have updated my code not to use select so it now reads:
Set ws = Sheets("CPD data 13-14")
Set pd = Sheets("Pretty Display (2)")
'Set up variables that are required
Y = ""
LoopCounter = 2
Do Until Y = "STOP"
'Grab the value from a list
Y = ws.Range("A" & LoopCounter).Value
'Change the chart values
pd.Range("A1").Value = Y
'Open word template
Set wordapp = CreateObject("word.Application")
wordapp.documents.Open "LOCATION"
wordapp.Visible = True
wordapp.Activate
wordapp.ActiveDocument.Bookmarks("InstitutionName").Range = Y
wordapp.ActiveDocument.Bookmarks("Graph1").Range = pd.ChartObjects("Chart 3")
'Close document
Mystring = Replace(Y, " ", "")
wordapp.ActiveDocument.SaveAs Filename:="LOCATION" & Mystring & ".docx"
wordapp.Quit
Set wordapp = Nothing
'Increase count and loop
LoopCounter = LoopCounter + 1
Loop
I still get the same runtime error at the same point.
try this
Option Explicit
Public X As Integer
Public Y As String
Sub Macro2()
Dim wordApp As Object
Dim LoopCounter As Integer
Dim Mystring As String
Dim ws As Worksheet, pd As Worksheet
Set ws = Sheets("CPD data 13-14")
Set pd = Sheets("Pretty Display (2)")
'Set up variables that are required
Y = ""
LoopCounter = 2
' open one Word session for all the documents to be processed
Set wordApp = CreateObject("word.Application")
Do Until Y = "STOP"
'Grab the value from a list
Y = ws.Range("A" & LoopCounter).Value
With pd
.Range("A1").Value = Y 'Change the chart values
.ChartObjects("Chart 3").Copy ' Copy the chart
End With
'act on Word application
With wordApp
'open word template
.documents.Open "LOCATION"
.Visible = True
' paste into bookmarks, "save as" document and close it
With .ActiveDocument
.Bookmarks("InstitutionName").Range = Y
.Bookmarks("Graph1").Range.PasteSpecial
Mystring = Replace(Y, " ", "")
.SaveAs Filename:="LOCATION" & Mystring & ".docx"
.Close
End With
End With
'Increase count and loop
LoopCounter = LoopCounter + 1
Loop
'Close Word
wordApp.Quit
Set wordApp = Nothing
End Sub
I couldn't have a word "Range" object directly set to an Excel "Chart" object
So I had to copy the chart and use "PasteSpecial" method of the Word "Range" object
Furthemore I worked with one Word session only, which'd result in a faster job
Finally I also made some "comsetics" to make the code more readable/maintanable
just as a suggestion: I'd always use "Option Explicit" statement. that'll force you some extra work to explicitly declare each and every variable you use, but that will also give much more control over your work and result in less debbugging issues, thus saving time at the end
My advice is to set the Explicit flag and try to decompile the code. Any variables that you didn't dimension will throw an error. This is a good time to dimension the variable and type the data appropriately.
If that doens't throw an error, which it should since you have at least one variable LoopCounter that isn't dimensioned and could therefore cause data type errors then try changing Public X As Integer to Public X As Long so as to avoid values beyond the limit of the Integer data type.
.Activate is sometimes necessary even when using .Select from my experience. Selecting a worksheet should make it the active worksheet, but that's not always the case.

Macros, using array to copy worksheets to a different workbook

We have an SSRS report that has a separate worksheet for each division. We run a macro to rename all the worksheets with the division name and then copy specific worksheets to a new workbook to be emailed to the divisions. The problem with the code is that if one of the divisions does not have a worksheet that month the macro errors out with an error of "not in specified range". Is there a way to tell it to ignore missing worksheets if they do not exist this time? Here is the code:
Sheets(Array("AB", "CD", "EF", "GH", "IJ", "KL")).Copy
Sheets("AB").Select
ActiveWorkbook.SaveAs Filename:= _
Path & "Holder Agings " & Today & ".xlsx", FileFormat:=xlOpenXMLWorkbook, _
Password:="", WriteResPassword:="", ReadOnlyRecommended:=False, _
CreateBackup:=False
Thank You!
I agree with Rusan Kax, without a complete block of code it is difficult to produce exactly the code you need. The code below shows two techniques. You should be able to adapt one of them to your requirements.
Option Explicit
Sub Test1()
' Demonstrate CheckWshts(Array) which removes names from the array
' if they do not match the name of a worksheet within the active
' workbook
Dim InxWsht As Long
Dim WshtTgt() As Variant
WshtTgt = Array("AB", "CD", "EF", "GH", "IJ", "KL")
Call CheckWshts(WshtTgt)
For InxWsht = LBound(WshtTgt) To UBound(WshtTgt)
Debug.Print WshtTgt(InxWsht)
Next
End Sub
Sub Test2()
' Demonstrates WorksheetExists(Name) which returns True
' if worksheet Name is present within the active workbook.
Dim InxWsht As Long
Dim WshtTgt() As Variant
WshtTgt = Array("AB", "CD", "EF", "GH", "IJ", "KL")
For InxWsht = LBound(WshtTgt) To UBound(WshtTgt)
If WorksheetExists(CStr(WshtTgt(InxWsht))) Then
Debug.Print WshtTgt(InxWsht) & " exists"
Else
Debug.Print WshtTgt(InxWsht) & " does not exist"
End If
Next
End Sub
Sub CheckWshts(WshtTgt() As Variant)
' * WshtTgt is an array of worksheet names
' * If any name is not present in the active workbook,
' remove it from the array
Dim Found As Boolean
Dim InxWshtActCrnt As Long
Dim InxWshtTgtCrnt As Long
Dim InxWshtTgtMax As Long
InxWshtTgtCrnt = LBound(WshtTgt)
InxWshtTgtMax = UBound(WshtTgt)
Do While InxWshtTgtCrnt <= InxWshtTgtMax
Found = False
For InxWshtActCrnt = 1 To Worksheets.Count
If Worksheets(InxWshtActCrnt).Name = WshtTgt(InxWshtTgtCrnt) Then
Found = True
Exit For
End If
Next
If Found Then
' Worksheet WshtTgt(InxWshtTgtCrnt) exists
InxWshtTgtCrnt = InxWshtTgtCrnt + 1
Else
' Worksheet WshtTgt(InxWshtTgtCrnt) does not exist
WshtTgt(InxWshtTgtCrnt) = WshtTgt(InxWshtTgtMax)
InxWshtTgtMax = InxWshtTgtMax - 1
End If
Loop
' Warning this code does not handle the situation
' of none of the worksheets existing
ReDim Preserve WshtTgt(LBound(WshtTgt) To InxWshtTgtMax)
End Sub
Function WorksheetExists(WshtName As String)
' Returns True is WshtName is the name of a
' worksheet within the active workbook.
Dim InxWshtCrnt As Long
For InxWshtCrnt = 1 To Worksheets.Count
If Worksheets(InxWshtCrnt).Name = WshtName Then
WorksheetExists = True
Exit Function
End If
Next
WorksheetExists = False
End Function

Excel 2010 - Tabs created, now need to copy from external source

I was forced to start to learn this by my employer. Unfortunately I was not given much time to prepare and I need to give results soon :-)
Here is something I was able to put together with assist of this forum - it's creating tabs for each day and naming them properly:
Sub Testovanie()
'
' Testovanie Macro
'
' Keyboard Shortcut: Ctrl+a
'
Dim pocet_tabov As Integer
Dim netusim As Integer
Dim sheet_meno As String
Dim string_pre_datum As String
Dim zadany_mesiac As Integer
Dim datum As Date
zadany_mesiac = 13
While (zadany_mesiac < 1) Or (zadany_mesiac > 12)
zadany_mesiac = Val(InputBox("Numeric month?"))
If zadany_mesiac = 0 Then Exit Sub
Wend
Application.ScreenUpdating = False
string_pre_datum = Str(zadany_mesiac) & "/1/" & Year(Now())
datum = CDate(string_pre_datum)
For pocet_tabov = 1 To 10
sheet_meno = Format((datum + pocet_tabov - 1), "dd.MMM.yyyy")
If Month(datum + pocet_tabov - 1) = zadany_mesiac Then
If pocet_tabov <= Sheets.Count Then
If Left(Sheets(pocet_tabov).Name, 5) = "Sheet" Then
Sheets(pocet_tabov).Name = sheet_meno
Else
Sheets.Add.Move after:=Sheets(Sheets.Count)
ActiveSheet.Name = sheet_meno
End If
Else
Sheets.Add.Move after:=Sheets(Sheets.Count)
ActiveSheet.Name = sheet_meno
End If
End If
Next pocet_tabov
For pocet_tabov = 1 To (Sheets.Count - 1)
For netusim = pocet_tabov + 1 To Sheets.Count
If Right(Sheets(pocet_tabov).Name, 10) > _
Right(Sheets(netusim).Name, 10) Then
Sheets(netusim).Move before:=Sheets(pocet_tabov)
End If
Next netusim
Next pocet_tabov
Sheets(1).Activate
Application.ScreenUpdating = True
End Sub
Now I need to copy prepared template from for example "C:\Troll\Template.xlsx" into all of theese created sheets. Additionally, template includes this formula: ='C:\Troll[source.xls]1.febr'!$U$33
I need this one to be updated in every new sheet. So the sheet with name 01.Feb.2014 needs to have template copied from [source.xls]1.febr'!$U$33, second sheet 02.Feb.2014 needs to have [source.xls]2.febr'!$U$33 and so on.
I was trying to do the copy - that worked. However I'm not able to join it with this one to be one big script.
Copying:
Public Function kopirovanie(sheet_meno As String)
Dim bWasClosed As Boolean
Dim cesta As String
Dim zdroj As Workbook
Dim ciel As Workbook
'Set ciel = Workbooks("template for copy.xlsx")
Set ciel = ActiveWorkbook ' for testing
' just in case the source wb is already open...
On Error Resume Next ' avoid the error if not open
Set zdroj = Workbooks("template for copy.xlsx")
On Error GoTo 0
If zdroj Is Nothing Then
bWasClosed = True
cesta = "C:\Project Tata\Kopirovanie\"
Set zdroj = Application.Workbooks.Open(cesta & "template for copy.xlsx")
End If
zdroj.Worksheets("Sheet1").Copy before:=ciel.Worksheets("Sheet1")
If bWasClosed Then
zdroj.Close False ' close without saving
End If
End Function
the function is supposed to be called after this
If pocet_tabov <= Sheets.Count Then
If Left(Sheets(pocet_tabov).Name, 5) = "Sheet" Then
Sheets(pocet_tabov).Name = sheet_meno
But I get error that copying is out of range. I think that I need to specify that it should copy regardless of the Tab name. Or actually I want it to copy into Active sheet...
the error is "Run-time error'9'" Subscript out of range.. and it marks me this one yellow: zdroj.Worksheets("Sheet1").Copy before:=ciel.Worksheets("Sheet1")
!! Look for the comments - part of this was already solved.
Now to continue with changing formula:
I have two docs. Lets call them Source.xls and Results.xls
Results doc has the macro you've wrote in it. That means we've copied 1 table that is exactly the same in all the newly created sheets - that's a part fo the job. However if I would do this with the table I have I would end up with Workbook created for 31 days of the month where is table with formula " ='C:\Troll[data_source.xls]1.febr'!$U$33 " .. this would end up with every day of Results showing results of the 1.st february of the data_source.
I need worksheet that was created for 1st feb, to get data from 1st feb, sheet for 2nd to get data from 2nd feb and so on.. Please be aware that source of table with formula and source of data which formula refers to are 2 different workbooks
I think this macro meets the first part of your requirement.
I have used your variable names when I am confident that I understand then. I have used my own names for other variables. I suggest you avoid renaming them until we have met your entire requirement.
I have not explained my new code. I did not want to spent time doing so if it does not meet your requirement. I am happy to explain anything you want to understand.
I use Excel 2003 so my extensions are different to yours. Change "xls" to "xlsx" before trying the macro.
I have three workbooks:
The workbook containing the macro.
The workbook containing the template worksheet. I have used your name for this workbook (except for the extension) but have changed the path to the folder holding the macro workbook.
The workbook created by the macro. I have named this Format(datum, "yyyy mmm"). Again I have changed the path to the folder holding the macro workbook.
You can change the paths immediately or you can wait until we have finished development.
Edit The remainder of this answer has been replaced.
The revised code below now updates the formula in cell C3 of each sheet created in WbookCreate. I believe I have made the correct change so the formula references the correct worksheet in workbook Source.xlsx.
However, I have made another change. In the original code, I named the created sheets as "dd.MMM.yyyy". I believe that was incorrect and I should have named then as "d.MMM". However, in the new code I name them as "d" and have added a statement to adjust the TabRatio. This means that all the tabs are visible at the same time. This is just a demonstration of what is possible; you can easily change to any name you prefer.
Option Explicit
Sub CreateDailySheets()
Const WbookCopyName As String = "template for copy.xls"
Dim datumCrnt As Date
Dim datumStart As Date
Dim Formula As String
Dim InxWbook As Long
Dim InxWsheet As Long
Dim PathCopy As String
Dim PathCreate As String
Dim PosLastSquare As Long
Dim PosLastQuote As Long
Dim WbookCopy As Workbook
Dim WbookCopyWasClosed As Boolean
Dim WbookCreate As Workbook
Dim WbookThis As Workbook
Dim zadany_mesiac As Long
Set WbookThis = ThisWorkbook
' These set the paths for the template workbook and the workbook to be
' created to that for the workbook containing the macro. Change as
' required.
PathCopy = WbookThis.Path
PathCreate = WbookThis.Path
' Check for template workbook being open
WbookCopyWasClosed = True
For InxWbook = 1 To Workbooks.Count
If Workbooks(InxWbook).Name = WbookCopyName Then
WbookCopyWasClosed = False
Set WbookCopy = Workbooks(InxWbook)
Exit For
End If
Next
If WbookCopyWasClosed Then
' Template workbook is not open so open it
Set WbookCopy = Workbooks.Open(PathCopy & "\" & WbookCopyName, True)
End If
' Create an empty workbook
Set WbookCreate = Workbooks.Add
' WbookCreate is now the active workbook
' Get the month of the current year for which workbook is to be created
zadany_mesiac = 13
While (zadany_mesiac < 1) Or (zadany_mesiac > 12)
zadany_mesiac = Val(InputBox("Numeric month?"))
If zadany_mesiac = 0 Then Exit Sub
Wend
'Calculate first day of target month
datumStart = DateSerial(Year(Now()), zadany_mesiac, 1)
datumCrnt = datumStart
' Loop until datumCrnt is within the next month
Do While Month(datumCrnt) = Month(datumStart)
' Copy template worksheet from template workbook and name for day
WbookCopy.Worksheets("Sheet1").Copy _
After:=WbookCreate.Worksheets(Worksheets.Count)
With ActiveSheet
' In original code, I had "dd.MMM.yyyy" but I believe this should have
' been "d.MMM". However, I have changed to just "d" because with the
' TabRatio set to .7 all the tab names are visible. You can change this
' easily to your preferred value.
.Name = Format((datumCrnt), "d")
Formula = .Range("C3").Formula
PosLastSquare = InStrRev(Formula, "]")
PosLastQuote = InStrRev(Formula, "'")
If PosLastSquare <> 0 And PosLastQuote <> 0 And _
PosLastQuote > PosLastSquare Then
' Sheet name is bracketed by PosLastSquare and posLastQuote
' Replace sheet name from template with one required for this sheet
Formula = Mid(Formula, 1, PosLastSquare) & Format((datumCrnt), "d.MMM") & _
Mid(Formula, PosLastQuote)
.Range("C3").Formula = Formula
End If
End With
datumCrnt = DateAdd("d", 1, datumCrnt)
Loop
' Delete default worksheet
With WbookCreate
' The default sheets are at the beginning of the list
Do While Left(.Worksheets(1).Name, 5) = "Sheet"
Application.DisplayAlerts = False ' Surpress "Are you sure" message
.Worksheets(1).Delete
Application.DisplayAlerts = True
Loop
.Worksheets(1).Activate
End With
ActiveWindow.TabRatio = 0.7
WbookCreate.SaveAs PathCreate & "\" & Format(datumStart, "yyyy mmm")
If WbookCopyWasClosed Then
' Template workbook was not open so close
WbookCopy.Close SaveChanges:=False
End If
End Sub

Copy data from closed workbook based on variable user defined path

I have exhausted my search capabilities looking for a solution to this. Here is an outline of what I would like to do:
User opens macro-enabled Excel file
Immediate prompt displays for user to enter or select file path of desired workbooks. They will need to select two files, and the file names may not be consistent
After entering the file locations, the first worksheet from the first file selection will be copied to the first worksheet of the macro-enabled workbook, and the first worksheet of the second file selection will be copied to the second worksheet of the macro-enabled workbook.
I've come across some references to ADO, but I am really not familiar with that yet.
Edit: I have found a code to import data from a closed file. I will need to tweak the range to return the variable results.
Private Function GetValue(path, file, sheet, ref)
path = "C:\Users\crathbun\Desktop"
file = "test.xlsx"
sheet = "Sheet1"
ref = "A1:R30"
' Retrieves a value from a closed workbook
Dim arg As String
' Make sure the file exists
If Right(path, 1) <> "\" Then path = path & "\"
If Dir(path & file) = "" Then
GetValue = "File Not Found"
Exit Function
End If
' Create the argument
arg = "'" & path & "[" & file & "]" & sheet & "'!" & _
Range(ref).Range("A1").Address(, , xlR1C1)
' Execute an XLM macro
GetValue = ExecuteExcel4Macro(arg)
End Function
Sub TestGetValue()
path = "C:\Users\crathbun\Desktop"
file = "test"
sheet = "Sheet1"
Application.ScreenUpdating = False
For r = 1 To 30
For C = 1 To 18
a = Cells(r, C).Address
Cells(r, C) = GetValue(path, file, sheet, a)
Next C
Next r
Application.ScreenUpdating = True
End Sub
Now, I need a command button or userform that will immediately prompt the user to define a file path, and import the data from that file.
I don't mind if the files are opened during process. I just didn't want the user to have to open the files individually. I just need them to be able to select or navigate to the desired files
Here is a basic code. This code asks user to select two files and then imports the relevant sheet into the current workbook. I have given two options. Take your pick :)
TRIED AND TESTED
OPTION 1 (Import the Sheets directly instead of copying into sheet1 and 2)
Option Explicit
Sub Sample()
Dim wb1 As Workbook, wb2 As Workbook
Dim Ret1, Ret2
Set wb1 = ActiveWorkbook
'~~> Get the first File
Ret1 = Application.GetOpenFilename("Excel Files (*.xls*), *.xls*", _
, "Please select first file")
If Ret1 = False Then Exit Sub
'~~> Get the 2nd File
Ret2 = Application.GetOpenFilename("Excel Files (*.xls*), *.xls*", _
, "Please select Second file")
If Ret2 = False Then Exit Sub
Set wb2 = Workbooks.Open(Ret1)
wb2.Sheets(1).Copy Before:=wb1.Sheets(1)
ActiveSheet.Name = "Blah Blah 1"
wb2.Close SaveChanges:=False
Set wb2 = Workbooks.Open(Ret2)
wb2.Sheets(1).Copy After:=wb1.Sheets(1)
ActiveSheet.Name = "Blah Blah 2"
wb2.Close SaveChanges:=False
Set wb2 = Nothing
Set wb1 = Nothing
End Sub
OPTION 2 (Import the Sheets contents into sheet1 and 2)
Option Explicit
Sub Sample()
Dim wb1 As Workbook, wb2 As Workbook
Dim Ret1, Ret2
Set wb1 = ActiveWorkbook
'~~> Get the first File
Ret1 = Application.GetOpenFilename("Excel Files (*.xls*), *.xls*", _
, "Please select first file")
If Ret1 = False Then Exit Sub
'~~> Get the 2nd File
Ret2 = Application.GetOpenFilename("Excel Files (*.xls*), *.xls*", _
, "Please select Second file")
If Ret2 = False Then Exit Sub
Set wb2 = Workbooks.Open(Ret1)
wb2.Sheets(1).Cells.Copy wb1.Sheets(1).Cells
wb2.Close SaveChanges:=False
Set wb2 = Workbooks.Open(Ret2)
wb2.Sheets(1).Cells.Copy wb1.Sheets(2).Cells
wb2.Close SaveChanges:=False
Set wb2 = Nothing
Set wb1 = Nothing
End Sub
The function below reads data from a closed Excel file and returns the result in an array. It loses formatting, formulas etc. You might want to call the isArrayEmpty function (at the bottom) in your main code to test that the function returned something.
Public Function getDataFromClosedExcelFile(parExcelFileName As String, parSheetName As String) As Variant
'see http://www.ozgrid.com/forum/showthread.php?t=19559
'returns an array (1 to nRows, 1 to nCols) which should be tested with isArrayEmpty in the calling function
Dim locConnection As New ADODB.Connection
Dim locRst As New ADODB.Recordset
Dim locConnectionString As String
Dim locQuery As String
Dim locCols As Variant
Dim locResult As Variant
Dim i As Long
Dim j As Long
On Error GoTo error_handler
locConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;" _
& "Data Source=" & parExcelFileName & ";" _
& "Extended Properties=""Excel 8.0;HDR=YES"";"
locQuery = "SELECT * FROM [" & parSheetName & "$]"
locConnection.Open ConnectionString:=locConnectionString
locRst.Open Source:=locQuery, ActiveConnection:=locConnection
If locRst.EOF Then 'Empty sheet or only one row
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'''''' FIX: an empty sheet returns "F1"
'''''' http://support.microsoft.com/kb/318373
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
If locRst.Fields.Count = 1 And locRst.Fields(0).Name = "F1" Then Exit Function 'Empty sheet
ReDim locResult(1 To 1, 1 To locRst.Fields.Count) As Variant
For i = 1 To locRst.Fields.Count
locResult(1, i) = locRst.Fields(i - 1).Name
Next i
Else
locCols = locRst.GetRows
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'''''' FIX: an empty sheet returns "F1"
'''''' http://support.microsoft.com/kb/318373
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
If locRst.Fields.Count = 1 And locRst.Fields(0).Name = "F1" And UBound(locCols, 2) = 0 And locCols(0, 0) = "" Then Exit Function 'Empty sheet
ReDim locResult(1 To UBound(locCols, 2) + 2, 1 To UBound(locCols, 1) + 1) As Variant
If locRst.Fields.Count <> UBound(locCols, 1) + 1 Then Exit Function 'Not supposed to happen
For j = 1 To UBound(locResult, 2)
locResult(1, j) = locRst.Fields(j - 1).Name
Next j
For i = 2 To UBound(locResult, 1)
For j = 1 To UBound(locResult, 2)
locResult(i, j) = locCols(j - 1, i - 2)
Next j
Next i
End If
locRst.Close
locConnection.Close
Set locRst = Nothing
Set locConnection = Nothing
getDataFromClosedExcelFile = locResult
Exit Function
error_handler:
'Wrong file name, sheet name, or other errors...
'Errors (#N/A, etc) on the sheet should be replaced by Null but should not raise an error
If locRst.State = ADODB.adStateOpen Then locRst.Close
If locConnection.State = ADODB.adStateOpen Then locConnection.Close
Set locRst = Nothing
Set locConnection = Nothing
End Function
Public Function isArrayEmpty(parArray As Variant) As Boolean
'Returns false if not an array or dynamic array that has not been initialised (ReDim) or has been erased (Erase)
If IsArray(parArray) = False Then isArrayEmpty = True
On Error Resume Next
If UBound(parArray) < LBound(parArray) Then isArrayEmpty = True: Exit Function Else: isArrayEmpty = False
End Function
Sample use:
Sub test()
Dim data As Variant
data = getDataFromClosedExcelFile("myFile.xls", "Sheet1")
If Not isArrayEmpty(data) Then
'Copies content on active sheet
ActiveSheet.Cells(1,1).Resize(UBound(data,1), UBound(data,2)) = data
End If
End Sub