9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
| Home | Excel | Access | Word | Outlook | FrontPage | VBA | Downloads 
| Index | 
Access and SQL 6 
Access Home 
C ontinue the 
tutorial: 
Part 1: 
Introduction 
Part 2: VBA & 
SQL 
Part 3: Examples 
Part 4: Building 
Queries 
Part 5: More 
Instant Queries 
Part 6: Dynamic 
Reports 
Printer Friendly 
Version 
Download the File 
Coding Note 
Sometimes lines of code 
can be very long! Where I 
have had to break a line of 
code to fit it on the page I 
have used the symbol 
to indicate that the line 
below is a continuation and 
that the code should be 
written as a single line 
<Blah blah blah 
blah> 
should be written: 
<Blah blah blah blah> 
Access and SQL 
Part 6: Dynamic Reports 
An Access Report is similar to an Access Form. It displays data from a record 
source you specify (a table or query) and you can customize the way the data is 
displayed through its design. In fact designing a report uses the many of the same 
techniques as designing a form. Forms are chiefly used for entering and editing 
data whereas reports are used for viewing and printing data. 
Reports are often seen as inflexible, with the decisions about which data is to be 
displayed being made at design time. Access users often think that if they want to 
see a different set of data they need to build a new report, or change the query on 
which the report is based. Not true! 
When you look at data in a table or form Access provides you with tools for filtering 
and sorting... 
But when you view a report in Print Preview these tools are absent, so you could be 
forgiven for thinking that it's too late to change what's on display. 
This tutorial will show you how to make use of a report's Filter and OrderBy 
properties to create dynamic reports which the user can filter and sort using a 
simple dialog, and see the result appear immediately in the report's Print Preview 
window. To see a different set of data all they have to do is specify a different filter, 
click the button, and the report will change to display the new data. 
In the example below the user filtered the report for a list of all the Female staff 
working in the Production and Shipping departments of the Birmingham and Cardiff 
offices [click the thumbnail to see a demonstration in a new window]: 
You can download a fully working sample file containing all the examples described 
here from the link at the bottom of this page. 
Building the Report 
Nothing special here, and no code required! You can use an existing report or 
create a new one for this project. You should bear in mind that the tool you are 
going to build will filter the report's data so it might be appropriate for you to base 
your report on an unfiltered table rather than on a query where the data is already 
filtered by the time it reaches the report. It's up to you. Familiarize yourself with 
the names of the fields displayed in the report and their data types. 
http://www.fontstuff.com/access/acctut19.htm 1/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
Building the Dialog Box 
The job of the dialog box is to allow the user to specify criteria for filtering and, in 
the more sophisticated examples sorting, the records displayed by the report. This 
means that you must choose which fields you want to offer the user and, in most of 
these examples, be able to provide a list of suitable search criteria to populate a 
combo box or list box for each field. 
To turn an Access form into a dialog box only requires you to change a few settings 
on the form's properties sheet. If you haven't built an Access form as a dialog box 
before click here to find out what you need to do. 
I have created several different dialog boxes, offering different levels of 
complexity. You can choose from simple examples using combo boxes, or more 
complex ones using multi-select list boxes: 
A simple dialog 
using combo 
boxes. 
Combo boxes and 
an option group. Multi-select list boxes. 
List boxes and sorting 
options. 
Or free-text filtering where the user can search on any text string: 
In my combo box and list box examples, the fields that I have chosen for filtering 
contain a known range of entries. One is a list of company Offices and another is a 
list of Departments. My database has tables containing lists of these entries and I 
am using these tables for the Row Source of the combo boxes and list boxes. Later 
in this tutorial I will show how to use wildcards to allow users to filter records using 
string fragments (such as records starting with a particular letter, or containing a 
particular group of letters). 
The important parts of the VBA code for each dialog box are 
described and explained below. To see the full code module for 
each dialog box follow the links marked "Code Listing 1" etc. 
These will display all the code used in the dialog box in a form 
that you can copy and paste into your own code window if you 
wish. 
A Simple Dialog Using Combo Boxes 
This is the simplest of the designs. The dialog has two combo boxes, one for each 
of the fields that I am allowing the user to filter. You can add as many combo boxes 
as you like providing you modify the code accordingly. There are also two 
command buttons: one for applying the filter and another for removing it. 
The illustration below shows the layout of the dialog and the names I used for the 
http://www.fontstuff.com/access/acctut19.htm 2/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
various objects... 
The aim of the code behind the Apply Filter button is to construct an SQL WHERE 
clause using the user's combo box choices. The SQL will then be applied to the 
report's Filter property and the FilterOn property of the report will be set to True so 
that it displays the filtered recordset. 
If the user makes a choice from a combo box the report should display only the 
records which have that value in the field. So if, for example, they choose New 
York from the Office combo box the code will have to construct SQL like this: 
[Office] = 'New York' 
But if the user leaves a combo box empty, I want the report to display all the 
records for that field, which would require SQL like this: 
[Office] Like '*' 
Coding the "Apply Filter" Button 
My code starts by declaring three string variables, one each to hold the SQL from 
the combo boxes and one to hold the combined criteria for the filter: 
Private Sub cmdApplyFilter_Click() 
Dim strOffice As String 
Dim strDepartment As String 
Dim strFilter As String 
If a combo box is empty its value is Null so I can use an If Statement to check 
whether or not the user made a choice and then construct the appropriate SQL: 
If IsNull(Me.cboOffice.Value) Then 
strOffice = "Like '*'" 
Else 
strOffice = "='" & Me.cboOffice.Value & "'" 
End If 
There is a similar If Statement for each combo box: 
If IsNull(Me.cboDepartment.Value) Then 
strDepartment = "Like '*'" 
Else 
strDepartment = "='" & Me.cboDepartment.Value & "'" 
End If 
Next comes a line which combines the criteria to form a WHERE clause for the filter: 
strFilter = "[Office] " & strOffice & " AND [Department] 
" 
& strDepartment 
http://www.fontstuff.com/access/acctut19.htm 3/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
And finally the filter is applied to the report and switched on: 
With Reports![rptStaff] 
.Filter = strFilter 
.FilterOn = True 
End With 
End Sub 
Anticipating Errors 
Before completing any project like this you should consider what might go wrong. 
All but the most basic of your VBA procedures should contain a simple error 
handler, but you can help things a great deal by anticipating what problems might 
arise. C hanges in the design of the report or the fields included might result in an 
error, but if you are fairly confident that this won't happen you can leave it for the 
error handler to take care of. This project requires the report to be open before the 
filter is applied. If it isn't then you get an error: 
Notice that the error doesn't distinguish between the report not being open and not 
existing at all. It can only "see" an object if it is open. It may be that the user 
simply forgot to open the report, or perhaps its name has been changed or 
someone might have deleted it. 
You could have the report open automatically when the dialog opens (or vice versa) 
by including the procedure: 
Private Sub Form_Load() 
DoCmd.OpenReport "rptStaff", acViewPreview 
End Sub 
But this would still need to take account of a missing or renamed report. My 
preferred method of dealing with this possibility is to check whether or not the 
report is open and advise the user accordingly. This code goes at the beginning of 
the cmdApplyFilter_Click procedure after the variable declarations: 
If SysCmd(acSysCmdGetObjectState, acReport, "rptStaff") <> 
acObjStateOpen Then 
MsgBox "You must open the report first." 
Exit Sub 
End If 
If a report with the name specified is not found in an "Open" state the If Statement 
displays a message to the user then terminates the procedure to prevent an error 
occurring. 
Coding the "Remove Filter" Button 
http://www.fontstuff.com/access/acctut19.htm 4/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
All that is required to remove the filter is a line which sets the FilterOn property of 
the report to False: 
Private Sub cmdRemoveFilter_Click() 
On Error Resume Next 
Reports![rptStaff].FilterOn = False 
End Sub 
The line On Error Resume Next tells Access to ignore any error which might occur 
if, for example, the report has already been closed by the user. 
You can review the complete code for the dialog box in Code Listing 1. 
^ top 
Adding an Option Group 
C ombo boxes are not the only way to offer the user a choice of predefined options. 
This dialog box, otherwise exactly the same as the previous example, makes use of 
an option group to allow the user to filter the records by gender. 
Option groups are useful when there are just a few items to choose from. Here 
there are three choices: Female, Male or Both... 
When you design your dialog box you can create an option group manually or with 
the help of the wizard. An Option Group consists of a Frame containing a collection 
of Option Buttons (sometimes called Radio Buttons). Alternatively you can use 
check boxes or toggle buttons. 
If you use the wizard to create the option group all the work is done for you. If you 
prefer to create the option group manually, first draw a frame on the form then 
name it and add a suitable caption to the label. Then add the individual buttons 
inside the frame. As you do this each button is automatically assigned a value. The 
first button you add has the value 1, the next has the value 2 and so on. In my 
example the values are: Female (1), Male (2), Both (3). You can assign any value 
you like to each button by changing its Option Value property. You must use whole 
numbers (data type Long Integer) and normally they should all be different. 
When option buttons are used as a group like this, only one member of the group 
can be selected at a time. The selected button passes its value to the group itself. It 
is a good idea to set a starting value for the group by specifying the frame's Default 
Value property. In my example the default value is 3, so that when the dialog box 
opens the Both button is already selected. 
Detecting and programming the user's choice is simple and is best done with a 
C ase Statement. First it is necessary to declare an additional variable to hold the 
Gender criteria: 
http://www.fontstuff.com/access/acctut19.htm 5/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
Dim strGender As String 
Then the C ase Statement can place the appropriate criteria string into the variable 
depending on the value of the option group: 
SelectCase Me.fraGender.Value 
Case 1 
strGender = "='F'" 
Case 2 
strGender = "='M'" 
Case 3 
strGender = "Like '*'" 
End Select 
The only other change to the code is the addition of the criteria for the Gender field 
to the filter string: 
strFilter = "[Office] " & strOffice & " AND [Department] " 
& strDepartment & " AND [Gender] " & strGender 
You can review the complete code for the dialog box in Code Listing 2. 
^ top 
Using Multi-Select List Boxes 
The combo boxes used in the previous examples have the disadvantage that the 
user can select only one item from each. This dialog box uses list boxes. These 
offer a multi-select facility so that the user can make several choices from each if 
they wish. 
Designing a list box is similar to designing a combo box in that is needs a Row 
Source from which to make its list. As before I have used tables for this. You must 
enable the multi-select feature by choosing a suitable mode for the Multi Select 
property of the list box. C hoose Simple or Extended depending on how you want 
the user to make their choices. 
If you want to know more about list box basics read my tutorial Making Sense of 
List Boxes. 
In this example the code has the job of creating a criteria string from the user's 
choices in each of the list boxes. If, for example, the user chooses London, 
Brussels and Paris from the Office list box it means that they want to see records 
for the London OR Brussels OR Paris offices. 
http://www.fontstuff.com/access/acctut19.htm 6/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
There are different ways this can be represented in SQL. One way is to use the 
keyword "OR" like this: 
[Office] = 'London' OR [Office] = 'Brussels' OR [Office] = 
'Paris' 
but this can result in very long criteria strings, so I prefer to use an SQL "IN" clause 
like this: 
[Office] IN('London','Brussels','Paris') 
To do this I use a code statement that loops through the selected items in the list 
box and strings them together, separated by commas: 
Dim varItem As Variant 
For Each varItem In Me.lstOffice.ItemsSelected 
strOffice = strOffice & ",'" & 
Me.lstOffice.ItemData(varItem) _ 
& "'" 
Next varItem 
If the resulting string has no length (i.e. it is empty because the user didn't select 
anything) the code creates criteria which shows all records. But if the user did 
make a selection my code creates an IN clause listing the selections, after first 
removing the leading comma from the front of the string: 
If Len(strOffice) = 0 Then 
strOffice = "Like '*'" 
Else 
strOffice = Right(strOffice, Len(strOffice) - 1) 
strOffice = "IN(" & strOffice & ")" 
End If 
Each list box requires a similar set of code statements. The code for the Gender 
option group and the code that creates the filter string are exactly the same as in 
the previous example. 
You can review the complete code for the dialog box in Code Listing 3. 
^ top 
Offering the Option to Sort the Records 
In many cases sorting the records in a report is just as important as filtering them. 
If you don't want to offer a sorting option to the user, you can predefine the sort 
order by basing the report on a query or use an SQL statement incorporating an 
ORDER BY clause as the report's Record Source. For example: 
SELECT * FROM tblStaff ORDER BY [Office], [Department]; 
will display the records sorted first by the Office field then by the Department field, 
both in ascending order. 
Adding sorting options to the dialog increases its power a great deal but to make it 
a really useful and user-friendly tool requires a bit of thought and quite a lot of 
code (although much of it is quite simple and repeated several times). Here's how 
the dialog box looks: 
http://www.fontstuff.com/access/acctut19.htm 7/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
I have put the sorting tools (three combo boxes and three command buttons) inside 
a frame purely for design effect. It has no other practical implications. The features 
of the sorting tools are as follows: 
There are three combo boxes, allowing the user to sort by up to three 
fields at a time. 
The combo boxes have a hierarchy (1, 2, 3) by which the sort is 
prioritised. The records are sorted first by the choice in combo box 1, 
then by the choice in combo box 2, then by the choice in combo box 3. 
Each combo box contains a list of fields from which the user can choose 
to sort by, plus the option "Not Sorted". 
If the "Not Sorted" option is chosen any combo boxes lower in the 
hierarchy are automatically disabled and their values set to "Not 
Sorted" too. 
If the user chooses a sort field, and the value in the next combo box is 
disabled, then the next combo box becomes enabled with its value 
remaining as "Not Sorted". 
The user can not choose the same field in two different combo boxes. 
There are three command buttons, corresponding to the three combo 
boxes, by which the user can specify the sort direction for each field. 
When a field is chosen the corresponding command button is enabled, 
but when a combo box has the value "Not Sorted" the corresponding 
command button is disabled. 
C licking the command button changes its caption from Ascending to 
Descending. C licking it again changes it back again. The default caption 
is Ascending. 
As you can see, there is quite a lot of functionality here and that requires quite a lot 
of code, but it is fairly simple and much of it is repeated for each combo box so the 
task of creating an ORDER BY clause isn't as daunting as it might seem. 
Filling The Combo Box Lists 
To create the list, each combo box has its Row Source Type property set to Value 
http://www.fontstuff.com/access/acctut19.htm 8/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
List. This allows a list of items to be typed directly into the Row Source property 
textbox: 
Separated by semicolons, each item becomes an option on the combo box list: 
Coding the Combo Boxes 
Each combo box has three jobs to do when the user makes a choice: 
1. It has to check that the choice is not the same as a choice that has 
already been made. 
2. It has to disable the combo boxes below it in the hierarchy if the user 
chose "Not Sorted" and set their values to "Not Sorted". 
3. If the user chose "Not Sorted" it has to disable its own sort direction 
command button and any below it in the hierarchy and set their 
captions to "Ascending". 
The first job is best handled by the combo box's Before Update event. This is 
because, if the user's choice is not acceptable, the update can be cancelled easily. 
The After Update event doesn't offer this facility. Here's the code for the first combo 
box: 
Private Sub cboSortOrder1_BeforeUpdate(Cancel As Integer) 
If Me.cboSortOrder1.Value <> "Not Sorted" Then 
If Me.cboSortOrder1.Value = Me.cboSortOrder2.Value _ 
Or Me.cboSortOrder1.Value = Me.cboSortOrder3.Value 
Then 
MsgBox "You already chose that item." 
Cancel = True 
Me.cboSortOrder1.Dropdown 
End If 
End If 
End Sub 
There are two If Statements, one inside the other (this is referred to as "nested"). 
The first If Statement checks to see if the user chose "Not Sorted". If they did, 
nothing happens because it's OK if more than one combo box has this value. But if 
any other value has been chosen it checks to see if the chosen value matches any 
of the other combo boxes. If it does a message is shown to the user, their action is 
undone (by cancelling the event) and the combo box list is dropped to prompt the 
user to make a different choice. 
The Before Update event procedure of each combo box has similar code with the 
numbers changed so that each combo box checks the other two. 
http://www.fontstuff.com/access/acctut19.htm 9/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
The remaining jobs are handled by the combo boxes' Change event: 
Private Sub cboSortOrder1_Change() 
Dim i As Integer 
If Me.cboSortOrder1.Value = "Not Sorted" Then 
For i = 2 To 3 
With Me.Controls("cboSortOrder" & i) 
.Enabled = False 
.Value = "Not Sorted" 
End With 
Next i 
For i = 1 To 3 
With Me.Controls("cmdSortDirection" & i) 
.Enabled = False 
.Caption = "Ascending" 
End With 
Next i 
Else 
Me.cboSortOrder2.Enabled = True 
Me.cmdSortDirection1.Enabled = True 
End If 
End Sub 
When the user changes the value of the combo box an If Statement checks the new 
value to see if it is "Not Sorted". If it is, a For...Next loop disables the other combo 
boxes and sets their values to "Not Sorted". Then another For...Next loop disables 
the command buttons and sets their captions to "Ascending". 
If the user made a choice other than "Not Sorted" then its own sort direction 
command button is enabled and the combo box below is enabled. 
As before, the code is similar for the remaining combo boxes. Each one referring to 
the appropriate controls. 
NOTE: One of the aims of writing good code is to make it as brief 
as possible, and in the above example I have demonstrated this 
with the use of loops and With Statements. Using loops avoids 
having to repeat chunks of code. The variable "i" not only tells 
Access how many times to run the loop but also serves to identify 
which control is being manipulated each time. I can do this 
because I gave the controls names which included numbers. In 
the first loop, the first time it runs the value of "i" is 2 so 
Me.Controls("cboSortOrder” & i) means the same as 
Me.cboSortOrder2 and so on. Although there are only three 
combo boxes in this example the loop would require no additional 
code for any number of combo boxes. I would just have to 
change the upper limit of the value of "i". With Statements also 
help by removing the need to repeat the first part of a long code 
line when writing several consecutive statements all referring to 
the same thing. 
Coding the Sort Direction Command Buttons 
I want to make the selection of a sort direction (i.e. ascending or descending) as 
easy as possible. Since there are only two choices it isn't worth using combo boxes, 
and an option buttons would take up too much room. So I borrowed Microsoft's idea 
(take a look at the Query Wizard!) and used command buttons. C licking one of the 
command buttons doesn't do anything other than change it's caption. The code 
which later builds the SQL statement for the report filter will read the button's 
caption to determine which way to sort the records. The code is simple and the 
same for each button (with the appropriate control names inserted): 
http://www.fontstuff.com/access/acctut19.htm 10/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
Private Sub cmdSortDirection1_Click() 
If Me.cmdSortDirection1.Caption = "Ascending" Then 
Me.cmdSortDirection1.Caption = "Descending" 
Else 
Me.cmdSortDirection1.Caption = "Ascending" 
End If 
End Sub 
Coding the Apply Filter Command Button 
The code used to create the filter string is exactly the same as in the previous 
example, but this time there is an additional task: to create an Order By string that 
will be applied to the OrderBy property of the report. 
In the same way that the code generates a criteria string, applies it to the Filter 
property of the report and then activates it by setting the FilterOn property to True, 
it now has to generate a sort string, apply it to the OrderBy property of the report 
and activate it by setting the OrderByOn property to True. 
If the user chose to sort the data by LastName then by FirstName, here's what a 
completed OrderBy string should look like: 
[LastName], [FirstName] 
Fields are simply listed in the desired order, separated by commas. I am in the 
habit of enclosing field names in square brackets as shown here but this is only 
absolutely necessary when the names contain spaces. 
Unless specified otherwise, each field is sorted in ascending order (A-Z, 1-9) but if 
a field is to be sorted in descending order (Z-A, 9-1) the keyword DESC is used. 
The sort direction must be specified for each field. if it is omitted it is assumed that 
the field is to be sorted in ascending order. So, if the user chose to sort the data by 
LastName then by FirstName both in descending order the string should look like: 
[LastName] DESC, [FirstName] DESC 
Here is the code used to build the sort string: 
If Me.cboSortOrder1.Value <> "Not Sorted" Then 
strSortOrder = "[" & Me.cboSortOrder1.Value & "]" 
If Me.cmdSortDirection1.Caption = "Descending" Then 
strSortOrder = strSortOrder & " DESC" 
End If 
If Me.cboSortOrder2.Value <> "Not Sorted" Then 
strSortOrder = strSortOrder & ",[" & 
Me.cboSortOrder2.Value & "]" 
If Me.cmdSortDirection2.Caption = "Descending" Then 
strSortOrder = strSortOrder & " DESC" 
End If 
If Me.cboSortOrder3.Value <> "Not Sorted" Then 
strSortOrder = strSortOrder & ",[" & 
Me.cboSortOrder3.Value & "]" 
If Me.cmdSortDirection3.Caption = "Descending" 
Then 
strSortOrder = strSortOrder & " DESC" 
End If 
End If 
End If 
End If 
The code consists of a series of nested If Statements, each one depending on the 
http://www.fontstuff.com/access/acctut19.htm 11/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
result of the previous one. First of all, an If Statement checks to see if the value of 
the first combo box is something other than "Not Sorted". If the value is "Not 
Sorted" the If Statement finishes and the OrderBy string remains empty. But if the 
first combo box contains a field name then it is placed, surrounded by square 
brackets, into a variable named strSortOrder. A second If Statement then looks at 
the caption of the first command button and if it reads "Descending" the keyword 
DESC is added to the string in the strSortOrder variable. 
The process is repeated for each combo box and finishes if the value "Not Sorted" 
is found, but if a field name is found its name and the sort direction is noted and the 
code moves to the next combo box. 
The last section of the code applies and activates both the filter string and the sort 
string: 
With Reports![rptStaff] 
.Filter = strFilter 
.FilterOn = True 
.OrderBy = strSortOrder 
.OrderByOn = True 
End With 
Coding the Remove Filter Command Button 
In the earlier examples the this button has simply set the FilterOn property of the 
report to False. Now it has to do the same for the OrderByOn property: 
With Reports![rptStaff] 
.FilterOn = False 
.OrderByOn = False 
End With 
But this time I have added some extra functionality. The following statement 
removes the selections from the Office list box (there is a similar one for the 
Department list box): 
For Each varItem In Me.lstOffice.ItemsSelected 
Me.lstOffice.Selected(varItem) = False 
Next varItem 
The option group is reset to its original value (Gender = Both): 
Me.fraGender.Value = 3 
Finally the following loop sets the value of each combo box to "Not Sorted" and 
disables it, then sets the caption of each sort direction command button to 
"Ascending" and disables it. The last line enables the first combo box ready for the 
user to make their next choice. 
For i = 1 To 3 
Me.Controls("cboSortOrder" & i).Value = "Not Sorted" 
Me.Controls("cboSortOrder" & i).Enabled = False 
Me.Controls("cmdSortDirection" & i).Enabled = False 
Me.Controls("cmdSortDirection" & i).Caption = 
"Ascending" 
Next i 
Me.cboSortOrder1.Enabled = True 
You can review the complete code for the dialog box in Code Listing 4. 
^ top 
http://www.fontstuff.com/access/acctut19.htm 12/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
Filtering with Free Text 
Each example so far has offered the user a fixed range of choices for filtering the 
report's records. That is fine if the fields that are being filtered contain a known 
range of items. But you might need to offer the facility to filter using free text, i.e. 
allowing the user to enter anything they want. 
This final example shows how you can allow the user to enter any string of text into 
a text box and choose how that string is used to filter the records... 
For each available field the dialog box has a text box and a set of option buttons. 
The user can enter a letter or a string of text in the text box and make a choice 
from the option group to specify how the string is used in the filter. If the user 
leaves a text box empty then all records are returned for that field. 
Supposing the user might enter the string mar into the FirstName textbox. The 
results they get will depend upon the choices they make from the option buttons: 
C hoosing Starts with... would return: Martin, Mark, Margaret, Mariana etc. 
C hoosing Contains... would return: Martin, Anne-Marie, Marlon, Omar etc. 
C hoosing Ends with... would return: Omar, Dagmar, Valdemar etc. 
C hoosing Equals... would return only: Mar 
The method used by the filter is to combine the string with one or more asterisk 
wildcards. In SQL the asterisk (*) combined with a string and the keyword LIKE 
represents any string of text: 
Like "g*" represents the letter g followed by any string of text. 
Like "*g*" represents any string of text containing the letter g. 
Like "*g" represents any string of text ending with the letter g. 
The code used to create the filter string is quite simple: 
If IsNull(Me.txtFirstName.Value) Then 
strFirstName = "Like '*'" 
Else 
SelectCase Me.fraFirstName.Value 
Case 1 
strFirstName = "Like '" & Me.txtFirstName.Value 
& "*'" 
Case 2 
strFirstName = "Like '*" & Me.txtFirstName.Value 
& "*'" 
Case 3 
http://www.fontstuff.com/access/acctut19.htm 13/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
strFirstName = "Like '*" & Me.txtFirstName.Value 
& "'" 
Case 4 
strFirstName = "= '" & Me.txtFirstName.Value & 
"'" 
End Select 
End If 
An If Statement looks for an entry in the text box. If the text box is empty (i.e. its 
value is Null) a filter expression is created that will return all the records. If there is 
an entry in the text box a C ase Statement creates the required filter expression 
depending on the value of the option group. 
This is repeated for each field and the filter expressions are combined to create a 
filter string: 
strFilter = "[FirstName] " & strFirstName & _ 
" AND [LastName] " & strLastName 
The filter string is applied to the report and activated in the same way as in the 
previous examples. 
You can review the complete code for the dialog box in Code Listing 5. 
^ top 
Ideas for Further Improvements 
The examples I have shown are each designed to illustrate particular techniques. 
Remember you can mix and match as many of the different tools as you need but 
remember to always think about the user. These tools are meant to make their 
lives easier - so keep it simple! 
Opening the Report Automatically 
The above examples include a few lines of code that look for the open report and 
warn the user if it isn't found: 
If SysCmd(acSysCmdGetObjectState, acReport, "rptStaff") <> 
acObjStateOpen Then 
MsgBox "You must open the report first." 
Exit Sub 
End If 
But you could save them the trouble by opening the report automatically as soon as 
they press the Apply Filter button: 
If SysCmd(acSysCmdGetObjectState, acReport, "rptStaff") <> 
acObjStateOpen Then 
DoCmd.OpenReport "rptStaff", acViewPreview 
End If 
Or perhaps you could ask the user what they want to do: 
Dim Response As VbMsgBoxResult 'For Access 97 use: As 
Variant 
If SysCmd(acSysCmdGetObjectState, acReport, "rptStaff") <> 
acObjStateOpen Then 
Response = MsgBox("The report is not open." _ 
http://www.fontstuff.com/access/acctut19.htm 14/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
& vbCrLf & "Do you want to open it now?" _ 
, vbQuestion + vbYesNoCancel) 
SelectCase Response 
Case vbYes 
DoCmd.OpenReport "rptStaff", acViewPreview 
Case vbNo 
Exit Sub 
Case vbCancel 
DoCmd.Close acForm, Me.Name 
Exit Sub 
End Select 
End If 
Automate the Report's Title 
Most reports need some sort of descriptive title and you might like the title to 
change to reflect the records displayed. You can use the user's choices in the 
combo boxes, list boxes and other controls to construct a variety of labels to place 
in the header or footer section of the report. 
For each title you need to place an empty control in the report's Report Header or 
Report Footer section. If these sections are not already shown on your report when 
you view it in Design View, go to View > Report Header/Footer to switch them 
on. 
You can either add a Label control (and use the code to specify its Caption 
property) or an unbound TextBox control (and use the code to specify its Value). 
My preference is to use a TextBox because it has a Can Grow property which, when 
set to Yes, allows the control to increase in height to accommodate the amount of 
text it contains. Also, its value can be set when the report is in Preview view (unlike 
a label caption which requires the report to be switched into Design view for 
editing). This is useful if you aren't sure how long the title is going to be. Make sure 
you draw the control as wide as the report permits because the width doesn't 
change, only the height: 
The text of your title can be made up of plain text combined with values extracted 
from the dialog box controls. It can be applied to the report at the same time as the 
filter. Here's an example of the sort of code you might use (the textbox control is 
named txtReportTitle): 
With Reports![rptStaff] 
.Filter = strFilter 
.FilterOn = True 
.txtReportTitle.Value = _ 
"Staffing Report - " & Format(Date, "dd mmm yyyy") _ 
& vbCrLf & "Office: " & Me.cboOffice.Value _ 
& vbCrLf & "Department: " & Me.cboDepartment.Value 
http://www.fontstuff.com/access/acctut19.htm 15/16
9/8/2014 Access Tips: Access and SQL Part 6: Dynamic Reports 
End With 
Note the use of vbCrLf in the code to add line breaks to the finished text. Here's 
the result: 
Download the File 
You can download a fully-working database file containing this tutorial's completed 
project in either Access 97 or Access 2000 format. The database contains all five 
dialog boxes described in the tutorial and a sample report and tables. The files are 
provided in Access 97 and Access 2000 format. 
AccessAndSQL6_97.zip [107KB] Access 97 format. 
AccessAndSQL6_2000.zip [115KB] Access 2000 format. 
©2004 Martin Green. All rights reserved. martin@fontstuff.com 
^ top 
http://www.fontstuff.com/access/acctut19.htm 16/16

