VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "clsVBAFunctions" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = True Option Explicit '*** Constant declarations Private Const strMODULE_NAME As String = "clsVBAFunction" '*** Function to generate and write a chart picture using the Microsoft Excel Object Library '*** Input Parameters: '*** rsData: ADO recordset to chart '*** strCategoryField: name of the field in the recordset to be used for the category axis (bottom) '*** arrSeriesFields: array of field names in the recordset to be used for the data series (left and/or right) '*** arrFieldAxisGroupings: array defining which axis to use for each series (1 = left, 2 - right) '*** arrSeriesColors: array defining which color to use for each series as RGB value (ie., "255,0,0") '*** strCategoryAxisLabel: label to display for the date (y) axis '*** strLeftValueAxisLabel: label to display for the left value (x) axis '*** strRightValueAxisLabel: label to display for the right value (x) axis '*** strCategoryAxisFormat: format string for category axis values (ie., "MM/YY" etc.) '*** strTitle: chart title '*** strFont: font name to use for all text '*** intFontSizeTitle: font size for title '*** intFontSizeBody: font size for all other text '*** blnDisplayValueLabels: display value labels for each data point? '*** intFontSizeValueLabels: font size to use for optional value labels '*** strValueLabelsFormat: format string for optional value labels (ie., "###.###,###.0" etc.) '*** blnDisplayLegend: display legend? '*** strTempFileName: file name to write (including absolute path) '*** Note: no validation is done to verify that all parameters are defined correctly for the specified chart Public Function blnWriteLineChart(ByVal rsData As ADODB.Recordset, _ ByVal strCategoryField As String, _ ByVal arrSeriesFields As Variant, _ ByVal arrFieldAxisGroupings As Variant, _ ByVal arrSeriesColors As Variant, _ ByVal strCategoryAxisLabel As String, _ ByVal strCategoryAxisFormat As String, _ ByVal intCategoryAxisSpacingFactor As Integer, _ ByVal strLeftValueAxisLabel As String, _ ByVal strRightValueAxisLabel As String, _ ByVal strTitle As String, _ ByVal strFont As String, _ ByVal intFontSizeTitle As Integer, _ ByVal intFontSizeBody As Integer, _ ByVal blnDisplayValueLabels As Boolean, _ ByVal blnDisplayMarkerLabels As Boolean, _ ByVal intFontSizeValueLabels As Integer, _ ByVal strValueLabelsFormat As String, _ ByVal blnDisplayLegend As Boolean, _ ByVal strTempFileName As String) As Boolean Dim objXlApp As Excel.Application Dim objXlBook As Excel.Workbook Dim objXlSheet As Excel.Worksheet Dim objXLChart As Excel.Chart Dim intRecordCounter As Integer Dim i As Integer Dim j As Integer Dim strDataValue As String Dim intFieldCounter As Integer Dim intOutFieldCounter As Integer Dim intCategoryAxisSpacing As Integer '*** Enable error handling On Error GoTo ErrorHandler '*** Create an Excel application and workbook Set objXlApp = CreateObject("Excel.Application") Set objXlBook = objXlApp.Workbooks.Add '*** Remove all but a single worksheet (for some reason the workbook starts out with 3 and various code examples always show deleting the extra worksheets) objXlApp.DisplayAlerts = False For i = objXlBook.Worksheets.Count To 2 Step -1 objXlBook.Worksheets(i).Delete Next i Set objXlSheet = objXlBook.Worksheets(1) '*** Iterate through the recordset and write only the chart fields from each row to the worksheet '*** Note: the category and series fields can be in any order or position in the recordset, '*** the category field will be automatically written to column 1 (A) in the worksheet intRecordCounter = 0 With rsData .MoveFirst While .EOF <> True intRecordCounter = intRecordCounter + 1 '*** Initialize output field counter intOutFieldCounter = 1 '*** Iterate through each field in the row For intFieldCounter = 0 To .Fields.Count - 1 '*** If the current field is the category axis field then add this to column 1 (A) If UCase(.Fields(intFieldCounter).Name) = UCase(strCategoryField) Then objXlSheet.Cells(intRecordCounter, 1) = CStr(.Fields(intFieldCounter).Value & "") End If '*** If the current field is defined as a series field then add it to columns 2 - N (B - ?) If blnFindValueInArray(arrSeriesFields, .Fields(intFieldCounter).Name) = True Then intOutFieldCounter = intOutFieldCounter + 1 strDataValue = .Fields(intFieldCounter).Value & "" If strDataValue = "" Then strDataValue = 0 objXlSheet.Cells(intRecordCounter, intOutFieldCounter) = strDataValue End If Next .MoveNext Wend End With '*** Configure the workbook's color palette using the specified colors For i = 0 To UBound(arrSeriesColors, 1) objXlBook.Colors(i + 1) = RGB(Split(arrSeriesColors(i), "|")(0), _ Split(arrSeriesColors(i), "|")(1), Split(arrSeriesColors(i), "|")(2)) Next i '*** Calculate category spacing intCategoryAxisSpacing = (intRecordCounter) / intCategoryAxisSpacingFactor If intCategoryAxisSpacing < 1 Then intCategoryAxisSpacing = 1 '*** Create the chart object Set objXLChart = objXlBook.Charts.Add '*** Configure the chart With objXLChart '*** Define chart type .ChartType = xlLine '*** Create chart data series .SetSourceData Source:=objXlSheet.Range("A1:" & strGetLetter(intOutFieldCounter) & _ intRecordCounter), PlotBy:=xlColumns '*** Iterate through the field definitions and configure each series For i = 0 To UBound(arrSeriesFields, 1) With .SeriesCollection(i + 1) .Name = Replace(arrSeriesFields(i), "_", " ") .AxisGroup = arrFieldAxisGroupings(i) '*** Assign color and linestyle (color uses palette which was configured above) With .Border .ColorIndex = i + 1 .Weight = xlMedium .LineStyle = xlContinuous End With '*** If specified then configure data labels If blnDisplayValueLabels = True Then .HasDataLabels = True With .DataLabels .NumberFormat = strValueLabelsFormat .Font.Name = strFont .Font.Size = intFontSizeValueLabels .Font.Color = RGB(Split(arrSeriesColors(i), "|")(0), _ Split(arrSeriesColors(i), "|")(1), _ Split(arrSeriesColors(i), "|")(2)) .Position = xlLabelPositionAbove End With '*** If category axis spacing > 1 then filter data labels accordingly If intCategoryAxisSpacing > 1 Then '*** Iterate through each data label For j = 1 To intRecordCounter '*** If data label position does not match category axis value then blank it If Int(j / (intCategoryAxisSpacing)) <> j / (intCategoryAxisSpacing) Then .Points(j).DataLabel.Text = "" End If Next j End If If blnDisplayMarkerLabels = True Then .MarkerForegroundColorIndex = i + 1 .MarkerBackgroundColorIndex = i + 1 .MarkerStyle = xlDiamond .Smooth = False .MarkerSize = 5 End If End If End With Next i '*** Configure title If strTitle <> "" Then .HasTitle = True With .ChartTitle .Caption = strTitle .Font.Name = strFont .Font.Size = intFontSizeTitle .Font.Bold = True End With End If '*** Configure category axis (bottom) If strCategoryAxisLabel <> "" Then With .Axes(xlCategory, xlPrimary) .AxisBetweenCategories = False .HasTitle = True With .AxisTitle .Caption = strCategoryAxisLabel .Font.Name = strFont .Font.Size = intFontSizeBody .Font.Bold = True End With .HasMajorGridlines = True .HasMinorGridlines = False .MajorUnit = intCategoryAxisSpacing With .TickLabels .NumberFormat = strCategoryAxisFormat .Font.Name = strFont .Font.Size = intFontSizeBody End With End With End If '*** Configure primary axis (left) If strLeftValueAxisLabel <> "" Then With .Axes(xlValue, xlPrimary) .HasTitle = True With .AxisTitle .Caption = strLeftValueAxisLabel .Font.Name = strFont .Font.Size = intFontSizeBody .Font.Bold = True End With .HasMajorGridlines = True .HasMinorGridlines = False .MajorTickMark = xlOutside .MinorTickMark = xlCross .TickLabelPosition = xlNextToAxis With .TickLabels.Font .Name = strFont .Size = intFontSizeBody End With End With End If '*** Configure secondary axis (right) If strRightValueAxisLabel <> "" Then With .Axes(xlValue, xlSecondary) .HasTitle = True With .AxisTitle .Caption = strRightValueAxisLabel .Font.Name = strFont .Font.Size = intFontSizeBody .Font.Bold = True End With .MajorTickMark = xlOutside .MinorTickMark = xlCross .TickLabelPosition = xlNextToAxis With .TickLabels.Font .Name = strFont .Size = intFontSizeBody End With End With End If '*** Configure Legend If blnDisplayLegend = True Then .HasLegend = True With .Legend .Position = xlBottom .Font.Name = strFont .Font.Size = intFontSizeBody .Font.Bold = True End With End If '*** Export the chart to a graphic file .Export strTempFileName End With '*** Return success blnWriteLineChart = True '*** Close and quit excel objXlBook.Close objXlApp.Quit CommonExit: GoSub ObjectCleanup Exit Function ObjectCleanup: If Not (objXLChart Is Nothing) Then Set objXLChart = Nothing If Not (objXlSheet Is Nothing) Then Set objXlSheet = Nothing If Not (objXlBook Is Nothing) Then Set objXlBook = Nothing If Not (objXlApp Is Nothing) Then Set objXlApp = Nothing Return ErrorHandler: blnWriteLineChart = False GoSub ObjectCleanup '*** Raise an error to the calling procedure Err.Raise Err.Number, Err.Source & "[" & strMODULE_NAME & ".blnWriteLineChart()]", Err.Description End Function Public Function blnWriteXlsFile(ByVal arrColHeaders As Variant, _ ByVal arrFieldFormats As Variant, _ ByVal rsData As ADODB.Recordset, _ ByVal strTempFilePath As String) As Boolean Dim objXlApp As Excel.Application Dim objXlBook As Excel.Workbook Dim objXlSheet As Excel.Worksheet Dim i As Integer Dim j As Integer '*** Enable error handling On Error GoTo ErrorHandler '*** Create an Excel application and workbook Set objXlApp = CreateObject("Excel.Application") Set objXlBook = objXlApp.Workbooks.Add '*** Remove all but a single worksheet (for some reason the workbook starts out with 3 and various code examples always show deleting the extra worksheets) objXlApp.DisplayAlerts = False For i = objXlBook.Worksheets.Count To 2 Step -1 objXlBook.Worksheets(i).Delete Next i Set objXlSheet = objXlBook.Worksheets(1) '*** If a column header array was passed then write the array elements to the worksheet If IsArray(arrColHeaders) Then With objXlSheet '*** Iterate through each column in the array and add it to the sheet For i = 0 To UBound(arrColHeaders) With .Cells(1, i + 1) .Formula = arrColHeaders(i) .Font.Bold = True End With Next i .Range("A1:Z1").WrapText = True .Range("A1:Z1").HorizontalAlignment = xlHAlignCenter End With End If '*** Iterate through the recordset and write each row to the worksheet With rsData .MoveFirst '*** Initialize row pointer to second row i = 2 While .EOF <> True '*** Iterate through each field in the row and add it to the sheet For j = 0 To .Fields.Count - 1 '*** If a field output format array was provided then search for a format string If IsArray(arrFieldFormats) Then '*** If a format string was defined then format the field for display If arrFieldFormats(j) <> "" Then objXlSheet.Cells(i, j + 1) = Format(.Fields(j).Value, arrFieldFormats(j)) '*** Else just display the value "as-is" Else objXlSheet.Cells(i, j + 1) = CStr(.Fields(j).Value & "") End If '*** Else just display the value "as-is" Else objXlSheet.Cells(i, j + 1) = CStr(.Fields(j).Value & "") End If Next .MoveNext i = i + 1 Wend End With '*** Make columns in entire sheet autofit the data objXlSheet.Columns("A:Z").AutoFit '*** Save the worksheet objXlSheet.SaveAs (strTempFilePath) objXlBook.Close objXlApp.Quit '*** Return success blnWriteXlsFile = True CommonExit: GoSub ObjectCleanup Exit Function ObjectCleanup: If Not (objXlSheet Is Nothing) Then Set objXlSheet = Nothing If Not (objXlBook Is Nothing) Then Set objXlBook = Nothing If Not (objXlApp Is Nothing) Then Set objXlApp = Nothing Return ErrorHandler: blnWriteXlsFile = False GoSub ObjectCleanup '*** Raise an error to the calling procedure Err.Raise Err.Number, Err.Source & "[" & strMODULE_NAME & ".blnWriteXlsFile()]", Err.Description End Function Public Function blnWriteDocFile(ByVal arrRptTitle As Variant, _ ByVal arrColHeaders As Variant, _ ByVal arrFieldFormats As Variant, _ ByVal rsData As ADODB.Recordset, _ ByVal strFont As String, _ ByVal intFontSizeTitle As Integer, _ ByVal intFontSizeBody As Integer, _ ByVal strTempFilePath As String) As Boolean Dim objWordApp As Word.Application Dim objWordDoc As Word.Document Dim objWordRange As Word.Range Dim objWordTable As Word.Table Dim objWordFont As Word.Font Dim intColumnCount As Integer Dim i As Integer Dim j As Integer Dim arrMaxFieldSizes As Variant Const SCALING_FACTOR = 0.85 '*** Enable error handling On Error GoTo ErrorHandler '*** Create an Word application and document Set objWordApp = CreateObject("Word.Application") Set objWordDoc = objWordApp.Documents.Add objWordApp.DisplayAlerts = False '*** If a report title array was passed then write the array elements to the document If IsArray(arrRptTitle) Then Set objWordRange = objWordDoc.Range(0, 0) With objWordRange '*** Iterate through each title in the array and add it to the document For i = 0 To UBound(arrRptTitle, 1) .InsertAfter arrRptTitle(i) .InsertParagraphAfter .Font.Name = strFont .Font.Size = intFontSizeTitle .Font.Bold = True With objWordDoc.Paragraphs(i + 1) .Alignment = wdAlignParagraphCenter ' .SpaceAfter = InchesToPoints(0.25) End With Next i End With End If '*** Create a table for the data intColumnCount = rsData.Fields.Count objWordDoc.Paragraphs.Add rsData.MoveLast Set objWordTable = objWordDoc.Tables.Add(objWordDoc.Paragraphs(4).Range, rsData.RecordCount + 1, intColumnCount) With objWordTable '*** Format the table .Range.Font.Name = strFont .Range.Font.Size = intFontSizeBody '*** Get the max. field sizes for each column and set the column width arrMaxFieldSizes = arrGetMaxFieldSizes(rsData, arrFieldFormats) For i = 0 To .Columns.Count - 1 If arrMaxFieldSizes(i + 1) > 5 Then .Columns(i + 1).Width = (arrMaxFieldSizes(i + 1) * intFontSizeBody) * SCALING_FACTOR Else .Columns(i + 1).Width = (arrMaxFieldSizes(i + 1) * intFontSizeBody) End If Next i End With '*** If a column header array was passed then write the array elements to the worksheet If IsArray(arrColHeaders) Then With objWordTable '*** Iterate through each column in the array and add it to the sheet For i = 0 To intColumnCount - 1 .Cell(1, i + 1).Range.InsertAfter arrColHeaders(i) .Cell(1, i + 1).Shading.BackgroundPatternColorIndex = wdDarkBlue Next i End With End If '*** Iterate through the recordset and write each row to the worksheet With rsData .MoveFirst '*** Initialize row pointer to second row i = 2 While .EOF <> True '*** Iterate through each field in the row and add it to the sheet For j = 0 To intColumnCount - 1 '*** If a field output format array was provided then search for a format string If IsArray(arrFieldFormats) Then '*** If a format string was defined then format the field for display If arrFieldFormats(j) <> "" Then objWordTable.Cell(i, j + 1).Range.InsertAfter Format(.Fields(j).Value, arrFieldFormats(j)) '*** Else just display the value "as-is" Else objWordTable.Cell(i, j + 1).Range.InsertAfter CStr(.Fields(j).Value & "") End If '*** Else just display the value "as-is" Else objWordTable.Cell(i, j + 1).Range.InsertAfter CStr(.Fields(j).Value & "") End If Next .MoveNext i = i + 1 Wend End With '*** Save the document as a word document objWordDoc.SaveAs (strTempFilePath) objWordDoc.Close objWordApp.Quit '*** Return success blnWriteDocFile = True CommonExit: GoSub ObjectCleanup Exit Function ObjectCleanup: If Not (objWordTable Is Nothing) Then Set objWordTable = Nothing If Not (objWordRange Is Nothing) Then Set objWordTable = Nothing If Not (objWordDoc Is Nothing) Then Set objWordDoc = Nothing If Not (objWordApp Is Nothing) Then Set objWordApp = Nothing Return ErrorHandler: blnWriteDocFile = False GoSub ObjectCleanup '*** Raise an error to the calling procedure Err.Raise Err.Number, Err.Source & "[" & strMODULE_NAME & ".blnWriteDocFile()]", Err.Description End Function Public Function blnWriteTextFile(ByVal arrColHeaders As Variant, _ ByVal rsData As ADODB.Recordset, _ ByVal strTempFilePath As String) As Boolean Dim objFileSystemObject Dim objTextFile Dim i As Integer Dim strOutString As String Dim strFieldSeparator As String '*** Enable error handling On Error GoTo ErrorHandler '*** Open the output file Set objFileSystemObject = CreateObject("Scripting.FileSystemObject") Set objTextFile = objFileSystemObject.CreateTextFile(strTempFilePath, True) '*** Write starting comment objTextFile.WriteLine ("*** START OF DATA **************************************************************") '*** If a column header array was passed then write the array elements to the file If IsArray(arrColHeaders) Then '*** Iterate through each column in the array and add it to the output string For i = 0 To UBound(arrColHeaders) If i < UBound(arrColHeaders) Then strFieldSeparator = "," '*** Else use no separator (this is the last field) Else strFieldSeparator = "" End If strOutString = strOutString & """" & arrColHeaders(i) & """" & strFieldSeparator Next i '*** Write the header line objTextFile.WriteLine (strOutString) End If '*** Iterate through the recordset and write each row to the file With rsData .MoveFirst While .EOF <> True strOutString = "" '*** Iterate through each field in the row and add it to the output string For i = 0 To .Fields.Count - 1 '*** If not the last field then set the field separator = "|" If i < .Fields.Count - 1 Then strFieldSeparator = "," '*** Else use no separator (this is the last field) Else strFieldSeparator = "" End If strOutString = strOutString & """" & CStr(.Fields(i).Value & "") & """" & strFieldSeparator Next '*** Write the line objTextFile.WriteLine (strOutString) .MoveNext i = i + 1 Wend End With '*** Write an extra line (to work-around SoftArtisans.FileUp.Transfer bug that truncates part of the last line) objTextFile.WriteLine ("*** END OF DATA *********************************************************************************************************************************************************************************************************************************") '*** Return success objTextFile.Close blnWriteTextFile = True CommonExit: GoSub ObjectCleanup Exit Function ObjectCleanup: If Not (objTextFile Is Nothing) Then Set objTextFile = Nothing If Not (objFileSystemObject Is Nothing) Then Set objFileSystemObject = Nothing Return ErrorHandler: blnWriteTextFile = False GoSub ObjectCleanup '*** Raise an error to the calling procedure Err.Raise Err.Number, Err.Source & "[" & strMODULE_NAME & ".blnWriteTextFile()]", Err.Description End Function '*** This function formats a string using the specified format value Public Function strFormatValue(ByVal strValueIn As String, _ ByVal strFormat As String) As String '*** Enable error handling On Error GoTo ErrorHandler strFormatValue = Format(strValueIn, strFormat) CommonExit: Exit Function ErrorHandler: '*** Raise an error to the calling procedure Err.Raise Err.Number, Err.Source & "[" & strMODULE_NAME & ".strFormatValue()]", Err.Description End Function '*** This function returns an array containing the maximum length of data '*** contained in each field in a recordset. The first position in the array '*** (0) contains the total width of all fields Public Function arrGetMaxFieldSizes(ByVal rsData As ADODB.Recordset, _ Optional ByVal arrFieldFormats As Variant) As Variant Dim intFieldCount As Integer Dim arrMaxFieldSizes As Variant Dim i As Integer Dim intMaxDataSize As Integer Dim intCurDataSize As Integer Dim intTotalWidth As Integer '*** Enable error handling On Error GoTo ErrorHandler '*** Count the fields and ReDim an array for all the fields + 1 (for the total width) With rsData intFieldCount = .Fields.Count ReDim arrMaxFieldSizes(intFieldCount + 1) '*** Iterate through the each field in the recordset intTotalWidth = 0 For i = 0 To intFieldCount - 1 .MoveFirst intMaxDataSize = 0 '*** Iterate through in row in the recordset While rsData.EOF <> True If IsArray(arrFieldFormats) Then '*** If a format string was defined then format the field for display If arrFieldFormats(i) <> "" Then intCurDataSize = Len(Format(.Fields(i).Value, arrFieldFormats(i))) '*** Else just display the value "as-is" Else intCurDataSize = Len(CStr(.Fields(i).Value)) End If Else intCurDataSize = Len(CStr(.Fields(i).Value)) End If '*** If the current row's field size is greater than the max the reset the max If intCurDataSize > intMaxDataSize Then intMaxDataSize = intCurDataSize End If .MoveNext Wend '*** Add the max size to the total and assign max size to the array intTotalWidth = intTotalWidth + intMaxDataSize arrMaxFieldSizes(i + 1) = intMaxDataSize Next End With '*** Assign the total to the array and return the array arrMaxFieldSizes(0) = intTotalWidth arrGetMaxFieldSizes = arrMaxFieldSizes CommonExit: Exit Function ErrorHandler: '*** Raise an error to the calling procedure Err.Raise Err.Number, Err.Source & "[" & strMODULE_NAME & ".arrGetMaxFieldSizes()]", Err.Description End Function