VBA execution speed differs between Excel and Access - excel

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

Related

Loop Through Shapes in a Workbook and rename based on Location

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

Excel freezing when importing many rows

I created a macro to import multiple spreadsheet into on master report. I tested it with some small files without any issues. But when I am trying to import a file with more then just a couple of rows, excel keeps freezing.
Sub openFile(ByRef file As String)
Dim wbMaster As Workbook: Set wbMaster = ThisWorkbook
Dim wsMaster As Worksheet: Set wsMaster = wbMaster.Sheets("Report")
Dim tbMaster As ListObject: Set tbMaster = wsMaster.ListObjects("Report")
Dim hdrMaster As ListColumn
Dim rowMaster As ListRow
Dim wbSlave As Workbook: Set wbSlave = Workbooks.Open(Application.ActiveWorkbook.path & "\" & file)
Dim wsSlave As Worksheet
Dim rowSlave As Range
Dim cellSlave As Range
Dim hdrSlave() As Variant
For Each wsSlave In wbSlave.Worksheets
For Each rowSlave In wsSlave.Rows
If rowSlave.Row <= 1 Then
For Each cellSlave In rowSlave.Cells
If Not IsEmpty(cellSlave) Then
Set hdrMaster = Nothing
On Error Resume Next
Set hdrMaster = tbMaster.ListColumns(cellSlave.Text)
On Error GoTo 0
If hdrMaster Is Nothing Then
Set hdrMaster = tbMaster.ListColumns.Add
hdrMaster.Name = cellSlave.Text
End If
ReDim Preserve hdrSlave(cellSlave.Column)
hdrSlave(cellSlave.Column) = cellSlave.Text
Else
Exit For
End If
Next cellSlave
Else
If Not IsEmpty(rowSlave.Cells(1)) Then
Set rowMaster = tbMaster.ListRows.Add
rowMaster.Range(tbMaster.ListColumns("File").Index) = file
For Each cellSlave In rowSlave.Cells
If Not IsEmpty(cellSlave) Then
rowMaster.Range(tbMaster.ListColumns(hdrSlave(cellSlave.Column)).Index) = cellSlave.Text
Else
Exit For
End If
Next cellSlave
Else
Exit For
End If
End If
Next rowSlave
Next wsSlave
tbMaster.Range.Columns.AutoFit
wbMaster.Save
wbSlave.Close (False)
End Sub
I am also turning off ScreenUpdating before calling the sub.
Here's what I mean.
I've pulled out the process of appending a range onto a Table/Listobject into AddRangeintoTable - you would call that from your main code.
I have a stub method Tester as an example of how it would be called.
Note the comment at the bottom of the main code about whether or not you'll need to resize the table after adding the new content - there's a setting in Options to control that.
Sub Tester()
AddRangeIntoTable Range("A4").CurrentRegion, _
ActiveSheet.ListObjects("Table2"), _
True
End Sub
'append a Range onto a Table/Listobject, optionally inserting
' any columns not found in the Table
Sub AddRangeIntoTable(FromRange As Range, ToTable As ListObject, _
Optional AppendNewCols As Boolean = False)
Dim c As Range, lc As ListColumn, data, rw, col, newData(), hdr
Dim dictColPos, rngHdrs As Range, i As Long, numRows As Long, numCols As Long
data = FromRange.Value 'get all new data as array
Set dictColPos = CreateObject("scripting.dictionary") 'for mapping columns
'map headers and (optionally) add any necessary headers not already present
For col = 1 To UBound(data, 2)
hdr = data(1, col)
Set lc = Nothing
On Error Resume Next
Set lc = ToTable.ListColumns(hdr)
On Error GoTo 0
If lc Is Nothing And AppendNewCols Then 'add mising column(s)?
Set lc = ToTable.ListColumns.Add
lc.Name = hdr
End If
If Not lc Is Nothing Then
dictColPos(hdr) = lc.Index 'map header name to column index position
End If
Next col
'size array for data to append to listobject and fill it
numRows = UBound(data, 1) - 1
numCols = ToTable.ListColumns.Count
ReDim newData(1 To numRows, 1 To numCols)
For rw = 2 To UBound(data, 1)
For col = 1 To UBound(data, 2)
If dictColPos.exists(data(1, col)) Then
newData(rw - 1, dictColPos(data(1, col))) = data(rw, col)
End If
Next col
Next rw
With ToTable
With .DataBodyRange
.Rows(.Rows.Count).Cells(1).Offset(1, 0) _
.Resize(numRows, numCols).Value = newData 'add the new data
End With
' Excel options >> Proofing >> Autocorrect options >> Autoformat as you type
' >> "Include new rows and columns in table"
If Not Application.AutoCorrect.AutoExpandListRange Then
.Resize ToTable.Range.Resize(.Range.Rows.Count + numRows)
End If
.Range.Columns.AutoFit
End With
End Sub

Visual Basic Running QBFC13Lib, SDK, Running GeneralSummaryReportQuery, on Response, can't get the column names as text

I'm using Visual Basic to export a General Summary Report To excel using Quickbooks SDK QBFC13Lib.
On the response, I can't get the column titles. I need the column titles as text to send to an excel cell.
The code for the column titles is at 'Get value of NumColTitleRows
I have a text box MsgBox(ColTitle.value) that I was trying to show the column titles so I could see I was getting them.
enter code here
Public Sub WalkReportRet(ReportRet As IReportRet)
If (ReportRet Is Nothing) Then
Exit Sub
End If
'Go through all the elements of IReportRet
'Get value of ReportTitle
Dim ReportTitle10687 As String
ReportTitle10687 = ReportRet.ReportTitle.GetValue()
'Get value of ReportSubtitle
Dim ReportSubtitle10688 As String
ReportSubtitle10688 = ReportRet.ReportSubtitle.GetValue()
'Get value of NumRows
Dim NumRows10690 As Integer
NumRows10690 = ReportRet.NumRows.GetValue()
'Get value of NumColumns
Dim NumColumns10691 As Integer
NumColumns10691 = ReportRet.NumColumns.GetValue()
'SEND TO EXCEL
Dim appXL As Excel.Application
Dim wbXl As Excel.Workbook
Dim shXL As Excel.Worksheet
Dim raXL As Excel.Range
' Start Excel and get Application object.
appXL = CreateObject("Excel.Application")
appXL.Visible = True
' Add a new workbook.
wbXl = appXL.Workbooks.Add
shXL = wbXl.ActiveSheet
'Create an array to set multiple values at once.
Dim qbdata(200, 10) As String
qbdata(0, 0) = ReportTitle10687
qbdata(0, 1) = ReportSubtitle10688
qbdata(0, 2) = NumRows10690.ToString
qbdata(0, 3) = NumColumns10691.ToString
'Get value of NumColTitleRows
Dim NumColTitleRows10692 As Integer
NumColTitleRows10692 = ReportRet.NumColTitleRows.GetValue()
If (Not ReportRet.ColDescList Is Nothing) Then
Dim i10693 As Integer
For i10693 = 0 To ReportRet.ColDescList.Count - 1
Dim ColDesc As IColDesc
ColDesc = ReportRet.ColDescList.GetAt(i10693)
If (Not ColDesc.ColTitleList Is Nothing) Then
Dim i10694 As Integer
For i10694 = 0 To ColDesc.ColTitleList.Count - 1
Dim ColTitle As IColTitle
ColTitle = ColDesc.ColTitleList.GetAt(i10694)
MsgBox(ColTitle.value)
Next i10694
End If
'Get value of ColType
Dim ColType10695 As ENColType
ColType10695 = ColDesc.ColType.GetValue()
Next i10693
End If
If (Not ReportRet.ReportData Is Nothing) Then
If (Not ReportRet.ReportData.ORReportDataList Is Nothing) Then
Dim i10696 As Integer
'this runs the rows
For i10696 = 0 To ReportRet.ReportData.ORReportDataList.Count - 1
Dim ORReportData10697 As IORReportData
ORReportData10697 = ReportRet.ReportData.ORReportDataList.GetAt(i10696)
If (Not ORReportData10697.DataRow Is Nothing) Then
If (Not ORReportData10697.DataRow Is Nothing) Then
If (Not ORReportData10697.DataRow.RowData Is Nothing) Then
End If
If (Not ORReportData10697.DataRow.ColDataList Is Nothing) Then
Dim i10698 As Integer
For i10698 = 0 To ORReportData10697.DataRow.ColDataList.Count - 1
Dim ColData As IColData
ColData = ORReportData10697.DataRow.ColDataList.GetAt(i10698)
qbdata(i10696 + 5, i10698) = ColData.value.GetValue.ToString
Next i10698
End If
End If
End If
If (Not ORReportData10697.TextRow Is Nothing) Then
If (Not ORReportData10697.TextRow Is Nothing) Then
qbdata(i10696 + 5, 0) = ORReportData10697.TextRow.value.GetValue.ToString
End If
End If
If (Not ORReportData10697.SubtotalRow Is Nothing) Then
If (Not ORReportData10697.SubtotalRow Is Nothing) Then
If (Not ORReportData10697.SubtotalRow.RowData Is Nothing) Then
End If
If (Not ORReportData10697.SubtotalRow.ColDataList Is Nothing) Then
Dim i10699 As Integer
For i10699 = 0 To ORReportData10697.SubtotalRow.ColDataList.Count - 1
Dim ColData As IColData
ColData = ORReportData10697.SubtotalRow.ColDataList.GetAt(i10699)
qbdata(i10696 + 5, i10699) = ColData.value.GetValue.ToString
Next i10699
End If
End If
End If
If (Not ORReportData10697.TotalRow Is Nothing) Then
If (Not ORReportData10697.TotalRow Is Nothing) Then
If (Not ORReportData10697.TotalRow.RowData Is Nothing) Then
End If
If (Not ORReportData10697.TotalRow.ColDataList Is Nothing) Then
Dim i10700 As Integer
For i10700 = 0 To ORReportData10697.TotalRow.ColDataList.Count - 1
Dim ColData As IColData
ColData = ORReportData10697.TotalRow.ColDataList.GetAt(i10700)
qbdata(i10696 + 5, i10700) = ColData.value.GetValue.ToString
Next i10700
End If
End If
End If
Next i10696
End If
End If
' Fill A2:B6 with an array of data
shXL.Range("A1", "K205").Value = qbdata
' Make sure Excel is visible and give the user control
' of Excel's lifetime.
appXL.Visible = True
appXL.UserControl = True
' Release object references.
raXL = Nothing
shXL = Nothing
wbXl = Nothing
appXL.Quit()
appXL = Nothing
End Sub
Got it! I had a null on the first item in the list, and wasn't handling it
enter code here
If (Not ReportRet.ColDescList Is Nothing) Then
Dim i10693 As Integer
For i10693 = 0 To ReportRet.ColDescList.Count - 1
Dim ColDesc As IColDesc
ColDesc = ReportRet.ColDescList.GetAt(i10693)
If (Not ColDesc.ColTitleList Is Nothing) Then
Dim i10694 As Integer
For i10694 = 0 To ColDesc.ColTitleList.Count - 1
Dim ColTitle As IColTitle
ColTitle = ColDesc.ColTitleList.GetAt(i10694)
If (Not ColTitle.value Is Nothing) Then
qbdata(i10694 + 2, i10693) = ColTitle.value.GetValue.ToString
End If
Next i10694
End If
Dim ColType10695 As ENColType
ColType10695 = ColDesc.ColType.GetValue()
Next i10693
End If
For reference, this should be universal code for visual studio visual basic to export a balance sheet previous year comparison from open company file into excel. A few small changes would change it to export a P&L. This code only supports up to 200 lines, it would have to be modified for a longer report. (the array expanded and the range in excel expanded).
example of Quickbooks report exported to excel
enter code here Private Sub RunChurchCashFlowReportToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles RunChurchCashFlowReportToolStripMenuItem.Click
DoGeneralSummaryReportBalanceSheetQuery()
DoGeneralSummaryReportProfitAndLossQuery()
' RunCustomSummaryReport()
End Sub
Public Sub DoGeneralSummaryReportBalanceSheetQuery()
Dim sessionBegun As Boolean
sessionBegun = False
Dim connectionOpen As Boolean
connectionOpen = False
Dim sessionManager As QBSessionManager
sessionManager = Nothing
Try
'Create the session Manager object
sessionManager = New QBSessionManager
'Create the message set request object to hold our request
Dim requestMsgSet As IMsgSetRequest
requestMsgSet = sessionManager.CreateMsgSetRequest("US", 13, 0)
requestMsgSet.Attributes.OnError = ENRqOnError.roeContinue
BuildGeneralSummaryReportBalanceSheetQueryRq(requestMsgSet)
'Connect to QuickBooks and begin a session
sessionManager.OpenConnection("", "ChurchReports")
connectionOpen = True
sessionManager.BeginSession("", ENOpenMode.omDontCare)
sessionBegun = True
'Send the request and get the response from QuickBooks
Dim responseMsgSet As IMsgSetResponse
responseMsgSet = sessionManager.DoRequests(requestMsgSet)
'End the session and close the connection to QuickBooks
sessionManager.EndSession()
sessionBegun = False
sessionManager.CloseConnection()
connectionOpen = False
WalkGeneralSummaryReportQueryRs(responseMsgSet)
Catch e As Exception
MessageBox.Show(e.Message, "Error")
If (sessionBegun) Then
sessionManager.EndSession()
End If
If (connectionOpen) Then
sessionManager.CloseConnection()
End If
End Try
End Sub
Public Sub WalkGeneralSummaryReportQueryRs(responseMsgSet As IMsgSetResponse)
If (responseMsgSet Is Nothing) Then
Exit Sub
End If
Dim responseList As IResponseList
responseList = responseMsgSet.ResponseList
If (responseList Is Nothing) Then
Exit Sub
End If
'if we sent only one request, there is only one response, we'll walk the list for this sample
For j = 0 To responseList.Count - 1
Dim response As IResponse
response = responseList.GetAt(j)
'check the status code of the response, 0=ok, >0 is warning
If (response.StatusCode >= 0) Then
'the request-specific response is in the details, make sure we have some
If (Not response.Detail Is Nothing) Then
'make sure the response is the type we're expecting
Dim responseType As ENResponseType
responseType = CType(response.Type.GetValue(), ENResponseType)
If (responseType = ENResponseType.rtGeneralSummaryReportQueryRs) Then
'upcast to more specific type here, this is safe because we checked with response.Type check above
Dim ReportRet As IReportRet
ReportRet = CType(response.Detail, IReportRet)
WalkReportRet(ReportRet)
End If
End If
End If
Next j
End Sub
Public Sub WalkReportRet(ReportRet As IReportRet)
If (ReportRet Is Nothing) Then
Exit Sub
End If
'Go through all the elements of IReportRet
'Get value of ReportTitle
Dim ReportTitle10687 As String
ReportTitle10687 = ReportRet.ReportTitle.GetValue()
'Get value of ReportSubtitle
Dim ReportSubtitle10688 As String
ReportSubtitle10688 = ReportRet.ReportSubtitle.GetValue()
'Get value of NumRows
Dim NumRows10690 As Integer
NumRows10690 = ReportRet.NumRows.GetValue()
'Get value of NumColumns
Dim NumColumns10691 As Integer
NumColumns10691 = ReportRet.NumColumns.GetValue()
'SEND TO EXCEL
Dim appXL As Excel.Application
Dim wbXl As Excel.Workbook
Dim shXL As Excel.Worksheet
Dim raXL As Excel.Range
' Start Excel and get Application object.
appXL = CreateObject("Excel.Application")
appXL.Visible = True
' Add a new workbook.
wbXl = appXL.Workbooks.Add
shXL = wbXl.ActiveSheet
'Create an array to set multiple values at once.
Dim qbdata(200, 10) As String
qbdata(0, 0) = ReportTitle10687
qbdata(0, 1) = ReportSubtitle10688
qbdata(0, 2) = NumRows10690.ToString
qbdata(0, 3) = NumColumns10691.ToString
'Get value of NumColTitleRows
Dim NumColTitleRows10692 As Integer
NumColTitleRows10692 = ReportRet.NumColTitleRows.GetValue()
If (Not ReportRet.ColDescList Is Nothing) Then
Dim i10693 As Integer
For i10693 = 0 To ReportRet.ColDescList.Count - 1
Dim ColDesc As IColDesc
ColDesc = ReportRet.ColDescList.GetAt(i10693)
If (Not ColDesc.ColTitleList Is Nothing) Then
Dim i10694 As Integer
For i10694 = 0 To ColDesc.ColTitleList.Count - 1
Dim ColTitle As IColTitle
ColTitle = ColDesc.ColTitleList.GetAt(i10694)
If (Not ColTitle.value Is Nothing) Then
qbdata(i10694 + 2, i10693) = ColTitle.value.GetValue.ToString
End If
Next i10694
End If
Dim ColType10695 As ENColType
ColType10695 = ColDesc.ColType.GetValue()
Next i10693
End If
If (Not ReportRet.ReportData Is Nothing) Then
If (Not ReportRet.ReportData.ORReportDataList Is Nothing) Then
Dim i10696 As Integer
'this runs the rows
For i10696 = 0 To ReportRet.ReportData.ORReportDataList.Count - 1
Dim ORReportData10697 As IORReportData
ORReportData10697 = ReportRet.ReportData.ORReportDataList.GetAt(i10696)
If (Not ORReportData10697.DataRow Is Nothing) Then
If (Not ORReportData10697.DataRow Is Nothing) Then
If (Not ORReportData10697.DataRow.RowData Is Nothing) Then
End If
If (Not ORReportData10697.DataRow.ColDataList Is Nothing) Then
Dim i10698 As Integer
For i10698 = 0 To ORReportData10697.DataRow.ColDataList.Count - 1
Dim ColData As IColData
ColData = ORReportData10697.DataRow.ColDataList.GetAt(i10698)
qbdata(i10696 + 5, i10698) = ColData.value.GetValue.ToString
Next i10698
End If
End If
End If
If (Not ORReportData10697.TextRow Is Nothing) Then
If (Not ORReportData10697.TextRow Is Nothing) Then
qbdata(i10696 + 5, 0) = ORReportData10697.TextRow.value.GetValue.ToString
End If
End If
If (Not ORReportData10697.SubtotalRow Is Nothing) Then
If (Not ORReportData10697.SubtotalRow Is Nothing) Then
If (Not ORReportData10697.SubtotalRow.RowData Is Nothing) Then
End If
If (Not ORReportData10697.SubtotalRow.ColDataList Is Nothing) Then
Dim i10699 As Integer
For i10699 = 0 To ORReportData10697.SubtotalRow.ColDataList.Count - 1
Dim ColData As IColData
ColData = ORReportData10697.SubtotalRow.ColDataList.GetAt(i10699)
qbdata(i10696 + 5, i10699) = ColData.value.GetValue.ToString
Next i10699
End If
End If
End If
If (Not ORReportData10697.TotalRow Is Nothing) Then
If (Not ORReportData10697.TotalRow Is Nothing) Then
If (Not ORReportData10697.TotalRow.RowData Is Nothing) Then
End If
If (Not ORReportData10697.TotalRow.ColDataList Is Nothing) Then
Dim i10700 As Integer
For i10700 = 0 To ORReportData10697.TotalRow.ColDataList.Count - 1
Dim ColData As IColData
ColData = ORReportData10697.TotalRow.ColDataList.GetAt(i10700)
qbdata(i10696 + 5, i10700) = ColData.value.GetValue.ToString
Next i10700
End If
End If
End If
Next i10696
End If
End If
' Fill A2:B6 with an array of data
shXL.Range("A1", "K205").Value = qbdata
' Make sure Excel is visible and give the user control
' of Excel's lifetime.
appXL.Visible = True
appXL.UserControl = True
' Release object references.
raXL = Nothing
shXL = Nothing
wbXl = Nothing
appXL.Quit()
appXL = Nothing
End Sub
Public Sub BuildGeneralSummaryReportBalanceSheetQueryRq(requestMsgSet As IMsgSetRequest)
Dim GeneralSummaryReportQueryRq As IGeneralSummaryReportQuery
GeneralSummaryReportQueryRq = requestMsgSet.AppendGeneralSummaryReportQueryRq()
'Set field value for GeneralSummaryReportType
GeneralSummaryReportQueryRq.GeneralSummaryReportType.SetValue(ENGeneralSummaryReportType.gsrtBalanceSheetPrevYearComp)
'Set field value for DisplayReport
GeneralSummaryReportQueryRq.DisplayReport.SetValue(True)
'set report period
GeneralSummaryReportQueryRq.ORReportPeriod.ReportDateMacro.SetValue(ENReportDateMacro.rdmLastYear)
'Set field value for ReportDetailLevelFilter
GeneralSummaryReportQueryRq.ReportDetailLevelFilter.SetValue(ENReportDetailLevelFilter.rdlfAll)
'Set field value for ReportPostingStatusFilter
GeneralSummaryReportQueryRq.ReportPostingStatusFilter.SetValue(ENReportPostingStatusFilter.rpsfEither)
'Set field value for SummarizeColumnsBy
GeneralSummaryReportQueryRq.SummarizeColumnsBy.SetValue(ENSummarizeColumnsBy.scbTotalOnly)
'Set field value for IncludeSubcolumns
GeneralSummaryReportQueryRq.IncludeSubcolumns.SetValue(False)
'Set field value for ReportCalendar
GeneralSummaryReportQueryRq.ReportCalendar.SetValue(ENReportCalendar.rcFiscalYear)
'Set field value for ReturnRows
GeneralSummaryReportQueryRq.ReturnRows.SetValue(ENReturnRows.rrAll)
'Set field value for ReturnColumns
GeneralSummaryReportQueryRq.ReturnColumns.SetValue(ENReturnColumns.rcAll)
'Set field value for ReportBasis
GeneralSummaryReportQueryRq.ReportBasis.SetValue(ENReportBasis.rbCash)
End Sub

Matching subset of data

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

Excel VBA Code pastes result into wrong range

A script that copies a range into another range. However, when I try to copy the range from Sheet1 to Sheet2 the result won't be pasted into column J, it get pasted with an offset of 8 columns (column R). I cant understand why? Both RowCountSummary and ColumnCountSummary are set to 0, i.e. first index of the range?
Sub InsertForecastData()
Dim ColumnsCount As Integer
Dim ColCounter As Integer
Dim RowsCount As Integer
Dim ForeCastRange As Range
Dim ForecastWS As Worksheet
Dim SummaryWs As Worksheet
Dim PasteRange As Range
Dim ColumnCountSummary As Integer
Dim RowCountSummary As Integer
ColumnsCount = 300
ColCounter = 0
RowsCount1 = 0
RowsCount2 = 47
ColumnCountSummary = 0
RowCountSummary = 0
Do While ColCounter <= ColumnsCount
Worksheets("Sheet1").Select
Set ForeCastRange = Worksheets("Sheet1").Range("B2:KN49")
With ForeCastRange
.Range(.Cells(RowsCount1, ColCounter), .Cells(RowsCount2, ColCounter)).Copy
End With
Worksheets("Sheet2").Select
Set PasteRange = Worksheets("Sheet2").Range("J2:J13915")
With PasteRange
.Range(.Cells(RowCountSummary, ColumnCountSummary), .Cells(RowCountSummary + RowsCount2, ColumnCountSummary)).PasteSpecial
End With
RowCountSummary = RowCountSummary + 48
ColCounter = ColCounter + 1
Loop
End Sub
This behaviour has been encountered before and can seen with this simple demo
Sub test()
With Sheet1.Range("J3:J100")
Debug.Print .Range(.Cells(0, 0), .Cells(47, 0)).Address
End With
End Sub
which results in $R$4:$R$51. If you repeat run for the columns B to J the results are B,D,F,H,J,L,N,P showing the doubling effect. B is OK I think because of the zero column number.
You can probably fix your code by setting RowCountSummary = 1 and ColumnCountSummary = 1 and adding .parent
With PasteRange
.Parent.Range(.Cells(RowCountSummary, ColumnCountSummary), _
.Cells(RowCountSummary + RowsCount2, ColumnCountSummary)).PasteSpecial
End With
or you could try this
Sub InsertForecastData1()
Const columnCount As Integer = 3
Const rowCount As Integer = 48
Const sourceCol As String = "B"
Const targetCol As String = "J"
Const startRow As Integer = 2
Const records As Integer = 300
Dim rngSource as Range, rngTarget As Range
Dim start as Single, finish as Single
Set rngSource = Worksheets("Sheet1").Range(sourceCol & startRow)
Set rngSource = rngSource.Resize(rowCount, columnCount)
Set rngTarget = Worksheets("Sheet2").Range(targetCol & startRow)
start = Timer
Application.ScreenUpdating = False
Dim i As Integer
For i = 1 To records
'Debug.Print rngSource.Address, rngTarget.Address
rngSource.Copy rngTarget
Set rngSource = rngSource.Offset(rowCount, 0)
Set rngTarget = rngTarget.Offset(rowCount, 0)
Next i
Application.ScreenUpdating = True
finish = Timer
MsgBox "Completed " & records & " records in " & finish - start & " secs"
End Sub
See Remarks section the docs

Resources