This article contains three different approaches to creating dynamic reports.
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.
[code=vb]
Function CreateDynamicRe port(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.OpenRecordse t(strSQL)
' Create Label Title
Set lblNew = CreateReportCon trol(rpt.Name, acLabel, _
acPageHeader, , "Title", 0, 0)
lblNew.FontBold = True
lblNew.FontSize = 12
lblNew.SizeToFi t
' 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 = CreateReportCon trol(rpt.Name, acTextBox, _
acDetail, , fld.Name, lngLeft + 1500, lngTop)
txtNew.SizeToFi t
' Create new label control and size to fit data.
Set lblNew = CreateReportCon trol(rpt.Name, acLabel, acDetail, _
txtNew.Name, fld.Name, lngLeft, lngTop, 1400, txtNew.Height)
lblNew.SizeToFi t
' Increment top value for next control
lngTop = lngTop + txtNew.Height + 25
Next
' Create datestamp in Footer
Set lblNew = CreateReportCon trol(rpt.Name, acLabel, _
acPageFooter, , Now(), 0, 0)
' Create page numbering on footer
Set txtNew = CreateReportCon trol(rpt.Name, acTextBox, _
acPageFooter, , "='Page ' & [Page] & ' of ' & [Pages]", rpt.Width - 1000, 0)
txtNew.SizeToFi t
' Open new report.
DoCmd.OpenRepor t rpt.Name, acViewPreview
'reset all objects
rs.Close
Set rs = Nothing
Set rpt = Nothing
Set db = Nothing
End Function
[/code]
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.
CreateDynamicRe port "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:
[code=vb]
Private Sub Report_Open(Can cel As Integer)
Dim intI As Integer
Dim rs As Recordset
Set rs = CurrentDb.OpenR ecordset(Me.Rec ordSource)
'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).ControlSourc e = rs.Fields(intI) .Name
Next intI
'Place Total field
Me.ColTotal.Con trolSource = "=SUM([" & rs.Fields(2).Na me & "])"
End Sub
[/code]
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.
[code=vb]
Public Sub CreateAutoRepor t(strSQL As String)
Dim rptReport As Access.Report
Dim strCaption As String
CurrentDb.Query Defs("qryDummy" ).SQL = strSQL
' Open dummy query to invoke NewObjectAutoRe port command on it
' Put the report created to design view to make properties editable
With DoCmd
.OpenQuery "qryDummy", acViewNormal
.RunCommand acCmdNewObjectA utoReport
.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 CreateReportCon trol(.Name, acLabel, _
acPageHeader, , "Title", 0, 0)
.FontBold = True
.FontSize = 12
.SizeToFit
End With
' Create timestamp on footer
CreateReportCon trol .Name, acLabel, _
acPageFooter, , Now(), 0, 0
' Create page numbering on footer
With CreateReportCon trol(.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 = GetUniqueReport Name
If strCaption <> "" Then .Caption = strCaption
End With
DoCmd.RunComman d acCmdPrintPrevi ew
Set rptReport = Nothing
End Sub
Public Function GetUniqueReport Name() As String
Dim intCounter As Integer
Dim blnIsUnique As Boolean
For intCounter = 1 To 256
GetUniqueReport Name = "rptAutoReport_ " & Format(intCount er, "0000")
blnIsUnique = True
For Each rpt In CurrentProject. AllReports
If rpt.Name = GetUniqueReport Name Then blnIsUnique = False
Next
If blnIsUnique Then Exit Function
Next
GetUniqueReport Name = ""
End Function
[/code]
- 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.
[code=vb]
Function CreateDynamicRe port(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.OpenRecordse t(strSQL)
' Create Label Title
Set lblNew = CreateReportCon trol(rpt.Name, acLabel, _
acPageHeader, , "Title", 0, 0)
lblNew.FontBold = True
lblNew.FontSize = 12
lblNew.SizeToFi t
' 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 = CreateReportCon trol(rpt.Name, acTextBox, _
acDetail, , fld.Name, lngLeft + 1500, lngTop)
txtNew.SizeToFi t
' Create new label control and size to fit data.
Set lblNew = CreateReportCon trol(rpt.Name, acLabel, acDetail, _
txtNew.Name, fld.Name, lngLeft, lngTop, 1400, txtNew.Height)
lblNew.SizeToFi t
' Increment top value for next control
lngTop = lngTop + txtNew.Height + 25
Next
' Create datestamp in Footer
Set lblNew = CreateReportCon trol(rpt.Name, acLabel, _
acPageFooter, , Now(), 0, 0)
' Create page numbering on footer
Set txtNew = CreateReportCon trol(rpt.Name, acTextBox, _
acPageFooter, , "='Page ' & [Page] & ' of ' & [Pages]", rpt.Width - 1000, 0)
txtNew.SizeToFi t
' Open new report.
DoCmd.OpenRepor t rpt.Name, acViewPreview
'reset all objects
rs.Close
Set rs = Nothing
Set rpt = Nothing
Set db = Nothing
End Function
[/code]
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.
CreateDynamicRe port "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:
[code=vb]
Private Sub Report_Open(Can cel As Integer)
Dim intI As Integer
Dim rs As Recordset
Set rs = CurrentDb.OpenR ecordset(Me.Rec ordSource)
'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).ControlSourc e = rs.Fields(intI) .Name
Next intI
'Place Total field
Me.ColTotal.Con trolSource = "=SUM([" & rs.Fields(2).Na me & "])"
End Sub
[/code]
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.
[code=vb]
Public Sub CreateAutoRepor t(strSQL As String)
Dim rptReport As Access.Report
Dim strCaption As String
CurrentDb.Query Defs("qryDummy" ).SQL = strSQL
' Open dummy query to invoke NewObjectAutoRe port command on it
' Put the report created to design view to make properties editable
With DoCmd
.OpenQuery "qryDummy", acViewNormal
.RunCommand acCmdNewObjectA utoReport
.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 CreateReportCon trol(.Name, acLabel, _
acPageHeader, , "Title", 0, 0)
.FontBold = True
.FontSize = 12
.SizeToFit
End With
' Create timestamp on footer
CreateReportCon trol .Name, acLabel, _
acPageFooter, , Now(), 0, 0
' Create page numbering on footer
With CreateReportCon trol(.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 = GetUniqueReport Name
If strCaption <> "" Then .Caption = strCaption
End With
DoCmd.RunComman d acCmdPrintPrevi ew
Set rptReport = Nothing
End Sub
Public Function GetUniqueReport Name() As String
Dim intCounter As Integer
Dim blnIsUnique As Boolean
For intCounter = 1 To 256
GetUniqueReport Name = "rptAutoReport_ " & Format(intCount er, "0000")
blnIsUnique = True
For Each rpt In CurrentProject. AllReports
If rpt.Name = GetUniqueReport Name Then blnIsUnique = False
Next
If blnIsUnique Then Exit Function
Next
GetUniqueReport Name = ""
End Function
[/code]
Comment