Access tips access and sql part 6 dynamic reports

  • 1.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports | Home | Excel | Access | Word | Outlook | FrontPage | VBA | Downloads | Index | Access and SQL 6 Access Home C ontinue the tutorial: Part 1: Introduction Part 2: VBA & SQL Part 3: Examples Part 4: Building Queries Part 5: More Instant Queries Part 6: Dynamic Reports Printer Friendly Version Download the File Coding Note Sometimes lines of code can be very long! Where I have had to break a line of code to fit it on the page I have used the symbol to indicate that the line below is a continuation and that the code should be written as a single line <Blah blah blah blah> should be written: <Blah blah blah blah> Access and SQL Part 6: Dynamic Reports An Access Report is similar to an Access Form. It displays data from a record source you specify (a table or query) and you can customize the way the data is displayed through its design. In fact designing a report uses the many of the same techniques as designing a form. Forms are chiefly used for entering and editing data whereas reports are used for viewing and printing data. Reports are often seen as inflexible, with the decisions about which data is to be displayed being made at design time. Access users often think that if they want to see a different set of data they need to build a new report, or change the query on which the report is based. Not true! When you look at data in a table or form Access provides you with tools for filtering and sorting... But when you view a report in Print Preview these tools are absent, so you could be forgiven for thinking that it's too late to change what's on display. This tutorial will show you how to make use of a report's Filter and OrderBy properties to create dynamic reports which the user can filter and sort using a simple dialog, and see the result appear immediately in the report's Print Preview window. To see a different set of data all they have to do is specify a different filter, click the button, and the report will change to display the new data. In the example below the user filtered the report for a list of all the Female staff working in the Production and Shipping departments of the Birmingham and Cardiff offices [click the thumbnail to see a demonstration in a new window]: You can download a fully working sample file containing all the examples described here from the link at the bottom of this page. Building the Report Nothing special here, and no code required! You can use an existing report or create a new one for this project. You should bear in mind that the tool you are going to build will filter the report's data so it might be appropriate for you to base your report on an unfiltered table rather than on a query where the data is already filtered by the time it reaches the report. It's up to you. Familiarize yourself with the names of the fields displayed in the report and their data types. http://www.fontstuff.com/access/acctut19.htm 1/16
  • 2.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports Building the Dialog Box The job of the dialog box is to allow the user to specify criteria for filtering and, in the more sophisticated examples sorting, the records displayed by the report. This means that you must choose which fields you want to offer the user and, in most of these examples, be able to provide a list of suitable search criteria to populate a combo box or list box for each field. To turn an Access form into a dialog box only requires you to change a few settings on the form's properties sheet. If you haven't built an Access form as a dialog box before click here to find out what you need to do. I have created several different dialog boxes, offering different levels of complexity. You can choose from simple examples using combo boxes, or more complex ones using multi-select list boxes: A simple dialog using combo boxes. Combo boxes and an option group. Multi-select list boxes. List boxes and sorting options. Or free-text filtering where the user can search on any text string: In my combo box and list box examples, the fields that I have chosen for filtering contain a known range of entries. One is a list of company Offices and another is a list of Departments. My database has tables containing lists of these entries and I am using these tables for the Row Source of the combo boxes and list boxes. Later in this tutorial I will show how to use wildcards to allow users to filter records using string fragments (such as records starting with a particular letter, or containing a particular group of letters). The important parts of the VBA code for each dialog box are described and explained below. To see the full code module for each dialog box follow the links marked "Code Listing 1" etc. These will display all the code used in the dialog box in a form that you can copy and paste into your own code window if you wish. A Simple Dialog Using Combo Boxes This is the simplest of the designs. The dialog has two combo boxes, one for each of the fields that I am allowing the user to filter. You can add as many combo boxes as you like providing you modify the code accordingly. There are also two command buttons: one for applying the filter and another for removing it. The illustration below shows the layout of the dialog and the names I used for the http://www.fontstuff.com/access/acctut19.htm 2/16
  • 3.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports various objects... The aim of the code behind the Apply Filter button is to construct an SQL WHERE clause using the user's combo box choices. The SQL will then be applied to the report's Filter property and the FilterOn property of the report will be set to True so that it displays the filtered recordset. If the user makes a choice from a combo box the report should display only the records which have that value in the field. So if, for example, they choose New York from the Office combo box the code will have to construct SQL like this: [Office] = 'New York' But if the user leaves a combo box empty, I want the report to display all the records for that field, which would require SQL like this: [Office] Like '*' Coding the "Apply Filter" Button My code starts by declaring three string variables, one each to hold the SQL from the combo boxes and one to hold the combined criteria for the filter: Private Sub cmdApplyFilter_Click() Dim strOffice As String Dim strDepartment As String Dim strFilter As String If a combo box is empty its value is Null so I can use an If Statement to check whether or not the user made a choice and then construct the appropriate SQL: If IsNull(Me.cboOffice.Value) Then strOffice = "Like '*'" Else strOffice = "='" & Me.cboOffice.Value & "'" End If There is a similar If Statement for each combo box: If IsNull(Me.cboDepartment.Value) Then strDepartment = "Like '*'" Else strDepartment = "='" & Me.cboDepartment.Value & "'" End If Next comes a line which combines the criteria to form a WHERE clause for the filter: strFilter = "[Office] " & strOffice & " AND [Department] " & strDepartment http://www.fontstuff.com/access/acctut19.htm 3/16
  • 4.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports And finally the filter is applied to the report and switched on: With Reports![rptStaff] .Filter = strFilter .FilterOn = True End With End Sub Anticipating Errors Before completing any project like this you should consider what might go wrong. All but the most basic of your VBA procedures should contain a simple error handler, but you can help things a great deal by anticipating what problems might arise. C hanges in the design of the report or the fields included might result in an error, but if you are fairly confident that this won't happen you can leave it for the error handler to take care of. This project requires the report to be open before the filter is applied. If it isn't then you get an error: Notice that the error doesn't distinguish between the report not being open and not existing at all. It can only "see" an object if it is open. It may be that the user simply forgot to open the report, or perhaps its name has been changed or someone might have deleted it. You could have the report open automatically when the dialog opens (or vice versa) by including the procedure: Private Sub Form_Load() DoCmd.OpenReport "rptStaff", acViewPreview End Sub But this would still need to take account of a missing or renamed report. My preferred method of dealing with this possibility is to check whether or not the report is open and advise the user accordingly. This code goes at the beginning of the cmdApplyFilter_Click procedure after the variable declarations: If SysCmd(acSysCmdGetObjectState, acReport, "rptStaff") <> acObjStateOpen Then MsgBox "You must open the report first." Exit Sub End If If a report with the name specified is not found in an "Open" state the If Statement displays a message to the user then terminates the procedure to prevent an error occurring. Coding the "Remove Filter" Button http://www.fontstuff.com/access/acctut19.htm 4/16
  • 5.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports All that is required to remove the filter is a line which sets the FilterOn property of the report to False: Private Sub cmdRemoveFilter_Click() On Error Resume Next Reports![rptStaff].FilterOn = False End Sub The line On Error Resume Next tells Access to ignore any error which might occur if, for example, the report has already been closed by the user. You can review the complete code for the dialog box in Code Listing 1. ^ top Adding an Option Group C ombo boxes are not the only way to offer the user a choice of predefined options. This dialog box, otherwise exactly the same as the previous example, makes use of an option group to allow the user to filter the records by gender. Option groups are useful when there are just a few items to choose from. Here there are three choices: Female, Male or Both... When you design your dialog box you can create an option group manually or with the help of the wizard. An Option Group consists of a Frame containing a collection of Option Buttons (sometimes called Radio Buttons). Alternatively you can use check boxes or toggle buttons. If you use the wizard to create the option group all the work is done for you. If you prefer to create the option group manually, first draw a frame on the form then name it and add a suitable caption to the label. Then add the individual buttons inside the frame. As you do this each button is automatically assigned a value. The first button you add has the value 1, the next has the value 2 and so on. In my example the values are: Female (1), Male (2), Both (3). You can assign any value you like to each button by changing its Option Value property. You must use whole numbers (data type Long Integer) and normally they should all be different. When option buttons are used as a group like this, only one member of the group can be selected at a time. The selected button passes its value to the group itself. It is a good idea to set a starting value for the group by specifying the frame's Default Value property. In my example the default value is 3, so that when the dialog box opens the Both button is already selected. Detecting and programming the user's choice is simple and is best done with a C ase Statement. First it is necessary to declare an additional variable to hold the Gender criteria: http://www.fontstuff.com/access/acctut19.htm 5/16
  • 6.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports Dim strGender As String Then the C ase Statement can place the appropriate criteria string into the variable depending on the value of the option group: SelectCase Me.fraGender.Value Case 1 strGender = "='F'" Case 2 strGender = "='M'" Case 3 strGender = "Like '*'" End Select The only other change to the code is the addition of the criteria for the Gender field to the filter string: strFilter = "[Office] " & strOffice & " AND [Department] " & strDepartment & " AND [Gender] " & strGender You can review the complete code for the dialog box in Code Listing 2. ^ top Using Multi-Select List Boxes The combo boxes used in the previous examples have the disadvantage that the user can select only one item from each. This dialog box uses list boxes. These offer a multi-select facility so that the user can make several choices from each if they wish. Designing a list box is similar to designing a combo box in that is needs a Row Source from which to make its list. As before I have used tables for this. You must enable the multi-select feature by choosing a suitable mode for the Multi Select property of the list box. C hoose Simple or Extended depending on how you want the user to make their choices. If you want to know more about list box basics read my tutorial Making Sense of List Boxes. In this example the code has the job of creating a criteria string from the user's choices in each of the list boxes. If, for example, the user chooses London, Brussels and Paris from the Office list box it means that they want to see records for the London OR Brussels OR Paris offices. http://www.fontstuff.com/access/acctut19.htm 6/16
  • 7.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports There are different ways this can be represented in SQL. One way is to use the keyword "OR" like this: [Office] = 'London' OR [Office] = 'Brussels' OR [Office] = 'Paris' but this can result in very long criteria strings, so I prefer to use an SQL "IN" clause like this: [Office] IN('London','Brussels','Paris') To do this I use a code statement that loops through the selected items in the list box and strings them together, separated by commas: Dim varItem As Variant For Each varItem In Me.lstOffice.ItemsSelected strOffice = strOffice & ",'" & Me.lstOffice.ItemData(varItem) _ & "'" Next varItem If the resulting string has no length (i.e. it is empty because the user didn't select anything) the code creates criteria which shows all records. But if the user did make a selection my code creates an IN clause listing the selections, after first removing the leading comma from the front of the string: If Len(strOffice) = 0 Then strOffice = "Like '*'" Else strOffice = Right(strOffice, Len(strOffice) - 1) strOffice = "IN(" & strOffice & ")" End If Each list box requires a similar set of code statements. The code for the Gender option group and the code that creates the filter string are exactly the same as in the previous example. You can review the complete code for the dialog box in Code Listing 3. ^ top Offering the Option to Sort the Records In many cases sorting the records in a report is just as important as filtering them. If you don't want to offer a sorting option to the user, you can predefine the sort order by basing the report on a query or use an SQL statement incorporating an ORDER BY clause as the report's Record Source. For example: SELECT * FROM tblStaff ORDER BY [Office], [Department]; will display the records sorted first by the Office field then by the Department field, both in ascending order. Adding sorting options to the dialog increases its power a great deal but to make it a really useful and user-friendly tool requires a bit of thought and quite a lot of code (although much of it is quite simple and repeated several times). Here's how the dialog box looks: http://www.fontstuff.com/access/acctut19.htm 7/16
  • 8.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports I have put the sorting tools (three combo boxes and three command buttons) inside a frame purely for design effect. It has no other practical implications. The features of the sorting tools are as follows: There are three combo boxes, allowing the user to sort by up to three fields at a time. The combo boxes have a hierarchy (1, 2, 3) by which the sort is prioritised. The records are sorted first by the choice in combo box 1, then by the choice in combo box 2, then by the choice in combo box 3. Each combo box contains a list of fields from which the user can choose to sort by, plus the option "Not Sorted". If the "Not Sorted" option is chosen any combo boxes lower in the hierarchy are automatically disabled and their values set to "Not Sorted" too. If the user chooses a sort field, and the value in the next combo box is disabled, then the next combo box becomes enabled with its value remaining as "Not Sorted". The user can not choose the same field in two different combo boxes. There are three command buttons, corresponding to the three combo boxes, by which the user can specify the sort direction for each field. When a field is chosen the corresponding command button is enabled, but when a combo box has the value "Not Sorted" the corresponding command button is disabled. C licking the command button changes its caption from Ascending to Descending. C licking it again changes it back again. The default caption is Ascending. As you can see, there is quite a lot of functionality here and that requires quite a lot of code, but it is fairly simple and much of it is repeated for each combo box so the task of creating an ORDER BY clause isn't as daunting as it might seem. Filling The Combo Box Lists To create the list, each combo box has its Row Source Type property set to Value http://www.fontstuff.com/access/acctut19.htm 8/16
  • 9.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports List. This allows a list of items to be typed directly into the Row Source property textbox: Separated by semicolons, each item becomes an option on the combo box list: Coding the Combo Boxes Each combo box has three jobs to do when the user makes a choice: 1. It has to check that the choice is not the same as a choice that has already been made. 2. It has to disable the combo boxes below it in the hierarchy if the user chose "Not Sorted" and set their values to "Not Sorted". 3. If the user chose "Not Sorted" it has to disable its own sort direction command button and any below it in the hierarchy and set their captions to "Ascending". The first job is best handled by the combo box's Before Update event. This is because, if the user's choice is not acceptable, the update can be cancelled easily. The After Update event doesn't offer this facility. Here's the code for the first combo box: Private Sub cboSortOrder1_BeforeUpdate(Cancel As Integer) If Me.cboSortOrder1.Value <> "Not Sorted" Then If Me.cboSortOrder1.Value = Me.cboSortOrder2.Value _ Or Me.cboSortOrder1.Value = Me.cboSortOrder3.Value Then MsgBox "You already chose that item." Cancel = True Me.cboSortOrder1.Dropdown End If End If End Sub There are two If Statements, one inside the other (this is referred to as "nested"). The first If Statement checks to see if the user chose "Not Sorted". If they did, nothing happens because it's OK if more than one combo box has this value. But if any other value has been chosen it checks to see if the chosen value matches any of the other combo boxes. If it does a message is shown to the user, their action is undone (by cancelling the event) and the combo box list is dropped to prompt the user to make a different choice. The Before Update event procedure of each combo box has similar code with the numbers changed so that each combo box checks the other two. http://www.fontstuff.com/access/acctut19.htm 9/16
  • 10.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports The remaining jobs are handled by the combo boxes' Change event: Private Sub cboSortOrder1_Change() Dim i As Integer If Me.cboSortOrder1.Value = "Not Sorted" Then For i = 2 To 3 With Me.Controls("cboSortOrder" & i) .Enabled = False .Value = "Not Sorted" End With Next i For i = 1 To 3 With Me.Controls("cmdSortDirection" & i) .Enabled = False .Caption = "Ascending" End With Next i Else Me.cboSortOrder2.Enabled = True Me.cmdSortDirection1.Enabled = True End If End Sub When the user changes the value of the combo box an If Statement checks the new value to see if it is "Not Sorted". If it is, a For...Next loop disables the other combo boxes and sets their values to "Not Sorted". Then another For...Next loop disables the command buttons and sets their captions to "Ascending". If the user made a choice other than "Not Sorted" then its own sort direction command button is enabled and the combo box below is enabled. As before, the code is similar for the remaining combo boxes. Each one referring to the appropriate controls. NOTE: One of the aims of writing good code is to make it as brief as possible, and in the above example I have demonstrated this with the use of loops and With Statements. Using loops avoids having to repeat chunks of code. The variable "i" not only tells Access how many times to run the loop but also serves to identify which control is being manipulated each time. I can do this because I gave the controls names which included numbers. In the first loop, the first time it runs the value of "i" is 2 so Me.Controls("cboSortOrder” & i) means the same as Me.cboSortOrder2 and so on. Although there are only three combo boxes in this example the loop would require no additional code for any number of combo boxes. I would just have to change the upper limit of the value of "i". With Statements also help by removing the need to repeat the first part of a long code line when writing several consecutive statements all referring to the same thing. Coding the Sort Direction Command Buttons I want to make the selection of a sort direction (i.e. ascending or descending) as easy as possible. Since there are only two choices it isn't worth using combo boxes, and an option buttons would take up too much room. So I borrowed Microsoft's idea (take a look at the Query Wizard!) and used command buttons. C licking one of the command buttons doesn't do anything other than change it's caption. The code which later builds the SQL statement for the report filter will read the button's caption to determine which way to sort the records. The code is simple and the same for each button (with the appropriate control names inserted): http://www.fontstuff.com/access/acctut19.htm 10/16
  • 11.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports Private Sub cmdSortDirection1_Click() If Me.cmdSortDirection1.Caption = "Ascending" Then Me.cmdSortDirection1.Caption = "Descending" Else Me.cmdSortDirection1.Caption = "Ascending" End If End Sub Coding the Apply Filter Command Button The code used to create the filter string is exactly the same as in the previous example, but this time there is an additional task: to create an Order By string that will be applied to the OrderBy property of the report. In the same way that the code generates a criteria string, applies it to the Filter property of the report and then activates it by setting the FilterOn property to True, it now has to generate a sort string, apply it to the OrderBy property of the report and activate it by setting the OrderByOn property to True. If the user chose to sort the data by LastName then by FirstName, here's what a completed OrderBy string should look like: [LastName], [FirstName] Fields are simply listed in the desired order, separated by commas. I am in the habit of enclosing field names in square brackets as shown here but this is only absolutely necessary when the names contain spaces. Unless specified otherwise, each field is sorted in ascending order (A-Z, 1-9) but if a field is to be sorted in descending order (Z-A, 9-1) the keyword DESC is used. The sort direction must be specified for each field. if it is omitted it is assumed that the field is to be sorted in ascending order. So, if the user chose to sort the data by LastName then by FirstName both in descending order the string should look like: [LastName] DESC, [FirstName] DESC Here is the code used to build the sort string: If Me.cboSortOrder1.Value <> "Not Sorted" Then strSortOrder = "[" & Me.cboSortOrder1.Value & "]" If Me.cmdSortDirection1.Caption = "Descending" Then strSortOrder = strSortOrder & " DESC" End If If Me.cboSortOrder2.Value <> "Not Sorted" Then strSortOrder = strSortOrder & ",[" & Me.cboSortOrder2.Value & "]" If Me.cmdSortDirection2.Caption = "Descending" Then strSortOrder = strSortOrder & " DESC" End If If Me.cboSortOrder3.Value <> "Not Sorted" Then strSortOrder = strSortOrder & ",[" & Me.cboSortOrder3.Value & "]" If Me.cmdSortDirection3.Caption = "Descending" Then strSortOrder = strSortOrder & " DESC" End If End If End If End If The code consists of a series of nested If Statements, each one depending on the http://www.fontstuff.com/access/acctut19.htm 11/16
  • 12.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports result of the previous one. First of all, an If Statement checks to see if the value of the first combo box is something other than "Not Sorted". If the value is "Not Sorted" the If Statement finishes and the OrderBy string remains empty. But if the first combo box contains a field name then it is placed, surrounded by square brackets, into a variable named strSortOrder. A second If Statement then looks at the caption of the first command button and if it reads "Descending" the keyword DESC is added to the string in the strSortOrder variable. The process is repeated for each combo box and finishes if the value "Not Sorted" is found, but if a field name is found its name and the sort direction is noted and the code moves to the next combo box. The last section of the code applies and activates both the filter string and the sort string: With Reports![rptStaff] .Filter = strFilter .FilterOn = True .OrderBy = strSortOrder .OrderByOn = True End With Coding the Remove Filter Command Button In the earlier examples the this button has simply set the FilterOn property of the report to False. Now it has to do the same for the OrderByOn property: With Reports![rptStaff] .FilterOn = False .OrderByOn = False End With But this time I have added some extra functionality. The following statement removes the selections from the Office list box (there is a similar one for the Department list box): For Each varItem In Me.lstOffice.ItemsSelected Me.lstOffice.Selected(varItem) = False Next varItem The option group is reset to its original value (Gender = Both): Me.fraGender.Value = 3 Finally the following loop sets the value of each combo box to "Not Sorted" and disables it, then sets the caption of each sort direction command button to "Ascending" and disables it. The last line enables the first combo box ready for the user to make their next choice. For i = 1 To 3 Me.Controls("cboSortOrder" & i).Value = "Not Sorted" Me.Controls("cboSortOrder" & i).Enabled = False Me.Controls("cmdSortDirection" & i).Enabled = False Me.Controls("cmdSortDirection" & i).Caption = "Ascending" Next i Me.cboSortOrder1.Enabled = True You can review the complete code for the dialog box in Code Listing 4. ^ top http://www.fontstuff.com/access/acctut19.htm 12/16
  • 13.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports Filtering with Free Text Each example so far has offered the user a fixed range of choices for filtering the report's records. That is fine if the fields that are being filtered contain a known range of items. But you might need to offer the facility to filter using free text, i.e. allowing the user to enter anything they want. This final example shows how you can allow the user to enter any string of text into a text box and choose how that string is used to filter the records... For each available field the dialog box has a text box and a set of option buttons. The user can enter a letter or a string of text in the text box and make a choice from the option group to specify how the string is used in the filter. If the user leaves a text box empty then all records are returned for that field. Supposing the user might enter the string mar into the FirstName textbox. The results they get will depend upon the choices they make from the option buttons: C hoosing Starts with... would return: Martin, Mark, Margaret, Mariana etc. C hoosing Contains... would return: Martin, Anne-Marie, Marlon, Omar etc. C hoosing Ends with... would return: Omar, Dagmar, Valdemar etc. C hoosing Equals... would return only: Mar The method used by the filter is to combine the string with one or more asterisk wildcards. In SQL the asterisk (*) combined with a string and the keyword LIKE represents any string of text: Like "g*" represents the letter g followed by any string of text. Like "*g*" represents any string of text containing the letter g. Like "*g" represents any string of text ending with the letter g. The code used to create the filter string is quite simple: If IsNull(Me.txtFirstName.Value) Then strFirstName = "Like '*'" Else SelectCase Me.fraFirstName.Value Case 1 strFirstName = "Like '" & Me.txtFirstName.Value & "*'" Case 2 strFirstName = "Like '*" & Me.txtFirstName.Value & "*'" Case 3 http://www.fontstuff.com/access/acctut19.htm 13/16
  • 14.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports strFirstName = "Like '*" & Me.txtFirstName.Value & "'" Case 4 strFirstName = "= '" & Me.txtFirstName.Value & "'" End Select End If An If Statement looks for an entry in the text box. If the text box is empty (i.e. its value is Null) a filter expression is created that will return all the records. If there is an entry in the text box a C ase Statement creates the required filter expression depending on the value of the option group. This is repeated for each field and the filter expressions are combined to create a filter string: strFilter = "[FirstName] " & strFirstName & _ " AND [LastName] " & strLastName The filter string is applied to the report and activated in the same way as in the previous examples. You can review the complete code for the dialog box in Code Listing 5. ^ top Ideas for Further Improvements The examples I have shown are each designed to illustrate particular techniques. Remember you can mix and match as many of the different tools as you need but remember to always think about the user. These tools are meant to make their lives easier - so keep it simple! Opening the Report Automatically The above examples include a few lines of code that look for the open report and warn the user if it isn't found: If SysCmd(acSysCmdGetObjectState, acReport, "rptStaff") <> acObjStateOpen Then MsgBox "You must open the report first." Exit Sub End If But you could save them the trouble by opening the report automatically as soon as they press the Apply Filter button: If SysCmd(acSysCmdGetObjectState, acReport, "rptStaff") <> acObjStateOpen Then DoCmd.OpenReport "rptStaff", acViewPreview End If Or perhaps you could ask the user what they want to do: Dim Response As VbMsgBoxResult 'For Access 97 use: As Variant If SysCmd(acSysCmdGetObjectState, acReport, "rptStaff") <> acObjStateOpen Then Response = MsgBox("The report is not open." _ http://www.fontstuff.com/access/acctut19.htm 14/16
  • 15.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports & vbCrLf & "Do you want to open it now?" _ , vbQuestion + vbYesNoCancel) SelectCase Response Case vbYes DoCmd.OpenReport "rptStaff", acViewPreview Case vbNo Exit Sub Case vbCancel DoCmd.Close acForm, Me.Name Exit Sub End Select End If Automate the Report's Title Most reports need some sort of descriptive title and you might like the title to change to reflect the records displayed. You can use the user's choices in the combo boxes, list boxes and other controls to construct a variety of labels to place in the header or footer section of the report. For each title you need to place an empty control in the report's Report Header or Report Footer section. If these sections are not already shown on your report when you view it in Design View, go to View > Report Header/Footer to switch them on. You can either add a Label control (and use the code to specify its Caption property) or an unbound TextBox control (and use the code to specify its Value). My preference is to use a TextBox because it has a Can Grow property which, when set to Yes, allows the control to increase in height to accommodate the amount of text it contains. Also, its value can be set when the report is in Preview view (unlike a label caption which requires the report to be switched into Design view for editing). This is useful if you aren't sure how long the title is going to be. Make sure you draw the control as wide as the report permits because the width doesn't change, only the height: The text of your title can be made up of plain text combined with values extracted from the dialog box controls. It can be applied to the report at the same time as the filter. Here's an example of the sort of code you might use (the textbox control is named txtReportTitle): With Reports![rptStaff] .Filter = strFilter .FilterOn = True .txtReportTitle.Value = _ "Staffing Report - " & Format(Date, "dd mmm yyyy") _ & vbCrLf & "Office: " & Me.cboOffice.Value _ & vbCrLf & "Department: " & Me.cboDepartment.Value http://www.fontstuff.com/access/acctut19.htm 15/16
  • 16.
    9/8/2014 Access Tips:Access and SQL Part 6: Dynamic Reports End With Note the use of vbCrLf in the code to add line breaks to the finished text. Here's the result: Download the File You can download a fully-working database file containing this tutorial's completed project in either Access 97 or Access 2000 format. The database contains all five dialog boxes described in the tutorial and a sample report and tables. The files are provided in Access 97 and Access 2000 format. AccessAndSQL6_97.zip [107KB] Access 97 format. AccessAndSQL6_2000.zip [115KB] Access 2000 format. ©2004 Martin Green. All rights reserved. martin@fontstuff.com ^ top http://www.fontstuff.com/access/acctut19.htm 16/16