Is there any way to export a report in MS Access to PDF based on a certain criteria/field on the report?
I have created a productivity report in MS Access. Instead of exporting 50 pages into 1 PDF, is there a way to export based on the manager's name? The field for the managers name is included on the actual report.
You can take this idea and play with it. Insert this into a Module
Option Explicit
Dim g_ManagerReportFilterEnabled As Boolean
Dim g_ManagerReportFilter As String
Public Function IsManagerReportFilterEnabled() As Boolean
IsManagerReportFilterEnabled = g_ManagerReportFilterEnabled
End Function
Public Function GetManagerReportFilter() As String
GetManagerReportFilter = g_ManagerReportFilter
End Function
Public Sub ExportFilteredManagerReportToPDF(strManagerName As String)
On Error GoTo ExportFilteredManagerReportToPDF_ErrorHandler
g_ManagerReportFilterEnabled = True
g_ManagerReportFilter = "[MyManagerNameField] = " & Chr(34) & strManagerName & Chr(34)
DoCmd.OutputTo acOutputReport, "MyReportName", acFormatPDF, "MyPath:\MyFileName.PDF", False
GoTo ExitMe
ExportFilteredManagerReportToPDF_ErrorHandler:
Debug.Print err.Number & ": " & err.Description
ExitMe:
g_ManagerReportFilterEnabled = False
Exit Sub
End Sub
and review the variables you need to replace. And this into Report_Open of your report:
Private Sub Report_Open(Cancel As Integer)
If IsManagerReportFilterEnabled = True Then
Me.Filter = GetManagerReportFilter
Me.FilterOn = True
End If
End Sub
So the issue this code is trying to solve is that we want to use DoCmd.OutputTo to output our PDF, but it does not take a Filter parameter. So we work around this by setting up two global variables (I know...) which let us know if we should use the Manager filter and what that filter is. When we run ExportFilteredManagerReportToPDF and pass through a name, the sub will output the report to PDF. Because of the code attached the report, when OutputTo runs, the report will detect whether the filter is enabled, and if it is, apply it. Then OutputTo finishes its work and the PDF is output.
To run this for manager John Smith, say, you can run this from the debug window:
ExportFilteredManagerReportToPDF "John Smith"
Related
I have an Access database frontend that houses 16 different forms. All of them have three buttons in common namely Show All, Clear and Refresh, that perform that exact same function using their respective subforms. For instance, for viewing data from a table named tbl_Students the 'On Click' event of these buttons on the Students Form have the following code:
Option Explicit
'Show all records button
Private Sub cmdShowAll_Click()
Dim task As String
task = "SELECT * FROM tbl_Students"
Me.frm_Students_subform.Form.RecordSource = task
Me.frm_Students_subform.Form.Requery
End Sub
'Clear displayed records button
Private Sub cmdClear_Click()
Dim task As String
task = "SELECT * FROM tbl_Students WHERE (StudentID) is null"
Me.frm_Students_subform.Form.RecordSource = task
Me.frm_Students_subform.Form.Requery
End Sub
'Refresh records button
Private Sub cmdRefresh_Click()
Me.frm_Students_subform.Form.Requery
End Sub
Currently, I'm using the exact same code, but with different respective subform names, in all my 16 forms. Is there a better, more efficient way to do it, with code reuse? Thanks.
Consider creating one generalized subroutine in a standard module that all 16 forms call passing needed parameters. Specifically use CurrentProject and Controls to reference objects dynamically by string.
Module save in a named standard module (not behind any form)
Option Explicit
Public Sub ProcessForms(task As String, mainform As String, subform As String)
On Error GoTo ErrHandle
CurrentProject.AllForms(mainform).Controls(subform).Form.RecordSource = task
CurrentProject.AllForms(mainform).Controls(subform).Requery
Exit Sub
ErrHandle:
Msgbox Err.Number & " - " & Err.Description, vbCritical, "RUNTIME ERROR"
Exit Sub
End Sub
Example Form single line calls
Option Explicit
'Show all records button
Private Sub cmdShowAll_Click()
Call ProcessForms("SELECT * FROM tbl_Students", _
"frm_students", "frm_Students_subform")
End Sub
'Clear displayed records button
Private Sub cmdClear_Click()
Call ProcessForms("SELECT * FROM tbl_Students WHERE (StudentID) IS NULL", _
"frm_students", "frm_Students_subform")
End Sub
'Refresh records button
Private Sub cmdRefresh_Click()
' Re-assigns same recordsource in line with others
Call ProcessForms(Me.Controls("frm_Students_subform").Form.RecordSource, _
"frm_students", "frm_Students_subform")
End Sub
Does anyone knows how to filter reports the right way within Access runtime mode?
The usual code with DoCmd doesn't work.
This is what I tried for the report:
Private Sub Befehl217_Click()
DoCmd.OpenReport "Tagesbericht", acViewPreview
End Sub
Private Sub Bezeichnungsfeld26_Click()
DoCmd.GoToControl "DateFilter"
DoCmd.RunCommand acCmdFilterMenu
End Sub
This didn't work. Access complained that "FilterMenu isn't available".
I tried to create a context menu but this only displayed me cut, copy and paste.
You confirmed your report includes a control named Bezeichnungsfeld26 and when the user clicks in that control, you want to bring up the filter menu for that control.
When the user clicks in that control, it has the focus, so there is no need for GoToControl. And you don't want to go to a different control if you want the user to filter on Bezeichnungsfeld26.
Disable the GoToControl line ...
Private Sub Bezeichnungsfeld26_Click()
'DoCmd.GoToControl "DateFilter"
DoCmd.RunCommand acCmdFilterMenu
End Sub
You can use the Filter property:
Me.Filter = "[YourField] = " & somevalue & ""
Me.FilterOn = True
or, to expand on your current method:
DoCmd.OpenReport "Tagesbericht", acViewPreview, , "[YourField] = " & somevalue & ""
If you filter for a date you must pass a properly formatted string expression for the date:
Dim FilterDate As Date
FilterDate = Date
DoCmd.OpenReport "Tagesbericht", acViewPreview, , "[DateFilter] = #" & Format(FilterDate, "yyyy\/mm\/dd") & "#"
I am developing a search form / report builder in Access 2007 called frmSearch. The existing search form works well, showing its results in a subform of a tabcontrol. Then I can click a button to show a report of all tests, which works fine. I want to modify the code to show a report for a single fldTestsID, but am stuck with the Form/Subform/Control path syntax.
There are two tabs: tabResultsTabular shows a subform frmResultsTabular as a query-like list of row. tabResultsRecord shows a subform frmResultsRecords, showing a single record. The report is rpt_TestDatasetExposureEffects. The report's underlying query is q_TestDatasetExposureEffect containing a field called fldTestsID.
So, the path is frmSearch to tabResultsRecord containing button cmdQAReportResults to frmResultsRecords to fldTestsID.
I see that that are other posts for the same error, but did not get them to work. The Access 2007 docs on DoCmd.OpenReport do not mention this specific instance.
Here is the code of the cmdQAReportResults click event including the options I have tried. It fails on the line strRptWhere =. The DoCmd.OpenReport syntax is working based on the Access 2007 docs.
Private Sub cmdQAReportResults_Click()
doQAReport
End Sub
Private Sub doQAReport()
'On Error GoTo Err_doQAReport 'comment during debugging
Dim stDocName As String ' report name
' Dim strRptSQL As String ' report SQL String
Dim strRptWhere As String ' report WHERE clause
stDocName = "rpt_TestDatasetExposureEffects"
'Override the recordsource to match the current record TestsID
' strRptSQL = "SELECT * FROM q_TestDatasetExposureEffects WHERE fldTestsID = " & fldTestsID
' DoCmd.OpenReport stDocName, acPreview
strRptWhere = "0 = 0"
strRptWhere = "fldTestsID = " & Me.Form![tabResultsRecord].fldTestsID.Value 'error 468
' other attempts follow
' strRptWhere = "fldTestsID = " & Forms("frmSearch").Controls("tabResultsRecord").Form.Controls("frmResultsRecords").Form.Controls("fldTestsID").Value
' strRptWhere = "fldTestsID = " & Me.Form.fldTestsID 'error 2465
DoCmd.OpenReport stDocName, acPreview, , strRptWhere
' Reports("rpt_TestDatasetExposureEffects").RecordSource = strRptSQL
Exit_doQAReport:
Exit Sub
Err_doQAReport:
MsgBox Err.Description
Resume Exit_doQAReport
End Sub
The correct syntax is:
Forms(<Parent Form>).<control container for subform>.form.<control>
In your case this might be:
Forms("frmSearch").frmResultsRecords.Form.fldTestsID
frmResultsRecords is the name of the subform, but is it also the name of the subform's container?
If not, replace it with the name of the control on the main form containing the subform.
I'm using VBA to dynamically load the content of a report, and depending on which report is selected from the control panel form I've built, the report's query might be filtered.
At the beginning of my Report_Open function, I have this:
Private Sub Report_Open(Cancel As Integer)
Me.Filter = "VIP=True"
Me.FilterOnLoad = True
Now, this was working when I started this project - I had commented out these lines, and when uncommenting them discovered that this doesn't work properly anymore. Instead of the filter being applied when the report is loaded, I have to reload the report for the filter to work - either by switching to print preview and switching back to report view, or by switching to design view and then back to report view (in design view, the selected filter does display in the properties pane).
I am loading the report using command buttons that allow the user to view, export to PDF, or print (opens in print preview). None of these commands cause the report to open with the filter applied - it has to be reloaded.
The commands I'm using to load the report are below for reference:
If (Action = "View") Then
DoCmd.OpenReport "Test", acViewReport
ElseIf (Action = "PDF") Then
DoCmd.OutputTo acOutputReport, "Test", acFormatPDF
ElseIf (Action = "Print") Then
DoCmd.OpenReport "Test", acViewPreview
End If
Ok, I have no idea why Me.Filter and Me.FilterOnLoad don't work, since from everything I have seen on MSDN and elsewhere, it should work. That being said, I figured out that I can use DoCmd.ApplyFilter instead:
'Check if VIP - add filter if necessary
If (getGlobal(1) = True) Then
DoCmd.ApplyFilter , "VIP = True"
End If
I'd still like to know why the other way was behaving so oddly, if anyone has any ideas...
As #David-W-Fenton suggested, use the WhereCondition with OpenReport instead of setting a Filter expression. Your WhereCondition can be the same string you were using for the Filter expression.
Also, if you give OpenReport an empty string as the WhereCondition, the effect is the same as no WhereCondition, so this (untested) code should work whether or not your getGlobal(1) returns True.
Dim strWhereCondition As String
Dim strReport As String
strReport = "Test"
If (getGlobal(1) = True) Then
strWhereCondition = "VIP = True"
End If
Select Case Action
Case "View"
DoCmd.OpenReport strReport, acViewReport, , strWhereCondition
Case "PDF"
DoCmd.OpenReport strReport, acViewReport, , strWhereCondition
DoCmd.OutputTo acOutputReport, , acFormatPDF
Case "Print"
DoCmd.OpenReport strReport, acViewPreview, , strWhereCondition
End Select
Notice also that DoCmd.OutputTo, without an ObjectName argument, uses the active object ... which will be the "Test" report in this case.
I am considering the use of a tab control on a parent form for which I would like to have around 20 tabs. Each tab I am considering the use of one or two separate sub forms. Each sub form will have varied complexity in coded logic. By taking this approach will I severally reduce the performance of my application? I am currently using this in MS Access 2003. I will expect an average of 15 users at any given time on the various forms.
Thoughts?
Yes, performance will be degraded slightly for each subform. One or three isn't too bad but twenty is definitely going to cause you performance issues.
Once you have the subform working to your satisfaction either save the Record Source as a query and give it a name or save the query SQL string. Then either paste the query name or the query SQL string in the VBA code in the tab control change event.
Private Sub TabCtl_Change()
On Error GoTo TabCtl_Change_Error
Select Case Me.TabCtl.Value
Case Me.pagPartsConsumed.PageIndex
If Me.PartsConsumedsbf.Form.RecordSource <> "Equipment - Parts Consumed sbf" Then _
Me.PartsConsumedsbf.Form.RecordSource = "Equipment - Parts Consumed sbf"
....
Now just to ensure that I don't accidentally leave some subform recordsources filled in slowing down the app on startup I check to see if the file the code is running is an MDB (instead of an MDE. The function is below) then display a message telling me I have to remove the recordsource.
If Not tt_IsThisAnMDE Then
If Me.PartsConsumedsbf.Form.RecordSource <> "" Then _
MsgBox "Record source of Equipment - Parts Consumed sbf not empty"
...
End If
Public Function tt_IsThisAnMDE()
On Error GoTo tagError
Dim dbs As Database
Set dbs = CurrentDb
Dim strMDE As String
On Error Resume Next
strMDE = dbs.Properties("MDE")
If Err = 0 And strMDE = "T" Then
' This is an MDE database.
tt_IsThisAnMDE = True
Else
tt_IsThisAnMDE = False
End If
Exit Function
tagError:
Call LogError(Application.CurrentObjectName, "")
Exit Function
End Function
Also in the form unload event I clear the Recourd Source as well.
Private Sub Form_Unload(Cancel As Integer)
On Error GoTo Form_Unload_Error
Me.PartsConsumedsbf.Form.RecordSource = ""
....
BTW I almost always would put each subform on a seperate tab. Also that many tab entries gets visusally unwieldy. When I had a similar question my fellow Access MVPs suggested using a listbox along the left hand side to control which subform is viewable.
Also each combo box and list box will also slightly degrade the performance. So if you have those on a subform then consider similar logic.
In addition to adding recordsets at runtime, I would generally only use one or two tabs and a number of controls to load various subforms into a subform control.
The text for the On Click event of the control might be:
=WhichPage([Form],"lblLocations")
Where WhichPage is a function with the following lines, amongst others:
Function WhichPage(frm, Optional LabelName = "")
<..>
Select Case LabelName
Case "lblLocations"
frm("sfrmAll").SourceObject = "sfrmLocations"
<...>
If necessary, the link child and link master fields can be changed at runtime. The link master field is best set to the name of a control, rather than a field, to avoid errors.
Me.sfrmAll.LinkChildFields = "LocationKey"
Me.sfrmAll.LinkMasterFields = "txtLocationKey"
To expand on Remou's answer...here is a sub I wrote that dynamically loads a form into a subform control. You pass in the name of the form in the call and it will load it into the subform of the Main form. The arguments map to the arguments of Docmd.OpenForm method of Access. If the main form that is hosting the subform control is not open...it just does a regular open of the form. Otherwise it loads it into the subform control. If a where clause was passed in it is used to filter the subform.
Public Sub MyOpenForm(FormName As String, _
Optional View As AcFormView = acNormal, _
Optional FilterName As String = vbNullString, _
Optional WhereCondition As String = vbNullString, _
Optional DataMode As AcFormOpenDataMode, _
Optional WindowMode As AcWindowMode, _
Optional OpenArgs As String)
On Error GoTo PROC_ERR
Dim frm As Form
Dim strNewForm As String
Dim strCurrentForm As String
Dim strNewTable As String
Dim fDoNotFilter As Boolean
Dim strActionText As String
Dim strID As String
If Not IsLoaded("frmMain") Then
DoCmd.OpenForm FormName:=FormName, View:=View, FilterName:=FilterName, WhereCondition:=WhereCondition, DataMode:=DataMode, WindowMode:=WindowMode, OpenArgs:=OpenArgs
Else
strCurrentForm = Forms![frmMain]![sfrMyForm].SourceObject
If strCurrentForm <> FormName Then
Forms![frmMain]![sfrMyForm].SourceObject = vbNullString
Forms![frmMain]![sfrMyForm].SourceObject = FormName
End If
If WhereCondition <> vbNullString Then
Forms![frmMain]![sfrMyForm].Form.Filter = WhereCondition
Forms![frmMain]![sfrMyForm].Form.FilterOn = True
End If
End If
PROC_EXIT:
Exit Sub
PROC_ERR:
MsgBox Err.Description
Resume PROC_EXIT
End Sub