What I want to do is very simple, but I'm totally new to VBA. I want to open a report and filter it by page number, so that I could open, say, only page 2. I already have a variable for how many pages it is. I just need to know what to put in the filter argument of the DoCmd.OpenReport function.
The overall goal is to export each page as a separate PDF. If I can do the above, I can do the rest, but if there's something really simple I'm missing, I'm open to suggestions. Thanks.
This is Access 2010 by the way.
To print single pages you can work with the printer object ( http://msdn.microsoft.com/en-us/library/ff837177.aspx ) and the PrintOut command ( http://msdn.microsoft.com/en-us/library/aa220516(v=office.11).aspx ), however, it seems likely that you have a natural break, such as a customer record, and these breaks can be used with the Where argument of OpenReport to easily get separate reports.
EDIT re comments
Very roughly:
sSQL = "SELECT SuitableField FROM Atable ORDER BY Something"
Set rs = CurrentDB.OpenRecordset(sSQL)
Do While Not rs.EOF
DoCmd.OpenReport "TheReport", acViewNormal, , _
"SuitableField='" & rs!SuitableField & "'"
rs.MoveNext
Loop
You may wish to do some work to ensure you are choosing reports with data in them.
Related
I am developing an Access database (using Office 2016) and have several tab controls which I want to display the number of records in the subform/subreport.
After a lot of searching etc I have it working for the subforms using a function which I call in the main forms current event (but in a seperate function so I can also call via a macro when I change the main forms record with a combo box, as it wasn't updating otherwise). The code I am using is:
Function ClientTotals()
Dim i As Integer
i = Form_sbfrm_ClientContacts.Recordset.RecordCount
Form_frm_Clients.ClientTabs.Pages("Contacts").Caption = "Contacts (" & i & ")"
End Function
This works perfectly for me and my tab name becomes "Contacts (No. of records)" but I can't get the syntax right to change this to work for a report, is it possible?
I have tried:
Function ClientTotals()
Dim i As Integer
i = Form_sbfrm_ClientContacts.Recordset.RecordCount
Form_frm_Clients.ClientTabs.Pages("Contacts").Caption = "Contacts (" & i & ")"
Dim j As Integer
j = Report_rpt_CurrentProjects.Recordset.RecordCount ' this line is highlighted with the debugger
Form_frm_Clients.ClientTabs.Pages("Current Projects").Caption = "Current Projects (" & j & ")"
End Function
As well as:
Dim j As Integer
j = rpt_CurrentProjects.Report.Recordset.RecordCount ' this line is highlighted with the debugger
Form_frm_Clients.ClientTabs.Pages("Current Projects").Caption = "Current Projects (" & j & ")"
and various others.
Another question I have is why is the syntax for the form "Form_sbfrm" etc and not using a "!". If I change to "!" it bugs out.
Thanks for your help, KAL
Thanks Delecron,
I think I will stick with the tabs for now as they are giving me exactly what I want, but remember what you have said for when I make future improvements if its a better way of doing it.
EDIT
Using what you have said I changed my VBA to a DCOUNT method:
Dim j As Integer
j = DCount("*", "qry_CurrentProjects", "FK_Project_Client_ID = Forms!Navigation!Navigationsubform.form!Client_ID")
Form_frm_Clients.ClientTabs.Pages("Current Projects").Caption = "Current Projects (" & j & ")"
This means my report tabs are now also working just how I wanted
I was getting in a muddle with the criteria/filter, hense the edit.
If Recordset is an old method I am assuming it would be best to replace my other code with the Dcount method?
Thanks again, KAL
Further EDIT
After doing this I could see that everytime the form was changed there was a slight flicker. Not bad but you could see there was a lot of calculation going on. Therefore I have changed my method to the following, and posted here for anyone looking at this in the future.
In the form footer a textbox with COUNT([Project_ID])
In my function
Dim j As Integer
j = Form_frm_Clients!rpt_CurrentProjects.Report!txt_CurrentProjectsCount.Value
Form_frm_Clients.ClientTabs.Pages("Current Projects").Caption = "Current Projects (" & j & ")"
Now I can see it is working quicker with no flicker.
Recordset if you need to return complex data, if you need one value, a total or a sum, Domain functions are the way to go. Don't overdue them though, having too many on a form or a report can start to bog down usability.
Glad I can help.
Recordset.Recordcount is a legacy feature that only worked in ADP files (Access front end's to a SQL database). Those are no longer supported.
If the report is based on 1 client only and there is no grouping then you can do this:
Click on the detail section and in Events create an event for On Paint. In there set
(Name of Page).Caption = DCount("*", "NAME OF QUERY/TABLE") or
(Name of Page).Caption = DCount("*", "NAME OF QUERY/TABLE", "Filter Expression") (Filter expression optional).
If the report is grouped where it will show a different page per client or date range or any other grouping this will not work since the Caption field is not data bound. You would have to add logic to the Dcount statement above to field by the current filter condition.
For example, say you have a database of 200 clients and you're running one big report on all of them, each page will get its own tab control per client, the syntax would be
(Name of Page).Caption = DCount("*", "ClientContacts, "ClientID = " & ClientID)
The better way to do it especially if you are grouping is get rid of the tab control and use databound controls. You could create a box around the information that would be in the tab page and put a textbox where the tab would go. Create a group header for however you are grouping the data. Create another invisible textbox in the group header and set the controlsource = Count([fieldname]) where fieldname is whatever you are grouping the data by (the inside brackets count).
Then in the textbox you created to simulate the tab, set the controlsource to the name of the invisible textbox.
Let me know if this helps.
I've come across a challenging problem with the DoCmd.OutputTo command in VBA Access 2013.
I've got below code which is basically to print a specific report from what is essentially a collection of records of reimbursements. The idea is that one would be able to [export] the active record (from the active form) to a PDF file and then add the scanned invoices to the PDF.
Thereto I would need to build the base file (i.e. the PDF where the invocies are added to) and then run the routine for adding the individual files.
Below code should create the initial PDF file:
Dim rpt as Report
filePath = "<some filepath>"
fName = Me!idDecl & " - (script).pdf"
filePath = filePath & fName
Set rpt = Report_qryDeclInvoice
With rpt
.Filter = "[fltID]= " & Me!id
.FilterOn = True
End With
DoCmd.OpenReport rpt.Name, acViewPreview, , "[fltID]= " & Me!id
DoCmd.OutputTo acOutputReport, rpt.Name, acFormatPDF, filePath, False
DoCmd.Close acReport, "qryDeclInvoice"
If I run the code -without breaks- the report opens as per the filtered argument, however, the subsequent command to output the record to PDF doesn't ?
That is, there appears a dialog box very briefly (can't read what it says) and then execution simply stops, no errors, no faultcodes just a clean break ?
Now for the interesting bit..
If I set a breakpoint on the DoCmd.OutputTo line, and execute the line with F8 the code works more or less flawlessly (see below)?? It appears that the break allows the preview routine to complete first and then run the OutputTo routine.
In addition to above challenge, on some of the reports (i.e. on some reimbursements) it works fine and the file is created yet on others it does not create the initial PDF at all and the code breaks without error codes or fault reporting. Without there being a distinguishable difference between the reports ?
I've tried delaying the OutputTo from the OpenReport function by sleeping it for 1000ms but that doesn't work (even upto 5000ms doesn't yield results)
Also if I remove the open preview line and just execute the OutputTo line, without opening the preview first, it works only when breaking and executing with F8 and again, only on some of the reports not all ?
It seems that the OutputTo command is -at least in my case- somewhat unreliable :-)
Any suggestions ??
OK, found out what was going on;
The faulty reports, that I mentioned, created multiple pages, based on grouping.
A section of VBA code in/on the report, re-created page numbers based on grouping (i.e. restarted page numbering for start of every group.) The execution of this VBA within the report (i.e. on open, or on page) interferes with the execution of the "OutputTo" routine. The above interfered with the OutputTo routine, causing it to break, though not sure why -yet-
Removal of all VBA code within the report solved the issue !!
Some added details:
No need for the preview anymore
set the filter of the report from VBA and open the report through the report object.name
Find below the working code:
Dim rpt as Report
filePath = "<somepath>"
fName = Me!idDecl & "-(script).pdf"
filePath = filePath & fName
'Debug.Print filePath
Set rpt = Report_qryInvoice
With rpt
.Filter = "[fltID]= " & Me!id
.FilterOn = True
End With
'Sleep (5000)
DoCmd.OutputTo acOutputReport, rpt.Name, acFormatPDF, filePath, False, , , acExportQualityScreen
I have a report (ReportX) that I wish to open from two different forms (FormA and FormB) in my database. I wish to do this because FormA and FormB address different aspects of my data, even if they ultimately get to the same output. ReportX is based on data from QueryX.
The problem I have is that QueryX would ideally filter the data based on the current RecordID in the current form. But I don't know how to accomplish this. I'd like to design QueryX so that the criteria for RecordID is essentially CurrentForm!RecordID, but research suggests that I cannot do this. Must I make separate but otherwise identical queries and reports for each form? Or is there a way to use VBA to define the query criteria when I click on the OpenReportX command button?
I already tried using the WHERE condition in the OpenReport command:
DoCmd.OpenReport "ReportX", acViewPreview, ,"RecordID = " & RecordID
but that did not display the results I wished. I need the report header to display/print for each RecordID and the page count in the page footer to reflect only the current/total pages of the RecordID in question. (In other words, if record 1 is one page, record 2 is two pages and record 3 is three pages, then ReportX, when displaying the first page of record 2, should say "Page 1 of 2" and not "Page 2 of 6.") So being able to display and print a single record properly using record filters would also solve my problem.
Which is the least cumbersome/most possible solution?
You should be able to accomplish this using the WHERE condition argument when you open a report:
DoCmd.OpenReport "rptName", acViewPreview, ,"RecordID = " & Me!RecordID
In the case where a I need more control over a Report's recordsource than what the Where condition argument can do for me, I will set the Reports RecordSource to be blank (after designing the report). Next I write code create the correct SQL statement in the Open Report button's Click event, followed by code to open the report and pass in the SQL as an opening argument for the report.
Private Sub cmdOpenReport_Click()
Dim sSQL as string
sSQL = "SELECT * FROM tblWhatever WHERE RecordID = " & Me!RecordID
DoCmd.OpenReport "rptReportName", acViewPreview, , , ,sSQL
End Sub
Then in the report's Open event I write code to set the recordsource:
Private Sub Report_Open(Cancel As Integer)
If IsNull(Me.OpenArgs) = False Then
Me.RecordSource = Me.OpenArgs
End If
End Sub
If neither of those accomplish what you want then you have an issue of a different sort. It's a little difficult for me to understand why you need All Records to show up in the header but only one record in the detail area. And how you expect to accomplish this. You might be best off trying to write a query first that gives you the exact results that your looking for so you know that it can be done.
As a side note, I actually use very few saved queries in my designs. It's not that there's anything wrong with using them, as the option is there for your convenience. I frequently use raw SQL on both forms and reports and set the RecordSource to the SQL on the Form or Reports Open or Load events.
Yes you can point to form data items in a query, and subsequently use that query in a report. The forms need to be open before the report runs. As far as having a header for each record, that is controlled in the settings of the report and how it displays the data.
In the Field, or Critera you can use the format:
[Forms]![frm_Process_Candidates]![QuestionTemplate]
Where frm_Process_Candidates would be the name assigned to your form, and QuestionTemplate is either the name of a control on your form, or a field from the data source of your form.
If you have a sub-form, there will be another [Form] call in the middle there.
[Forms]![frm_Dropdown_Admin]![frm_Dropdown_Admin_Detail].[Form]![text22]
Access should figure it out from there.
I have a form which allows the user to edit the properties of a filter via some combo boxes, then open a report. The report is opened with
DoCmd.OpenReport rptName, acViewReport, , whereClause, acWindowNormal
'whereClause = "Building = '005'" for instance
Some reports open fine, by which I mean they populate with the filtered info. Others, however, even though they are based off the same in-house template, IGNORE the filter all together and display a report based on ALL data (not the filtered data).
Why would a report ignore the filter? When I edit the reports in design mode after opening them with the form:
Working Report | Non Working Report
Filter: Building = '005' | Filter:
Filter On Load: No | Filter On Load: No
What could be causing the non-working report to not register the filter argument? There's no On Load VBA, nor any VBA, in these reports (except for an Export to PDF and a close button which is copy-paste for each report).
EDIT
Should have checked before, this happens regardless of whether or not the query driving the report is empty (ie the filter is never applied to some reports, regardless of blankness)
Not sure if the code will help, but:
Private Sub btnOpenSummary_Click()
If IsNull(Me.cboSummary) Then
MsgBox "Please select a building for the SUMMARY report."
Exit Sub
End If
strCrit = "Building = '" & Me.cboSummary & "'"
MsgBox strCrit
survArray = getSurveyArray()
For Each Survey In survArray
DoCmd.OpenReport Survey, acViewReport, , strCrit, acWindowNormal
Next Survey
DoCmd.OpenReport "Total Summary", acViewReport, , , , Me.cboSummary
End Sub
My fault.. there was code which had for some reason been linefed all the way down the code page and out of view. There was an On Open which played with the Control Source. It ended up being useless (as the control source only needed to be set once, not every time) but it was disabling the filter for some reason. Anybody else who may have this problem:
Make sure the control source is not being altered in your VBA for the report.
Thanks to those that helped, sorry my information was wrong.
My OnOpen code:
Private Sub Form_Open(Cancel as Integer)
Me.RecordSource = Me.Name
End Sub
It takes the name of the report, which corresponds to a name of a query, and puts it as the recordsource. (I have about 40 reports done this way, so it's dependent on names to make it fast to duplicate for different items).
Removing this made it work perfectly using Access 2010. Not sure if this was a problem specific to my setup or what, but, this change directly fixed it.
This is not a direct solution, but rather a workaround which should almost certainly work. Instead of applying filtering to the report, dynamically change the report data source by passing the where clause as a parameter.
To open the report use:
DoCmd.OpenReport rptName, acViewReport, , , acWindowNormal, whereClause
Note that the whereClause string is being passed as the OpenArgs parameter.
Then in the report VB:
Private Sub Report_Open(Cancel As Integer)
On Error GoTo ReportOpenError
If Not(IsNull(Me.OpenArgs)) Then
Me.RecordSource = Replace(Me.RecordSource,";"," WHERE " & Me.OpenArgs & ";")
End If
Exit Sub
ReportOpenError:
MsgBox "Unable to open the specified report"
Cancel = 1
End Sub
This solution assumes the report RecordSource is defined as a semicolon terminated SQL query (not a query name) and the record source does not already contain any WHERE, GROUP BY, etc., clauses. In those cases, it may be easier to redefine the query from scratch.
This problem may also occur when a report is copied and re-purposed. I had a report that was working fine then made a copy of it and was no longer able to filter it.
Perhaps there is a glitch in Access that causes it to ignore the filter when the report is copied under certain circumstances.
Solution: Instead of copying and re-naming the report, try creating a new report, linking the data source, and copying the fields back into place. If you are dealing with a new report, try re-creating it.
I have a custom build print dialog that is used for many reports. Its arguments are the report name, filter string, open args for the report etc. What I would like to do is to display the caption of the report specified for printing on the form. For performance reasons, I would rather not open the report, get the caption and close it again. I would rather get it from the database somehow without actually opening the report itself.
One thing that DOES work is to call the report using it's class name report_some_report.caption but there is no way to do this without with a report name stored in a variable. I would have expected Reports("some_report").caption to work also but it only works for open reports.
Is there a better way to do this or am I going to have to do something like the following? (Which works)
docmd.OpenReport "schedule_simple",acViewDesign,,,acHidden
strCaption = Reports("schedule_simple").Caption
docmd.Close acReport,"schedule_simple"
There's no way to get a report caption from a report without first opening the report or using the report class object (as you know). It's also worth noting that "lightweight" reports (ie, ones whose HasModule property = False) do not have class objects.
You have a couple of options.
You could create a local table with a RptName and RptCaption field and query that. Of course, then you need to keep it updated somehow.
You could write a function that "memoizes" the results, so that you only have to open a given report once each time the program runs. For example:
.
Function GetReportCaption(RptName As String) As String
Static RptCaptions As Collection
If RptCaptions Is Nothing Then Set RptCaptions = New Collection
On Error Resume Next
GetReportCaption = RptCaptions(RptName)
If Err.Number = 0 Then Exit Function
On Error GoTo 0
DoCmd.OpenReport RptName, acViewDesign, , , acHidden
RptCaptions.Add Reports(RptName).Caption, RptName
DoCmd.Close acReport, RptName
GetReportCaption = RptCaptions(RptName)
End Function