Long time listener, first time caller.
Anyway, I could use a bit of help. I have a macro that adds Text Boxes, and names them "Fig Num " & ActiveSheet.Shapes.count.
Once all of these text boxes are spread through out the Workbook, I would like to rename all shapes with the name "Fig Num*", or at least the text inside them, to go in order from first page to last, top to bottom, and left to right.
Currently, my code will rename the the text boxes based on seniority. In other words, if I added a text box and it was labeled "Fig Num 3", it would still be named "Fig Num 3" whether it was on the first page or last page.
enter code here
Sub Loop_Shape_Name()
Dim sht As Worksheet
Dim shp As Shape
Dim i As Integer
Dim Str As String
i = 1
For Each sht In ActiveWorkbook.Worksheets
For Each shp In sht.Shapes
If InStr(shp.Name, "Fig Num ") > 0 Then
sht.Activate
shp.Select
shp.Name = "Fig Num"
End If
Next shp
For Each shp In sht.Shapes
If InStr(shp.Name, "Fig Num") > 0 Then
sht.Activate
shp.Select
shp.Name = "Fig Num " & i
Selection.ShapeRange(1).TextFrame2.TextRange.Characters.Text = _
"FIG " & i
i = i + 1
End If
Next shp
Next sht
End Sub
---
I have a workbook example, but I'm not sure how to load it up, this being my first time and all.
Edit:
I have found a code that will do what I'm looking for, however it's a bit clunky. I also need a good way to find the last row on the sheet that contains a shape. Since the shape names are based on creation, if I insert a shape in row 35 and use the shape.count. featured below, it will skip all shapes after row 35 unless I add additional rows that bog down the code.
Most Recent Code (loops through grouped shapes):
Private Sub Rename_FigNum2()
'Dimension variables and data types
Dim sht As Worksheet
Dim shp As Shape
Dim subshp As Shape
Dim i As Integer
Dim str As String
Dim row As Long
Dim col As Long
Dim NextRow As Long
Dim NextRow1 As Long
Dim NextCol As Long
Dim rangex As Range
Dim LR As Long
i = 1
'Iterate through all worksheets in active workbook
For Each sht In ActiveWorkbook.Worksheets
If sht.Visible = xlSheetVisible Then
LR = Range("A1").SpecialCells(xlCellTypeLastCell).row + 200
If sht.Shapes.Count > 0 Then
With sht
NextRow1 = .Shapes(.Shapes.Count).BottomRightCell.row + 200
'NextCol = .Shapes(.Shapes.Count).BottomRightCell.Column + 10
End With
If LR > NextRow1 Then
NextRow = LR
Else
NextRow = NextRow1
End If
End If
NextCol = 15
Set rangex = sht.Range("A1", sht.Cells(NextRow, NextCol))
For row = 1 To rangex.Rows.Count
For col = 1 To rangex.Columns.Count
For Each shp In sht.Shapes
If shp.Type = msoGroup Then
For Each subshp In shp.GroupItems
If Not Intersect(sht.Cells(row, col), subshp.TopLeftCell) Is Nothing Then
If InStr(subshp.Name, "Fig Num") > 0 Then
subshp.Name = "Fig Num " & i
subshp.TextFrame2.TextRange.Characters.Text = _
"FIG " & i
i = i + 1
End If
End If
Next subshp
Else
If Not Intersect(sht.Cells(row, col), shp.TopLeftCell) Is Nothing Then
If InStr(shp.Name, "Fig Num ") > 0 Then
shp.Name = "Fig Num " & i
shp.TextFrame2.TextRange.Characters.Text = _
"FIG " & i
i = i + 1
End If
End If
End If
Next shp
Next col
Next row
End If
Next sht
End Sub
Example of Workbook:
Rename Text Boxes
To use the ArrayList, you have to have .NET Framework 3.5 SP1 installed, even if you already have a later version (e.g. 4.7) installed.
It is assumed that each text box is an ActiveX control (not a Form control) and has a (per worksheet) unique top-left cell.
Option Explicit
Sub RenameTextBoxes()
Const oTypeName As String = "TextBox" ' OLEObject Type Name
Const fPattern As String = "Fig Num " ' Find Pattern (Unsorted)
Const tPattern As String = "Dummy" ' Temporary Pattern
Const nPattern As String = "Fig Num " ' New Pattern (Sorted)
Const ByRows As Boolean = False
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim arl As Object: Set arl = CreateObject("System.Collections.ArrayList")
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim ws As Worksheet
Dim ole As OLEObject
Dim Key As Variant
Dim n As Long
Dim r As Long
Dim Coord As Double
Dim tName As String
For Each ws In wb.Worksheets
arl.Clear
dict.RemoveAll
For Each ole In ws.OLEObjects
If TypeName(ole.Object) = oTypeName Then
If InStr(1, ole.Name, fPattern, vbTextCompare) = 1 Then
n = n + 1
If ByRows Then
Coord = GetNumericByRows(ole.TopLeftCell)
Else
Coord = GetNumericByColumns(ole.TopLeftCell)
End If
arl.Add Coord
tName = tPattern & n
ole.Name = tName
dict(Coord) = tName
End If
End If
Next ole
arl.Sort
For Each Key In arl
r = r + 1
ws.OLEObjects(dict(Key)).Name = nPattern & r
'Debug.Print nPattern & r, Key, dict(Key)
Next Key
Next ws
End Sub
Function GetNumericByColumns( _
ByVal OneCellRange As Range) _
As Double
If OneCellRange Is Nothing Then Exit Function
With OneCellRange.Cells(1)
GetNumericByColumns = Val(.Column & "." & Format(.Row, "000000#"))
End With
End Function
Function GetNumericByRows( _
ByVal OneCellRange As Range) _
As Double
If OneCellRange Is Nothing Then Exit Function
With OneCellRange.Cells(1)
GetNumericByRows = Val(.Row & "." & Format(.Column, "0000#"))
End With
End Function
' Modify the range address to see what the 'GetNumeric' functions are all about.
Sub GetNumericTEST()
Dim cCell As Range: Set cCell = Sheet1.Range("XFD1048576")
Debug.Print GetNumericByColumns(cCell)
Debug.Print GetNumericByRows(cCell)
End Sub
Related
How to add a worksheet in excel with VBA after a specific sheetname held by variable?
I tried:
Set sh = wb.Worksheets.Add(After:=wb.Sheets(wsPattern & CStr(n)))
The previous incremented sheetname is stored in "wsPattern & CStr(n)", The new sheetname increments up properly from another statement and variable, but the add after fails with the above syntax. I'm getting an out of range error at this line.
The code fully executes using this statement, but adds any newly created sheets from any given series at the end of all sheets:
Set sh = wb.Worksheets.Add(After:=wb.Sheets(wb.Sheets.Count))
As the workbook has 4 series of sheet names now (e.g. Test1, logistic1, Equip1, Veh1, etc.) that are incremented up as they are added, the next incremented sheet for a given series needs to be added to the end of that sheet name series (Equip2 should be after Equip1) and not at the end of all sheets.
Sub CreaIncWkshtEquip()
Const wsPattern As String = "Equip "
Dim wb As Workbook: Set wb = ThisWorkbook
Dim arr() As Long: ReDim arr(1 To wb.Sheets.Count)
Dim wsLen As Long: wsLen = Len(wsPattern)
Dim sh As Object
Dim cValue As Variant
Dim shName As String
Dim n As Long
For Each sh In wb.Sheets
shName = sh.Name
If StrComp(Left(shName, wsLen), wsPattern, vbTextCompare) = 0 Then
cValue = Right(shName, Len(shName) - wsLen)
If IsNumeric(cValue) Then
n = n + 1
arr(n) = CLng(cValue)
End If
End If
Next sh
If n = 0 Then
n = 1
Else
ReDim Preserve arr(1 To n)
For n = 1 To n
If IsError(Application.Match(n, arr, 0)) Then
Exit For
End If
Next n
End If
'adds to very end of workbook
'Set sh = wb.Worksheets.Add(After:=wb.Sheets(wb.Sheets.Count))
'Test-Add After Last Incremented Sheet-
Set sh = wb.Worksheets.Add(After:=wb.Sheets(wsPattern & CStr(n)))
sh.Name = wsPattern & CStr(n)
End Sub
Create a function
Sub Demo()
Dim s
s = AddSheet("SeriesName")
MsgBox s & " Added"
End Sub
Function AddSheet(sSeries As String) As String
Dim ws, s As String, i As Long, n As Long
With ThisWorkbook
' find last in series
For n = .Sheets.Count To 1 Step -1
s = .Sheets(n).Name
If s Like sSeries & "[1-9]*" Then
i = Mid(s, Len(sSeries) + 1)
Exit For
End If
Next
' not found add to end
If i = 0 Then
n = .Sheets.Count
End If
' increment series
s = sSeries & i + 1
.Sheets.Add after:=.Sheets(n)
.Sheets(n + 1).Name = s
End With
AddSheet = s
End Function
I need my loop to check for existing part numbers and only if there is no existing part number to add it to my table. If the part number already exists, to have a message box stating that it already exists. Its adding it to my table just fine, but will not give me the message box if there is already an existing part number.
Private Sub Add_Click()
Dim ws As Worksheet
Set ws = Sheet4
Dim X As Integer
Dim lastrow As Long
Dim PartColumnIndex As Integer
Dim DescriptionColumnIndex As Integer
Const Part = "CM ECP"
Const Description = "Material Description"
Dim PartNum As String
Dim MaterailDescription As String
Dim tbl As ListObject
Set tbl = ws.ListObjects("Master")
Dim newrow As ListRow
Set newrow = tbl.ListRows.Add
With ws
On Error Resume Next
Let PartColumnIndex = WorksheetFunction.Match(PartNum, .Rows(2), 0)
Let DescriptionColumnIndex = WorksheetFunction.Match(MaterialDecription, .Rows(2), 0)
Let lastrow = .Cells(.Rows.Count, PartColumnIndex).End(xlUp).Row
X = 3
Do
Let PartValue = .Cells(X, PartColumnIndex).Value
Let DecriptionColumnIndex = .Cells(X, DecriptionColumnIndex).Value
If TextBox1.Value = PartValue Then
MsgBox "Part Number " + TextBox1.Value + " already exists. Please try again or return to main screen."
ElseIf TextBox1.Value <> PartValue Then
With newrow
.Range(1) = TextBox1.Value
.Range(2) = TextBox2.Value
End With
ElseIf X < lastrow Then
X = X + 1
End If
Loop Until X > lastrow
End With
Scan all the rows in the table before deciding to add a new row or not, and always add Use Option Explicit to top of code to catch errors like DecriptionColumnIndex (no s).
Option Explicit
Sub Add_Click()
Const PART = "CM ECP"
Const DESCRIPTION = "Material Description"
Dim ws As Worksheet
Dim X As Integer, lastrow As Long
Dim PartColumnIndex As Integer, DescrColumnIndex As Integer
Dim PartNum As String, MaterialDescription As String
Dim tbl As ListObject, bExists As Boolean
Set ws = Sheet1
Set tbl = ws.ListObjects("Master")
With tbl
PartColumnIndex = .ListColumns(PART).Index
DescrColumnIndex = .ListColumns(DESCRIPTION).Index
PartNum = Trim(TextBox1.Value)
MaterialDescription = Trim(TextBox2.Value)
' search
With .DataBodyRange
lastrow = .Rows.Count
For X = 1 To lastrow
If .Cells(X, PartColumnIndex).Value = PartNum Then
bExists = True
Exit For
End If
Next
End With
' result
If bExists = True Then
MsgBox "Part Number `" & PartNum & "` already exists on Row " & X & vbLf & _
"Please try again or return to main screen.", vbExclamation
Else
With .ListRows.Add
.Range(, PartColumnIndex) = PartNum
.Range(, DescrColumnIndex) = MaterialDescription
End With
MsgBox "Part Number `" & PartNum & "` added", vbInformation
End If
End With
End Sub
I am populating ActiveX control labels with a subset of Excel data in VBA. My code previously worked for the entire Excel workbook, but once I changed my code to only reference a subset of the data, the incorrect data is being entered.
Here is a snapshot of example data. In my code, Column 6= CY and Column 7 = FY. The code is currently populating my labels with the headers of Column 6 and 7 rather than the values of 'active' or 'merged' projects.
As mentioned, I am not receiving any error messages, but the correct data is not being added to my ActiveX labels. FYI... In line 31 Code1 is the name of an ActiveX label.
Private Sub CommandButton1_Click()
Dim objExcel As Excel.Application
Dim exWB As Excel.Workbook
Dim rng As Excel.Range, m, rw As Excel.Range
Dim num, TableNo, seq As Integer
Dim ctl As MSForms.Label
Dim ils As Word.InlineShape
Dim rngrow As Excel.Range
Dim active As Excel.Range
Set objExcel = New Excel.Application
TableNo = ActiveDocument.Tables.Count
num = 3
seq = 1
Set exWB = objExcel.Workbooks.Open("O:\Documents\"Database.csv")
Set rng = exWB.Sheets("Sheet1").Cells
''''Select active projects as subset
For Each rngrow In rng.Range("A1:L144")
If rngrow.Columns(8).value = "Active" Or rngrow.Columns(8).value = "Merged" Then
If active Is Nothing Then
Set active = rngrow
Else
Set active = Union(active, rngrow)
End If
End If
Next rngrow
m = objExcel.Match(ActiveDocument.Code1.Caption, active.Columns(3), 0)
'' Now, create all ActiveX FY labels and populate with FY Use
Do
Set ils = ActiveDocument.Tables(num).cell(6, 2).Range.InlineShapes.AddOLEControl(ClassType:="Forms.Label.1")
Set ctl = ils.OLEFormat.Object
ctl.Name = "FY" & seq
If Not IsError(m) Then
Set rw = rng.Rows(m)
ctl.Caption = rw.Cells(7).value
Else
MsgBox "No match found"
End If
seq = seq + 1
num = num + 1
Loop Until num = TableNo + 1
'' Now, create all ActiveX CY labels and populate with CY
num = 3
seq = 1
Do
Set ils = ActiveDocument.Tables(num).cell(7, 2).Range.InlineShapes.AddOLEControl(ClassType:="Forms.Label.1")
Set ctl = ils.OLEFormat.Object
ctl.Name = "CY" & seq
If Not IsError(m) Then
Set rw = rng.Rows(m)
ctl.Caption = rw.Cells(6).value
Else
MsgBox "No match found"
End If
seq = seq + 1
num = num + 1
Loop Until num = TableNo + 1
Set exWB = Nothing
End Sub
Link to my previous question below:
Using Excel data to create Word Doc caption labels in VBA
This:
For Each rngrow In rng.Range("A1:L144")
will be interpreted as
For Each rngrow In rng.Range("A1:L144").Cells
so your loop will be A1, B1, C1, ... L1 then A2, B2 etc.
It seems like you meant it to be:
For Each rngrow In rng.Range("A1:L144").Rows
so rngRow will be A1:L1, then A2:L2, etc.
EDIT - You can't refer to active using something like MsgBox(active.Range ("A2")), since it's a multi-area range.
Try this for example -
For Each rw in active.Rows
debug.print "Row:" & rw.Row, rw.cells(8).value
Next rw
EDIT2: try this instead. Untested but I think it should work OK
Private Sub CommandButton1_Click()
Dim objExcel As Excel.Application
Dim exWB As Excel.Workbook
Dim data, r As Long, resRow As Long, seq As Long, num As Long
Dim doc As Document
'get the Excel data as a 2D array
Set objExcel = New Excel.Application
Set exWB = objExcel.Workbooks.Open("O:\Documents\Database.csv")
data = exWB.Sheets("Sheet1").Range("A1:L144").Value '>> 2D array
exWB.Close False
objExcel.Quit
resRow = 0
'find the first matching row, if any
For r = 1 To UBound(data, 1)
If (data(r, 8) = "Active" Or data(r, 8) = "Merged") And _
data(r, 3) = doc.Code1.Caption Then
resRow = r 'this is the row we want
Exit Sub 'done looking
End If
Next r
Set doc = ActiveDocument
seq = 1
For num = 3 To doc.Tables.Count
With doc.Tables(num)
AddLabel .Cell(6, 2), "FY" & seq, IIf(resRow > 0, data(resRow, 7), "Not found")
AddLabel .Cell(7, 2), "CY" & seq, IIf(resRow > 0, data(resRow, 6), "Not found")
End With
seq = seq + 1
Next num
End Sub
'add a label to a cell, set its name and caption
Sub AddLabel(theCell As Cell, theName As String, theCaption As String)
Dim ils As InlineShape, ctl As MSForms.Label
Set ils = theCell.Range.InlineShapes.AddOLEControl(ClassType:="Forms.Label.1")
Set ctl = ils.OLEFormat.Object
ctl.Name = theName
ctl.Caption = theCaption
End Sub
My brain is fried and this is easy points for the usual suspects. div is an array holding sheet names. I am looping through sheets in a master book and if one of the master sheets match one of the sheets in the div array, I want to transfer some data from master sheet to a sheet in thisworkbook.
In the event the sheet does not exist in thisworkbook, add one and name it after the master sheet. What's the most efficient way to do this? I feel like nested loops is a bad idea -_- A collection perhaps?
For i = 0 To UBound(div())
For Each s In book.Worksheets
wsName = Left(s.Name, 5)
If div(i) = wsName Then
If wsExists(wsName) Then
Set ws = ThisWorkbook.Worksheets(wsName)
Exit For
'Debug.Print "true " & ws.name
Else
Set ws = ThisWorkbook.Worksheets.Add
ws.Name = Left(s.Name, 5)
'Debug.Print "false " & ws.name
End If
end if
Next
With ws
.Columns(Start).Resize(, 2).Value = s.Columns("A:B").Value
.Columns(Start + label).Resize(, cols).Value = s.Columns(Start + label).Resize(, cols).Value
End With
Next
Do I even need to check if sheet exists? Code stolen from Tim.
Function wsExists(sName As String) As Boolean
Dim sht As Worksheet
On Error Resume Next
Set sht = ThisWorkbook.Sheets(sName)
On Error GoTo 0
wsExists = Not sht Is Nothing
End Function
Edit: I am calling the loop from a separate routine.
Call drop(thisWB, thisRange, ccArr)
where ccArr is
Dim ccArr() As Variant
ccArr = Array("30500", "30510", "30515", "30530", "30600", "30900", "40500")
The routine where above loop resides opens with
Sub drop(book As Workbook, cols As Integer, div As Variant, Optional startCol As Integer)
but I am getting a byref error trying to pass the array ;_;
Your nested loop is superfluous. You can check the sheet name from div directly against the workbook you want to check it against, then add it if needed.
See the code below, which also addresses the concerns in the edits to your OP. I modified the wsExists function to include a set reference to a particular workbook, which I think makes it more dynamic.
'assumes thisWB and thisRange set above
Dim ccArr() As String, sList As String
sList = "30500,30510,30515,30530,30600,30900,40500"
ccArr = Split(sList, ",")
drop thisWB, thisRange, ccArr 'assumes thisWb and thisRange are set already
' rest of code
'==================================================
Sub drop(book As Workbook, cols As Integer, div() As String, Optional startCol as Integer)
For i = 0 To UBound(div())
If wsExists(ThisWorkbook, div(i)) Then
Set ws = ThisWorkbook.Worksheets(div(i))
Exit For
'Debug.Print "true " & ws.name
Else
Set ws = ThisWorkbook.Worksheets.Add
ws.Name = div(i)
End If
'i think you need this here, otherwise, it will only work on the last worksheet in your loop
With ws
Dim s As Worksheet
Set s = book.Sheets(div(i))
.Columns(Start).Resize(, 2).Value = s.Columns("A:B").Value
.Columns(Start + Label).Resize(, cols).Value = s.Columns(Start + Label).Resize(, cols).Value
End With
Next
End Sub
Function wsExists(wb As Workbook, sName As String) As Boolean
Dim sht As Worksheet
On Error Resume Next
Set sht = wb.Sheets(sName)
On Error GoTo 0
wsExists = Not sht Is Nothing
End Function
Related to the re-sizing code:
This statement ws.Columns(1).Resize(, 2) translates to "2 million+ rows from column 1 and 2"
The solution you found works well but it's not dynamic (hard-coded last row)
This is how I'd setup the copy of columns:
Option Explicit
Public Sub copyCols()
Dim ws1 As Worksheet, ws2 As Worksheet, rng1 As Range, rng2 As Range
Dim cols As Long, lr As Long
Dim col1 As Long 'renamed from "Start" (VBA keyword - property)
Dim lbl As Long 'renamed from "label" (VBA keyword - Control object)
Set ws1 = Sheet1 'ws
Set ws2 = Sheet2 'book.Worksheets(wsName & "-F")
col1 = 1
cols = 2
lbl = 1
lr = ws2.Cells(ws2.UsedRange.Row + ws2.UsedRange.Rows.Count, "A").End(xlUp).Row
Set rng1 = ws1.Range(ws1.Cells(1, col1), ws1.Cells(lr, col1 + 1))
Set rng2 = ws2.Range("A1:B" & lr)
rng1.Value2 = rng2.Value2
Set rng1 = ws1.Range(ws1.Cells(1, col1 + lbl), ws1.Cells(lr, col1 + lbl + cols))
Set rng2 = ws2.Range(ws2.Cells(1, col1 + lbl), ws2.Cells(lr, col1 + lbl + cols))
rng1.Value2 = rng2.Value2
End Sub
I have an Excel file with around 70-80 columns. I need to get the min and max values for each columns. I also need the min and max values on Access. I wrote the code both for Access and Excel and the speed of the macro is very different in both. Both are a little different but very similar.
Here is the Excel code :
Public Sub MinAndMax()
Dim i As Long, j As Long
Dim usedTime As Double
usedTime = Timer
Dim nbCol As Long, nbRow As Long
nbCol = Range("A1").End(xlToRight).Column
nbRow = Range("A1").End(xlDown).Row
Dim min As Double, max As Double
Dim temp As Variant
'First column is for the table key
'First row is for table header
For j = 2 To nbCol
min = Cells(2, j)
max = Cells(2, j)
For i = 3 To nbRow
temp = Cells(i, j)
If IsNumeric(temp) Then
If temp > max Then max = temp
If temp < max Then min = temp
End If
Next i
Next j
MsgBox "Time : " Round(Timer - duree) " seconds."
End Sub
This takes approximatively 5 seconds on Excel.
On Access, it's now a function returning an array, with an option indicating if you want the array with the max or min for each columns. So in order to get both min and max, I have to call it twice.
Private Function GetMinAndMax_Access(Optional ByVal getMin As Boolean = False) As Double()
Dim Path As String
Path = "C:\File.xlsx"
Dim appExcel As Excel.Application
Set appExcel = CreateObject("Excel.Application")
appExcel.ScreenUpdating = False
Dim wb As Workbook
Set wb = appExcel.Workbooks.Open(Path)
Dim ws As Worksheet
Set ws = wb.Worksheets(1)
Dim nbCol As Long, nbRow As Long
nbCol = ws.Range("A1").End(xlToRight).Column
nbRow = ws.Range("A1").End(xlDown).Row
ReDim extremum(2 To nbCol) As Double
Dim temp As Variant
Dim i As Long, j As Long 'Again, data start at row 2, column 2
For j = 2 To nbCol
extremum(j) = ws.Cells(2, j)
For i = 3 To nbRow
temp = ws.Cells(i, j)
If IsNumeric(temp) Then
If getMin Then
If temp < extremum(j) Then extremum(j) = temp
Else
If temp > extremum(j) Then extremum(j) = temp
End If
End If
Next i
Next j
GetMinAndMax_Access = extremum
appExcel.ScreenUpdating = True
wb.Close SaveChanges:=False
appExcel.Quit
End Function
This took precisely 29 minutes to perform on the same dataset. Note that I called the function twice, once for min values and once for max ones.
Any idea why the speeds are so different between Access and Excel, and what can be done about that ? It seems really weird to me !
Seems like a bit of a long winded way to get the minimum and maximum numeric values from the columns. The worksheet functions MIN and MAX are pretty fast at doing it so:
In Excel:
Public Sub MinAndMax()
Dim rLastCell As Range
Dim x As Long
Dim colMinMax As Collection
Set rLastCell = Cells.Find(What:="*", After:=Cells(1, 1), SearchDirection:=xlPrevious)
If Not rLastCell Is Nothing Then
Set colMinMax = New Collection
For x = 1 To rLastCell.Column
colMinMax.Add Array(Application.WorksheetFunction.min(Columns(x)), _
Application.WorksheetFunction.max(Columns(x)))
Next x
End If
End Sub
In Access (with late binding so no need to set references):
Sub ToUse()
Dim MyCol As Collection
Set MyCol = New Collection
Set MyCol = GetMinMax("C:\Documents and Settings\crladmin.ADMINNOT\My Documents\MinMax.xlsm", "Sheet1")
End Sub
Private Function GetMinMax(sPath As String, sSheet As String) As Collection
Dim oXL As Object
Dim oWB As Object
Dim oWS As Object
Dim rLastCell As Object
Dim x As Long
Dim colMinMax As Collection
Set oXL = CreateXL
Set oWB = oXL.Workbooks.Open(sPath, False)
Set oWS = oWB.Worksheets(sSheet)
Set rLastCell = oWS.Cells.Find(What:="*", After:=oWS.Cells(1, 1), SearchDirection:=2) '2 = xlPrevious
If Not rLastCell Is Nothing Then
Set colMinMax = New Collection
For x = 1 To rLastCell.Column
colMinMax.Add Array(oXL.WorksheetFunction.min(oWS.Columns(x)), _
oXL.WorksheetFunction.max(oWS.Columns(x)))
Next x
End If
End Function
Public Function CreateXL(Optional bVisible As Boolean = True) As Object
Dim oTmpXL As Object
'''''''''''''''''''''''''''''''''''''''''''''''''''''
'Defer error trapping in case Excel is not running. '
'''''''''''''''''''''''''''''''''''''''''''''''''''''
On Error Resume Next
Set oTmpXL = GetObject(, "Excel.Application")
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
'If an error occurs then create an instance of Excel. '
'Reinstate error handling. '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
If Err.Number <> 0 Then
Err.Clear
On Error GoTo ERROR_HANDLER
Set oTmpXL = CreateObject("Excel.Application")
End If
oTmpXL.Visible = bVisible
Set CreateXL = oTmpXL
On Error GoTo 0
Exit Function
ERROR_HANDLER:
Select Case Err.Number
Case Else
MsgBox "Error " & Err.Number & vbCr & _
" (" & Err.Description & ") in procedure CreateXL."
Err.Clear
End Select
End Function
Both procedures will return collections with 2D arrays containing MIN & MAX values for each column:
Item 1(0) - 4
Item 1(1) - 98
Item 2(0) - 3
Item 2(1) - 15