Is there some way I can detect, from within a report, which form invoked the report?
I have a form for selecting several criteria which are used to build the WhereCondition for various reports. In the report On Load, I test for the calling form being open. If it is open, I grab the selection criteria for display in the report header. If it isn't open, I display things like "Species: All" instead of, for example "Species: Blue-tailed damselfly". That works ok in this particular case but it feels a bit flaky. I would prefer to know what it was that invoked the report, in case in some future more complex system, a report could be invoked from different places. I know that a parent form is available from within a subform but that doesn't apply in this situation.
You have a form where the user selects the criteria used to build the WhereCondition which is used to filter the report. Do not ask the report to re-examine the form criteria in order to generate the appropriate header for itself. Build your header string in the same form procedure which builds the WhereCondition. Then pass that string as OpenArgs when you call OpenReport.
For example, if no restriction is placed on species:
strHeader = "Species: All"
Or for the selected species:
strHeader = "Species: Blue-tailed damselfly"
Then pass strHeader when you open the report:
DoCmd.OpenReport ReportName:="YourReport", _
View:=acViewReport, _
WhereCondition:=strWhere, _
OpenArgs:=strHeader
And in the report's load event you can check whether you got something in OpenArgs and use it:
If Len(Me.OpenArgs) > 0 Then
' do what you want with it '
End If
This approach makes the report independent from any particular form. The strategy does not break if you rename the current form or create a different form for report selection criteria.
You could even designate an appropriate header when report criteria are standardized in advance. For example, a command button captioned "View Yesterday's Additions":
strWhere = "[Date_Added] >= (Date() - 1) AND [Date_Added] < Date()"
strHeader = "Species Added Yesterday"
DoCmd.OpenReport ReportName:="YourReport", _
View:=acViewReport, _
WhereCondition:=strWhere, _
OpenArgs:=strHeader
Related
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 text field with a button that filters by a keyword in a form.
Private Sub Command93_Click()
Me.Filter = "(Review Like '*" & Me.Text94 & "*')OR (Status Like '*" & Me.Text94 & "*')"
Me.FilterOn = True
Me.Requery
End Sub
I then have a button that generates the report from that filter.
Private Sub Filter_Click()
DoCmd.OpenReport "rptName", acViewPreview, , Me.Filter
End Sub
The problem is that whenever I hit this button to generate the report, I get a pop up box asking me to Enter Parameter ID and it is asking this for review. If I take the review criteria out (by the way I have many more fields I just used review and status to illustrate the example) then the report generates without any pop up box. The review is part of a notinlist event which opens another form and stores that info in a table review, if that is relevant at all. The report will still generate when I click ok and leave the Enter Parameter ID box blank but I'd like to somehow bypass it for two reasons - the first being the fact that I need other people who are not familiar with access to be able to use it and the second is the idea that if I learn what is causing it I can understand the way access works better. Thanks.
Does your report have a field called Review? If not, you either need to change the recordsource of the report so it is joined with the table that has review, or change your filter so it refers to the field that the report has.
Here's what I've done:
I created a Parameter Query in which one of the crtiterias is Between [Start Date] and [End Date]. I then put that Parameter Query along with other bound fields and created a subform. So far, no problems and it works great.
But I'm having issues when it comes to printing or trying to convert the form into a PDF. As soon as I ask it to print, for example, the pop-ups from the parameter query will pop up asking me the dates again and even after I enter them in (again), it keeps asking me multiple times and cancels the print job.
How do I keep the query from essentially running when I'm trying to print what's on the screen? The same thing happens if I'm trying to create a PDF.
In my not so humble opinion, parameters are handled badly in MS Access.
I think having to enter the values in the query whenever it is run (unless it is a one off experimental query) is just wrong headed. It would be so much easier to automate reports like that if you could just pass the parameters.
Typically, I create a report without the parameters in the where clause of the query, and then pass in your own where condition which gets added on Remou's answer here
You could also alter the query in the report before you call it, but that's pretty hackey.
-- Edit --
I see the confusion. I interpreted what you were doing as a report (not a form).
What's likely happening is you that when it tries to render/format the print job, it's having to make multiple calls to form's record source. And that why it keeps asking you for that data.
From what I understand in your question, you have a query that looks like this:
select foo
from bar
where
yaddah_date between [Start Date] and [End Date]
And then you've used that query as the record source for a form that you are trying to latter print as a PDF. First of all you should probably create a report that is an analogue of the form. And then open the report for printing with a filter on it:
DoCmd.OpenReport "myReport", , , , _
"yaddah_date between " & txtStartDate & _
" and " & txtEndDate
(the last part is basically the filter/where clause that report will apply to the results of the query that's generating its data).
If you MUST print the form, you can do something similar
DoCmd.OpenForm "foo", acNormal, , _
"yaddah_date between " & txtStartDate & _
" and " & txtEndDate
Or you can set the filter property of the form/subform.
Lets say you are designing a sales report in microsoft access. You have 2 parameters: Startdate and EndDate.
I can think of 3 ways to prompt the end user for these parameters when the report is run.
Create a form with 2 Text Boxes and a button. The Button runs the report, and the report refers back to the form by name to get the start date and end date.
Create a form with 2 text boxes and a button. The button runs the report but sets the appropriate filter via the docmd.openreport command.
Base the report on a Query and define query parameters. Access will automatically prompt for those parameters one by one.
Which is the best way to go?
Which is best depends on a number of factors. First off, if you want to run the report without parameters, you don't want to define them in the recordsource of the report. This is also the problem with your first suggestion, which would tie the report to the form (from Access/Jet's point of view, there is little difference between a PARAMETER declared in the SQL of the recordsource and a form control reference; indeed, if you're doing it right, any time you use a form control reference, you will define it as a parameter!).
Of the three, the most flexible is your second suggestion, which means the report can be run without needing to open the form and without needing to supply the parameters at runtime.
You've left out one possibility that's somewhat in between the two, and that's to use the Form's OnOpen event to set the recordsource at runtime. To do that, you'd open the form where you're collecting your dates using the acDialog argument, hide the form after filling it out, and then write the recordsource on the fly. Something like this:
Private Sub Report_Open(Cancel As Integer)
Dim dteStart as Date
Dim dteEnd As Date
DoCmd.OpenForm "dlgGetDates", , , , , acDialog
If IsLoaded("dlgGetDates") Then
With Forms!dlgGetDates
dteStart = !StartDate
dteEnd = !EndDate
End With
Me.Recordsource = "SELECT * FROM MyTable WHERE DateField Between #" _
& dteStart & "# AND #" & dteEnd & "#;"
DoCmd.Close acForm, "dlgGetDates"
End If
End Sub
[Edit: IsLoaded() is a function provided by MS. It's so basic to all my Access coding I forget it's not a built-in function. The code:]
Function IsLoaded(ByVal strFormName As String) As Boolean
' Returns True if the specified form is open in Form view or Datasheet view.
Const conObjStateClosed = 0
Const conDesignView = 0
If SysCmd(acSysCmdGetObjectState, acForm, strFormName) <> conObjStateClosed Then
If Forms(strFormName).CurrentView <> conDesignView Then
IsLoaded = True
End If
End If
End Function
Now, there are certain advantages to this approach:
you can pre-fill the dates such that the user would have to click only OK.
you can re-use the form in multiple locations to collect date values for multiple forms, since the form doesn't care what report it's being used in.
However, there is no conditional way to choose whether to open the form or not.
Because of that, I often use a class module to store filtering criteria for reports, and then check if the relevant class module instance has filter values set at the time the OnOpen event fires. If the criteria are set, then I write the SQL for the recordsource on the fly, if they aren't set, I just use the default recordsource (and any filter passed in the OpenReport argument). In this setup, you would run the report only after you've collected the criteria and set up the class module instance. The advantage of this approach is that the report can be run in either of the two contexts, and none of the parts need to know anything about each other -- the report simply needs to understand the class module's interface.
I used to use the Docmd.Openreport Where clause, your option 2. But I've switched to using report filters in the reports Open event, your option 1, as it supports creating PDF files which the Where clause does not. See Microsoft Access Report Printing Criteria Selection Form at my website.
As far as the dates go in SQL strings I use the Format statement as at Return Dates in US #mm/dd/yyyy# format
Also your option three can be handled by putting in the following example in the criteria in the reports record source SQL query. Forms!Form_Name!Control_Name. This is generally simpler for folks who don't want to get into VBA.