Create Dynamic Report using VBA  | Administrator | | Join Date: Aug 2006 Location: Dublin, Ireland
Posts: 10,865
# 1
Aug 20 '07
| |
This article contains three different approaches to creating dynamic reports. - Dynamic report from user defined SQL SELECT statement (Author: mmccarthy)
- Dynamic report designed to be used with Crosstab Queries (Author: Nico5038)
- Dynamic report creation via AutoReport command (Author: FishVal)
Dynamic report from user defined SQL SELECT statement
You will sometimes find yourself in a position where you need to allow the users to create dynamic reports based on a user defined query. The following function will create such a report when passed any appropriate SQL SELECT statement. -
Function CreateDynamicReport(strSQL As String)
-
Dim db As DAO.database ' database object
-
Dim rs As DAO.Recordset ' recordset object
-
Dim fld As DAO.Field ' recordset field
-
Dim txtNew As Access.TextBox ' textbox control
-
Dim lblNew As Access.Label ' label control
-
Dim rpt As Report ' hold report object
-
Dim lngTop As Long ' holds top value of control position
-
Dim lngLeft As Long ' holds left value of controls position
-
Dim title As String 'holds title of report
-
-
'set the title
-
title = "Title for the Report"
-
-
' initialise position variables
-
lngLeft = 0
-
lngTop = 0
-
-
'Create the report
-
Set rpt = CreateReport
-
-
' set properties of the Report
-
With rpt
-
.Width = 8500
-
.RecordSource = strSQL
-
.Caption = title
-
End With
-
-
' Open SQL query as a recordset
-
Set db = CurrentDb
-
Set rs = db.OpenRecordset(strSQL)
-
-
' Create Label Title
-
Set lblNew = CreateReportControl(rpt.Name, acLabel, _
-
acPageHeader, , "Title", 0, 0)
-
lblNew.FontBold = True
-
lblNew.FontSize = 12
-
lblNew.SizeToFit
-
-
' Create corresponding label and text box controls for each field.
-
For Each fld In rs.Fields
-
-
' Create new text box control and size to fit data.
-
Set txtNew = CreateReportControl(rpt.Name, acTextBox, _
-
acDetail, , fld.Name, lngLeft + 1500, lngTop)
-
txtNew.SizeToFit
-
-
' Create new label control and size to fit data.
-
Set lblNew = CreateReportControl(rpt.Name, acLabel, acDetail, _
-
txtNew.Name, fld.Name, lngLeft, lngTop, 1400, txtNew.Height)
-
lblNew.SizeToFit
-
-
' Increment top value for next control
-
lngTop = lngTop + txtNew.Height + 25
-
Next
-
-
' Create datestamp in Footer
-
Set lblNew = CreateReportControl(rpt.Name, acLabel, _
-
acPageFooter, , Now(), 0, 0)
-
-
' Create page numbering on footer
-
Set txtNew = CreateReportControl(rpt.Name, acTextBox, _
-
acPageFooter, , "='Page ' & [Page] & ' of ' & [Pages]", rpt.Width - 1000, 0)
-
txtNew.SizeToFit
-
-
' Open new report.
-
DoCmd.OpenReport rpt.Name, acViewPreview
-
-
'reset all objects
-
rs.Close
-
Set rs = Nothing
-
Set rpt = Nothing
-
Set db = Nothing
-
-
End Function
-
This report will not be saved until the user saves it or tries to close it. At which point they will be prompted to save it. You can play around with the layout of the report using the lngTop and lngLeft variables.
To call this function you simply need to pass a String parameter of a SQL statement to it as per the following.
CreateDynamicReport "SELECT * FROM TableName"
To create that SQL SELECT statement you can set up a form to allow the user to select options to build the query. There is no facility in this code to validate the SQL query as it is assumed this is done elsewhere. But thats another article.
Dynamic report designed to be used with Crosstab Queries
This code is especially "tuned" for crosstab queries. As I like to have control over the layout, thus I have the lay-out designed first with "coded" controls. Then the dynamic filling becomes very easy. The raw text I use to help with this is:
Making the columnheader and detaildata flexible is possible, but needs some VBA code in the OpenReport event.
To start, doing this you need to place the fields "coded" in the report.
The column headings should be called "lblCol1", "lblCol2", "lblCol3", etc.
The "detail" fields should be called "Col1", "Col2", "Col3", etc.
The report query has two rowheader columns and a Total column, therefore the first field is effectively column 4 (count starts at 0 so I used intI=3) but this could differ for you.
Make sure that the number of Columns is not bigger than the number placed. The program code has no protection against that.
The code needed for the open report event is: -
Private Sub Report_Open(Cancel As Integer)
-
Dim intI As Integer
-
Dim rs As Recordset
-
-
Set rs = CurrentDb.OpenRecordset(Me.RecordSource)
-
-
'Place headers
-
For intI = 3 To rs.Fields.Count - 1
-
Me("lblCol" & intI - 1).Caption = rs.Fields(intI).Name
-
Next intI
-
-
'Place correct controlsource
-
For intI = 3 To rs.Fields.Count - 1
-
Me("Col" & intI - 1).ControlSource = rs.Fields(intI).Name
-
Next intI
-
-
'Place Total field
-
Me.ColTotal.ControlSource = "=SUM([" & rs.Fields(2).Name & "])"
-
-
End Sub
-
The report query has two rowheader columns and a Total column, therefor the first field is effectively column 4 (count starts at 0 so I used intI=3) but it could differ for you. Dynamic report creation via AutoReport command
This code is used for dynamic report creation using the AutoReport command. You will first need to create a query and call it "qryDummy". This query is used by the code but the resulting report will not be based on the query as this would invalidate the report when the query was next changed. -
Public Sub CreateAutoReport(strSQL As String)
-
Dim rptReport As Access.Report
-
Dim strCaption As String
-
-
CurrentDb.QueryDefs("qryDummy").SQL = strSQL
-
-
' Open dummy query to invoke NewObjectAutoReport command on it
-
' Put the report created to design view to make properties editable
-
With DoCmd
-
.OpenQuery "qryDummy", acViewNormal
-
.RunCommand acCmdNewObjectAutoReport
-
.Close acQuery, "qryDummy"
-
.RunCommand acCmdDesignView
-
End With
-
-
' Get reference to just created report
-
For Each rpt In Reports
-
If rpt.Caption = "qryDummy" Then Set rptReport = rpt
-
Next
-
-
With rptReport
-
-
' Create title control
-
With CreateReportControl(.Name, acLabel, _
-
acPageHeader, , "Title", 0, 0)
-
.FontBold = True
-
.FontSize = 12
-
.SizeToFit
-
End With
-
-
' Create timestamp on footer
-
CreateReportControl .Name, acLabel, _
-
acPageFooter, , Now(), 0, 0
-
-
' Create page numbering on footer
-
With CreateReportControl(.Name, acTextBox, _
-
acPageFooter, , "='Page ' & [Page] & ' of ' & [Pages]", _
-
.Width - 1000, 0)
-
.SizeToFit
-
End With
-
-
' Detach the report from dummy query
-
.RecordSource = strSQL
-
-
' Set the report caption to autogenerated unique string
-
strCaption = GetUniqueReportName
-
If strCaption <> "" Then .Caption = strCaption
-
-
End With
-
-
DoCmd.RunCommand acCmdPrintPreview
-
-
Set rptReport = Nothing
-
-
End Sub
-
-
-
Public Function GetUniqueReportName() As String
-
Dim intCounter As Integer
-
Dim blnIsUnique As Boolean
-
-
For intCounter = 1 To 256
-
GetUniqueReportName = "rptAutoReport_" & Format(intCounter, "0000")
-
blnIsUnique = True
-
For Each rpt In CurrentProject.AllReports
-
If rpt.Name = GetUniqueReportName Then blnIsUnique = False
-
Next
-
If blnIsUnique Then Exit Function
-
Next
-
-
GetUniqueReportName = ""
-
-
End Function
-
| | Newbie | | Join Date: Nov 2007
Posts: 2
# 2
Nov 8 '07
| | | re: Create Dynamic Report using VBA
Hello,
I am sorry I don't know exactly the coding of how VBA generate autoreport in ACCESS, could you please indicate the command/ coding?
Many thanks,
Rock
| | Newbie | | Join Date: Nov 2007
Posts: 2
# 3
Nov 8 '07
| | | re: Create Dynamic Report using VBA
Hello,
If I have some tables, and want to use a form (with a button) to call the autoreport function by selecting one of the tables, then how can I do it??
Please give me some suggestions.
Many thanks!!
Rock
| | Newbie | | Join Date: Jan 2008
Posts: 2
# 4
Jan 21 '08
| | | re: Create Dynamic Report using VBA
I was wondering how one would dynamically change the sizing of the columns in the field. Creating an auto report is nice but its messy. Here is the code I have now -
Public Function StaticReportGen(SQLStr As String, Title As String, layout As String) As Boolean
-
Dim strReportName As String
-
Dim rpt As Report
-
Dim FieldName As Field
-
Dim RS As Recordset
-
Dim intI As Integer
-
Dim ctrl As Control
-
Dim ColWidth As Integer
-
Dim FirstCol As Boolean
-
Dim TextWidth As Integer
-
Dim TextCol As Boolean
-
Dim TextBoxes As Collection
-
Dim Labels As Collection
-
Dim rsLengthCheck As ADODB.Recordset
-
Dim objConn As ADODB.Connection
-
-
On Error GoTo rptErrHandler
-
-
ColWidth = 0
-
TextWidth = 0
-
TextCol = True
-
FirstCol = True
-
-
Set rpt = CreateReport()
-
strReportName = rpt.Name
-
rpt.Caption = Title
-
-
DoCmd.RunCommand acCmdDesignView
-
DoCmd.Save acReport, strReportName
-
DoCmd.Close acReport, strReportName, acSaveNo
-
DoCmd.Rename Title, acReport, strReportName
-
DoCmd.OpenReport Title, acViewDesign
-
Set rpt = Reports(Title)
-
-
'set printer stuff
-
rpt.Printer.BottomMargin = 360
-
rpt.Printer.LeftMargin = 360
-
rpt.Printer.RightMargin = 360
-
rpt.Printer.TopMargin = 360
-
-
If layout = "Landscape" Then
-
rpt.Printer.Orientation = acPRORLandscape
-
Else
-
rpt.Printer.Orientation = acPRORPortrait
-
End If
-
-
Set RS = CurrentDb.OpenRecordset(SQLStr)
-
rpt.RecordSource = SQLStr
-
-
'create label on pageheader
-
For Each FieldName In RS.Fields
-
CreateReportControl Title, acLabel, acPageHeader, , FieldName.Name, 0, 0
-
CreateReportControl Title, acTextBox, acDetail, , FieldName.Name, 0, 0
-
'
-
Next FieldName
-
-
'arrange fields
-
For Each ctrl In rpt.Controls
-
-
Select Case ctrl.ControlType
-
Case acTextBox
-
If TextCol Then
-
ctrl.Name = ctrl.ControlSource
-
ctrl.Move TextWidth, 0, ctrl.WIDTH, ctrl.Height
-
TextWidth = TextWidth + ctrl.WIDTH
-
Else
-
ctrl.Name = ctrl.ControlSource
-
ctrl.Move TextWidth, 0, ctrl.WIDTH, ctrl.Height
-
TextWidth = TextWidth + ctrl.WIDTH
-
End If
-
TextCol = False
-
Case acLabel
-
If FirstCol Then
-
ctrl.Name = "lbl" & ctrl.Caption
-
ctrl.Move ColWidth, 0, ctrl.WIDTH, ctrl.Height
-
Else
-
ctrl.Name = "lbl" & ctrl.Caption
-
ctrl.Move TextWidth, 0, ctrl.WIDTH, ctrl.Height
-
End If
-
ctrl.FontSize = 10
-
ctrl.FontWeight = 700
-
FirstCol = False
-
Case Else
-
-
End Select
-
-
Next ctrl
-
'create line
-
CreateReportControl Title, acLine, acPageHeader, , , 0, 300, rpt.WIDTH
-
-
'create title
-
CreateReportControl Title, acLabel, acHeader, , Title, 0, 0
-
CreateReportControl Title, acTextBox, acHeader, , Chr(61) & Chr(34) & "Printed on: " & Chr(34) & "& Date() ", 0, 300
-
-
For Each ctrl In rpt.Controls
-
-
Select Case ctrl.ControlType
-
Case acTextBox
-
If ctrl.Section = 1 Then
-
ctrl.FontWeight = 700
-
ctrl.FontSize = 14
-
ctrl.Height = 350
-
ctrl.WIDTH = 3500
-
ctrl.Top = 400
-
End If
-
-
Case acLabel
-
If ctrl.Section = 1 Then
-
ctrl.FontSize = 16
-
ctrl.FontWeight = 700
-
ctrl.Height = 350
-
ctrl.WIDTH = 3500
-
End If
-
End Select
-
-
Next ctrl
-
-
'size fields correctly
-
For Each ctrl In rpt.Controls
-
-
Select Case ctrl.ControlType
-
-
Case acTextBox
-
For Each FieldName In RS.Fields
-
If ctrl.Name = FieldName Then
-
-
End If
-
Next FieldName
-
-
Case acLabel
-
-
End Select
-
-
Next ctrl
-
-
DoCmd.Save acReport, Title
-
DoCmd.OpenReport Title, acViewPreview
-
StaticReportGen = True
-
Exit Function
-
-
rptErrHandler:
-
Select Case Err.Number
-
End Select
-
StaticReportGen = False
-
Debug.Print Err.Number
-
Debug.Print Err.Description
-
Exit Function
-
End Function
-
I want to use the textwidth property just having a block.
| | Newbie | | Join Date: Aug 2008 Location: TN
Posts: 23
# 5
Aug 26 '08
| | | re: Create Dynamic Report using VBA
Can you set a dynamic report to be tabular automatically?
Also, could somebody show what the code would look like to add groupings to a dynamic report?
I try
with rpt
.GroupLevel(0).controlsource = me.combo1.value
but keep getting "Error, no grouping or sorting command given"
Thanks.
|  | Similar Microsoft Access / VBA bytes | | | /bytes/about
We are a network of experts and professionals in IT and software development that help one another with answers to tough questions and share insights.
Get the best answers to your questions from over 226,295 network members.
|