Microsoft Access VBA Tutorials for self-paced learning, Class Modules, SQL Techniques, and AI Integration Guides.

Detail and Summary from same Report

Taking Detail and Summary from the same Report.

You don’t need to design two separate reports—one for a detailed listing of records with group-wise totals and another for group-wise totals alone. We can use a simple trick to generate both outputs from the same report, depending on the user’s choice.

Recommended reading before proceeding with this topic:

  1. Hiding Report Lines Conditionally
  2. Hiding Records and Group Footer Calculations
  3. Hiding Group Header/Footer and Detail Sections

The  Report.MoveLayout Property.

There are different methods for hiding Report Lines conditionally. For example, the following VBA Code (instead of the earlier simple method we have tried) can give you the same result for hiding Detail Section Report Lines:

Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
  If [OrderID] = 10280 Or [OrderID] = 10297 Then
      Report.MoveLayout = False
      Report.NextRecord = True
      Report.PrintSection = False
  Else
      Report.MoveLayout = True
     Report.NextRecord = True
      Report.PrintSection = True
  End If

End Sub

We must set all three Report-Property Values shown above in different combinations to get the same result. We have already explored the PrtDevMode and PrtMIP Report Properties, and learned how to change Paper Size and Page Orientation, Margin Settings, and Column Settings through the  Program, while previewing or sending the Report to a Network Printer.

If you would like to know more details about the above Report-Property settings, you may search the VBA Help Documents. You can get the Help Document related to this topic quickly if you open any Code Module and Type 'Report.MoveLayout' and press F1 while the cursor is next to the Text or in the Text.

We will continue with our new trick. For this experiment, we will use a copy of last week’s sample report, Order_Details2. If you already have a report with group-wise sub-totals, you can use that instead. If you are using your own report, make sure that the group item value or description is displayed along with the “Sub-Total” label on the left side of the sub-total value control in the Group Footer.

Sample Report Design View.

We will hide the Group Header and Detail Sections when the User opens the Report for Summary Print Preview or Print. A sample Report Image in Design View is given below.

We will replace the Code written for an earlier example with a new Program, so it is better to make a copy of that Report for our trial run now.

  1. Make a Copy of the Order_Details2 Report and paste it with the name Order_Details3.

  2. Open the Report in Design View.

  3. Write the Expression =Sum([Quantity]) in the empty Text Box (we have removed this for our earlier example) in the CustomerID Group Footer below the Quantity field in the Detail Section.

  4. Write the same expression in the Report Footer empty Text Box to take the Quantity report-level total.

  5. Display the Code Module of the Report (View ->Code).

  6. Delete the existing VBA Code from the Code Module (Class Module is the correct term for Report and Form Modules).

  7. Copy and Paste the following VBA Code into the Module and Save the Report.

The Report Class Module Code.

Option Compare Database
'Global declarations
Dim x_opt As Integer

Private Sub Report_Open(Cancel As Integer)
   If IsLoaded("MainSwitchBoard") Then
       x_opt = Forms![MainSwitchBoard]![Opt]
   Else
       x_opt = 1
   End If
End Sub

Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
    If x_opt = 2 Then
        Cancel = True
    Else
        Cancel = False
    End If
End Sub

Private Sub GroupHeader0_Format(Cancel As Integer, FormatCount As Integer)
    If x_opt = 2 Then
       Cancel = True
    Else
       Cancel = False
    End If
End Sub

As you can see, the code above is not hard to understand. We read the report option settings from the MainSwitchBoard in the Report_Open() event procedure and store the value in the global variable x_opt (defined at the top of the module, below the Global default declaration Option Compare Database). Use the IsLoaded() function to check whether the MainSwitchBoard form is open before attempting to read the value from the option group control on the form. If it isn’t open, the report opens normally for a detailed print preview.

The IsLoaded() Function.

Copy the following Code for the IsLoaded() Function and paste it into a Global Module (Standard Module) of your Project and save it:

Public Function IsLoaded(ByVal strForm As String) As Boolean
'---------------------------------------------------------
'Checks through the Forms Collection and if the Form is
'loaded in Memory then Returns TRUE else FALSE
'---------------------------------------------------------
Dim varFrm As Form

On Error GoTo IsLoaded_Err

IsLoaded = False

For Each varFrm In Forms
  If varFrm.Name = strForm Then
   IsLoaded = True
  End If
Next

IsLoaded_Exit:
Exit Function

IsLoaded_Err:
IsLoaded = False
Resume IsLoaded_Exit
End Function

The IsLoaded() function checks the open forms list for the MainSwitchBoard form. If the form is open, it returns the result: TRUE; otherwise, it returns FALSE.

If the report output option on the MainSwitchBoard is set to 1, the report prints normally, with all sections, the Group Header, and Footer sections.

If the option setting is 2, the Format event of the CustomerID Group Header and the Detail section is canceled (these sections are hidden), so only the Page Header/Footer, CustomerID Group Sub-Totals, and Report Total are shown in Print Preview or when printing.

The Report Option Group.

We will create a Report Option Group on the MainSwitchBoard form (or you design a new sample form as shown below) to set and launch our report, using one of the options provided for Detail and Summary views.

  1. Open a new Form or your Main Switchboard (Control Screen) Form in Design View.

  2. Check whether the Control Wizard (with the magic wand icon) in the ToolBox is in the selected state; if not, select it.

  3. Select the Option Group Tool from the Toolbox.

  4. Draw a rectangle on the Form as shown above. The Options Group Wizard will open up.

  5. Type Detail Report, press the Tab key, type Summary Report for two options, and click Next.

  6. Accept the Detail Report as the default choice, and click Finish.

  7. Drag and position the Child Label attached to the Options Group as shown in the design view above, and modify the Label Caption to Report Options.
  8. Click on the Option Group outer frame to select it, and display the Property Sheet (View -> Properties).

  9. Change the Name Property Value to Opt. (no dot at the end)

  10. Select the Command Button Tool from the Toolbox and draw a Command Button below the Option Group Control.

  11. Display the Property Sheet of the Command Button.

  12. Change the Caption Value to Report Preview.

  13. Set the Hyperlink SubAddress Property value to Report Order_Details3. Don't forget to leave a space between the word Report and your Report Name.

  14. Save the MainSwitchBoard Form.

  15. The Demo Run.

  16. Open it in a normal view. Click on the Report Preview Command Button to open the Report after setting the Detail Report or Summary Report Option in the Option Group Control.

NB: Don't forget to close the earlier Report Preview before attempting to open it for different Options.

Next, we will explore how to prepare and display Page Totals on each page of the Report.

Share:

Hiding Report Lines Conditionally-3

Continued from the last two weeks' topics.

    This article is a continuation of two earlier posts on the same subject. You may visit them by following the links below before proceeding further.

  1. Hiding Report Lines Conditionally
  2. Hiding Report Lines Conditionally-2.

We have learned how to hide report items conditionally and calculate sub-totals and report footer totals in code by excluding the values of the records removed from the report’s Detail section. We then updated the derived values in the Group Footer and Report Footer section controls.

We accomplished this by checking for specific values in the report’s source data and by canceling the Format event of the report.

Previously, we applied these techniques only to the Detail section by hiding specific records. However, our current goal is to hide all data for a particular customer group—including the Customer Group Header, Group Footer, and Detail sections.

Hiding Report Group Entries.

If you understood the methods used in the last two examples, you should have no difficulty following this one. The only difference here is that we will compare the group value (CustomerID) across all three sections—the Group Header, Detail, and Group Footer—within their Format event procedures, to hide or show them as needed, rather than applying the logic only to the Detail section as we did earlier.

Make a copy of the previous report (Order_Details) and save it as Order_Details2.
Open the report in Design View, and we’ll make a small change in the Group Footer section:

  1. Click the label with the caption Sub-Total to select it.

  2. From the Format menu, choose Change To → Text Box.
    This converts the label control into a text box, allowing us to display an expression displaying the CustomerID value of each customer group, along with the previous “Sub-Total” caption.

  3. Display the Property Sheet (View → Properties) for the new text box.

  4. In the Control Source property, enter the following expression:

    =[CustomerID] & " Sub-Total: "

After this change, the report output will display something like:
BSBEV Sub-Total: 123

For our example, the CustomerID code will be sufficient. However, when designing reports for real projects, you should use something more descriptive, such as the Customer Name, instead of the code.

We’ll be using this report for another trick later on, in addition to what we’re doing here. So take this exercise seriously—you’ll soon see why it’s important.

Implement Code Changes.

  1. Display the Code Module of the Report (View ->  Code).

  2. Delete the existing Code from the Module.

  3. Copy and Paste the following Code into the Report Code Module and Save the Report.

    Option Compare Database
    Dim x_subtot As Long, x_gtot As Long
    
    Private Sub GroupHeader0_Format(Cancel As Integer, FormatCount As Integer)
    If [CustomerID] = "BSBEV" Or [CustomerID] = "CENTC" Then
      Cancel = True
    Else
       Cancel = False
    End If
    
    End Sub
    
    Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
    If [CustomerID] = "BSBEV" Or [CustomerID] = "CENTC" Then
        Cancel = True
    Else
        Cancel = False
    End If
    
    End Sub
    
    Private Sub Detail_Print(Cancel As Integer, PrintCount As Integer)
        If PrintCount = 1 Then
            x_subtot = x_subtot + [Quantity]
            x_gtot = x_gtot + [Quantity]
        End If
    End Sub
    
    Private Sub GroupFooter0_print(Cancel As Integer, PrintCount As Integer)
        If PrintCount = 1 Then
            [subtot] = x_subtot
            x_subtot = 0
        End If
    End Sub
    
    Private Sub ReportFooter_print(Cancel As Integer, PrintCount As Integer)
    If PrintCount = 1 Then
        [gtot] = x_gtot
    End If
    End Sub
    
  4. Open the Report in Print Preview and check whether these Customer Group Header, Detail, and Group Footer Sections are hidden from the Report or not. Check the sample image given below.

In the VBA code above, we have intentionally removed the Group Footer Format event procedure so that only the Group Footer for the CustomerID values BSBEV and CENTC will appear. This allows us to observe the position of these group items on the report and see where the Group Header and Detail sections are hidden from view.

Another important point to note is that, since we canceled the Format event for the Detail section of these customer groups, the Print event did not occur in the Detail section. As a result, the group summary totals for these two customer codes are displayed as zeros.

Copy the following code and paste it at the bottom of the Code Module and save the Report.

Private Sub GroupFooter0_Format(Cancel As Integer, FormatCount As Integer)
     If [CustomerID] = "BSBEV" Or [CustomerID] = "CENTC" Then
           Cancel = True
     Else
           Cancel = False
     End If
End Sub

Note: If you’re unsure how to correctly name the subroutine and parameters for each section of the report, don’t worry—MS Access will handle that for you. In Design View, click on the section header or footer of the report, open the Property Sheet (View → Properties), set the Event Procedure for either the Format or Print event, and then click the Build ( … ) button on the right edge of the property sheet.

Access will automatically create the subroutine header (with the correct section reference and parameter list) and the corresponding End Sub line in the report’s code module. You only need to manually write the lines of code between them.

Preview Report after Code Changes.

Open the report again in Print Preview, and this time you’ll notice that the Group Footer for these customers is also hidden.

In this example, we’re checking for the customer codes BSBEV and CENTC in all three sections of the report within the Format event procedure to hide those sections. Once the Format action is prevented from running, the Print event does not occur, and therefore, the quantity values for those items are not added to the totals.

If you examine the code structure closely, you’ll see that we’re using both the Format and Print event procedures for the Detail and Group Footer sections. The Format event is used to hide or display report content based on specific criteria, while the Print event is used for calculations or updating the group total controls. For the Report Footer section, we’ve used only the Print event procedure.

Next, we will learn how to open the same Report in two different Modes, i.e., as a Detail Report and as a Summary Report.

Share:

Hiding Report Lines Conditionally-2

Continued from Last Week's Topic.

Last week, we began exploring how to hide data lines in the Report Detail section and worked through a simple example. However, we haven’t yet examined how this action affects the normal calculations in a report, such as computing group-wise sub-totals or report footer totals.

We’ve learned that this can be handled with a simple VBA code solution—provided we understand how MS Access processes data during the Format and Print actions before displaying the final results.

Handling Summary Information.

  1. This week, we’ll work with a sample report to examine how MS Access calculates group-level sub-totals when certain group records or data lines are conditionally hidden from the report using VBA code.

    For this example, we’ll use three data tables from the C:\Program Files\Microsoft Office\Office11\Samples\NorthWind.mdb sample database. Import the following Tables into your database:

    • Orders
    • Order Details
    • Products
  2. Open a new Query and display its SQL Window.

  3. Copy and Paste the following SQL String into the SQL Editing Window and Save the Query with the Name Order_DetailsQ:

    SELECT Orders.CustomerID,
       [Order Details].OrderID,
       Products.ProductName,
     [Order Details].Quantity
    FROM Orders INNER JOIN ([Order Details] INNER JOIN Products ON [Order Details].ProductID = Products.ProductID) ON Orders.OrderID = [Order Details].OrderID
    WHERE ((([Order Details].OrderID) Between 10248 And 10300) AND ((Left([CustomerID],1)) Between "A" And "L"));
    

    We have used conditions in the Query to select only a few records, enough to limit the number of Pages on the Report to three, so that we can check the accuracy of values appearing in Group-wise Sub-Totals and Report-Total Values.

  4. Click on the Order_DetailsQ query to select it and select Report from the Insert Menu.

  5. Select Report Wizard from the displayed list of options and click OK.

  6. Select all fields from the Available Fields list and move them to the Selected Fields list, and click Next Command Button.

  7. The CustomerID field is already selected for Grouping Levels. If any other Field is appearing on the Report sample View, then click the < Button to remove it, select the CustomerID Field from the Field List and click > Button to use it as Group Level and click Next.

  8. Click on the Summary Options Command Button and insert a check mark under the Sum option in the Quantity Field, click OK, and then click Finish to create the Report.

  9. Save the Report with the name Order_Details.

    Open the report in Print Preview and check the sub-totals and report footer total. The report appears similar to the sample image shown below. If you have access to a printer, go ahead and print it—it’s only three pages long. This will allow us to compare the results later when we hide the report lines using the program.

    Sample Report Image.

    We will hide the data lines for OrderID numbers 10280 (three records) and 10297 (two records) from the customer groups BERGS and BLONP, respectively. Let’s see how this affects the sub-totals and report totals when these five lines are excluded from the report display.

  10. Open the Report in Design View.

  11. Display the Code Module (View --> Code) of the Report.

  12. Copy and paste the following Code into the Module, save and close the Report.

    Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
    If [OrderID] = 10280 Or [OrderID] = 10297 Then
          Cancel = True
    Else
          Cancel = False
    End If
    End Sub

    Read the earlier Article with the Title: hiding-report-lines-Conditionally to learn how to use a parameter table to add several OrderIDs to filter, instead of Constant values in the If...Then statement.

    Print Preview of Report.

  13. Open the Report in Print Preview.

  14. Cross-check the sub-totals for CustomerIDs BERGS and BLONP with the printout you took earlier.

    Even though we have removed three entries from the first customer and two from the second, there is no change in the group sub-totals or report footer total. The code only prevents these data lines from appearing on the report—MS Access still includes them in the summary totals.

    The label above the summary totals, reading “Summary for CustomerID = BERGS (7 records)”, is also misleading. Before addressing the summary totals, we’ll remove this label, as we don’t want to add extra code to update its value.

  15. Open the Report in Design View and delete the Text Box with the expression that shows the above Summary message.

Hiding Report Detail Section or Cancel Report Line Formatting.

Before we proceed with corrective actions to display subtotals and report totals correctly, I want to draw your attention to the code used in this example.

If you tried last week’s example, you’ll notice some differences between the two VBA codes. In last week’s method, based on the selected OrderID criteria, we hid the Detail section using the statement:

Report.Section(acDetail).Visible = False

This was done during the execution of the report line Format event.

In this example, instead of hiding the Detail section, we instruct MS Access to cancel the report line Format action. As far as the report output is concerned, both approaches produce the same result.

You can run another experiment by executing the same code under the Print event procedure to observe the difference. I’ll provide the code below, which you can copy and paste into the report module, overwriting the earlier code.

Private Sub Detail_Print(Cancel As Integer, PrintCount As Integer)
If [OrderID] = 10280 Or [OrderID] = 10297 Then
   Cancel = True
Else
      Cancel = False
End If
End Sub

Open the report in Print Preview and examine the area for the records with the OrderIDs used in our criteria. Even though the data lines are suppressed from appearing on the report, the empty space for those lines remains visible.

We will now move on to calculate the sub-totals and the report footer total. Specifically, we need to calculate the quantity totals while excluding the values for orders 10280 and 10297, and write these corrected totals directly into the sub-total and report footer text boxes. Let’s see how this is done.

Performing Summary Calculations.

  1. Open the Report in Design View.

  2. Remove the expression =Sum([Quantity]) from the Text Box Control Source Property in the CustomerID Group Footer and in the Report Footer Sections.

  3. Click on the Group Footer Sub-Total Text Box and display its Property Sheet (View --> Properties).

  4. Change the Name Property Value to SubTotal.

  5. Similarly, change the Name Property Value of the Page Footer Text Box to GTotal.

  6. Copy and paste the following Code into the Report Module, replacing the earlier Code.

    'global declarations
    Dim x_SubTotal As Long, x_GTotal As Long
    
    Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
    If [OrderID] = 10280 Or [OrderID] = 10297 Then
        Cancel = True
    Else
        Cancel = False
    End If
    End Sub
    
    Private Sub Detail_Print(Cancel As Integer, PrintCount As Integer)
    If PrintCount = 1 Then
        x_SubTotal = x_SubTotal + [Quantity]
        x_GTotal = x_GTotal + [Quantity]
    End If
    End Sub
    
    Private Sub GroupFooter0_print(Cancel As Integer, PrintCount As Integer)
    If PrintCount = 1 Then
        [SubTotal] = x_SubTotal
        x_SubTotal = 0
    End If
    End Sub
    
    Private Sub ReportFooter_print(Cancel As Integer, PrintCount As Integer)
    If PrintCount = 1 Then
        [GTotal] = x_GTotal
    End If
    End Sub
  7. Save the Report and open it in Print Preview, and check whether the Sub-Totals and Report Footer Totals are appearing correctly now or not.

In the code above, we prevent the report from displaying the record lines in the Format event, while performing the calculations in the report’s Print event. We check whether the PrintCount parameter is set to 1 to ensure the totals are calculated correctly. Note that the Print event can occur multiple times if you navigate from one page to an earlier page using the Retreat event.

The Report Summary Calculation Issues.

So far, everything has gone well, but there is a major problem with this method, and the User must be aware of this to avoid undesirable results.

If you jump directly to a page—for example, from page 2 to the last page by typing the page number in the report’s page control in Print Preview—the line-by-line calculation on each page will fail, and the report footer totals will be incorrect. Therefore, if the user is unaware of this issue and navigates through the report pages in preview before printing, it can lead to incorrect totals.

To work around this problem, we can use any of the following methods:

  1. Use Custom Menus and Toolbars in your application and use separate options for Printing and Previewing the Report.

    Refer to the following Articles to learn more about Custom Menus and Toolbars.


    A Different Approach.

    I recommend the above method rather than the second option given below.

  2. Disable Print Command Buttons from the File Menu and from the Report Preview Menu while previewing this particular Report, and enable them again when the Preview is closed. To do this, add the following Code to the VBA Module of the Report:
    Private Sub Report_Close()
       CommandBars("File").Controls("Print...").Enabled = True
       CommandBars("Print Preview").Controls("Print").Enabled = True
    End Sub
    
    Private Sub Report_Open(Cancel As Integer)
       CommandBars("File").Controls("Print...").Enabled = False
       CommandBars("Print Preview").Controls("Print").Enabled = False
    End Sub

    If you are using MS Access 2000, use the same Control name, Print in both lines of Code. The above code is written for MS Access 2003.

    To send the Report directly to the Printer, you may use either a Print Macro or the following line of code to run from a Command Button Click Event or from a Listbox-based Menu Option.

    DoCmd.OpenReport "myReport", acViewNormal

  3. To be on the safe side, use this method only on two-page reports.

Next, we’ll explore how to hide group-level information—such as the Group Header, Detail, and Group Footer sections—using VBA code.

Share:

Hiding Report Lines Conditionally

Hiding Report Lines Conditionally.

Hiding report lines or report sections conditionally at runtime may not be an everyday requirement. However, when it becomes necessary, it’s interesting to know how to do it with the help of VBA programs. One feature I particularly like about MS Access reports is the ability to write programs directly in the Report Module—especially for highlighting critical information so that users’ attention is drawn to specific details. You can see an interesting example in an earlier post titled Highlighting Reports.

Normally, to suppress something from printing on a report, we would use a query to filter out unwanted items from the source data before previewing or printing. Frankly, using a query is the best method compared to what I’m showing here. To achieve the same result on the report without filtering the source data, we need to use a few tricks—after all, this is all about Tips & Tricks, right?

If you enjoy working with MS Access programs, read on. The code itself is not complicated; a few simple lines are enough, and even a VBA beginner can understand them easily.

It’s enjoyable to do something different, not only to break the monotony of repeating the same tasks but also to gain better insight into doing things more efficiently next time.

Design a Sample Report.

We will create a sample Report quickly to try out this Trick.

  1. Import the Orders and Employees tables from C:\Program Files\Microsoft Office\Office11\Samples\Northwind.mdb.

    We won’t be using the Employees table directly in the report. Without the Employees table in the database, only the Employee IDs will be displayed when designing the report using the Orders table. There is a reference to the Employees table in the EmployeeID field of the Orders table (for example, the combo box uses the Employees table).

    To create the report:

    1. Select the Orders table and select Report from the Insert menu.

    2. Choose Report Wizard from the displayed list and click OK.

    3. From the Available Fields list, select the following data fields and move them one by one to the report (fields list continues…).

      Selected Fields List:

      • OrderID
      • CustomerID
      • EmployeeID
      • OrderDate
      • RequiredDate
    4. Click Next, select CustomerID as Grouping Level, and move it to the right. Click Next Command Button.

    5. Select OrderID for Sorting in the first Sort Control and click Finish to create the sample Report.

      An image of the sample Report is given below:

    6. Open the Report in Print Preview and check the Order Numbers appearing under the first three Customer Groups on the Report.

      We will attempt to hide three different Orders (Order IDs 10702, 10625 & 10573 from ALFKI, ANATR & ANTON Customer#39 List, respectively) from appearing on the Report with the following simple lines of Code:

      Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
      If [OrderID] = 10702 Or [OrderID] = 10625 Or [OrderID] = 10573 Then
            Report.Section(acDetail).Visible = False
      Else
         Report.Section(acDetail).Visible = True
      End If
      End Sub

      The OnFormat() Event Procedure.

      If you look at the above code, you can see that the code runs under the On Format Event Procedure in the Detail Section of the Report.

    7. Open your Report in Design View and display the Code Module (View -> Code).

    8. Copy and Paste the above Code into the Report Module.

    9. Select Close and Return to Microsoft Office Access from the File Menu to close the VBA Window and return to the Design View of the Report.

      Or you can press Alt+F11 to Toggle between the Database Window and the VBA Window. Visit the Page Keyboard Shortcuts to learn more about Keyboard Shortcuts.

    10. Click on the Detail Section of the Report and display the Property Sheet (View -> Properties).

    11. Check the On Format Event Property, and you can see that the entry [Event Procedure] is appearing there, indicating that the Code that we have pasted Runs on the Format Event of the Report.

      There are two more Events associated with the Report's Printing or Previewing action: Print Event and Retreat Event.

      The Report Formatting Passes

      MS Access makes two passes over a report before it is actually displayed or printed. The first, the Format pass, lays out the contents of each report section and performs any necessary calculations for summary information (we’ll cover this in more detail in forthcoming articles). The second pass, the Print pass, prints the report’s contents before previewing or sending it to the printer.

      The Retreat event occurs when you move the page preview to an earlier page. During this event, the Format action runs a second time for the retreated page, and the FormatCount parameter is set. 

      Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)

      is incremented by one. This can have unintended effects if calculations are being performed in this procedure. We will examine this aspect further in upcoming articles.

    12. Save the Report and open it in Print Preview.
    13. Check whether the Report Lines associated with OrderIDs 10702, 10625 & 10573 are still appearing on the Report or not.

    The Parameter Table.

    We were using Constant values for OrderIDs in the Program to check and suppress the Report lines from appearing on the Report. We cannot expect the User to open the Report in Design View and change the Program every time to change the OrderID values, if it becomes necessary, before printing the Report. Besides, there may be more than two or three Orders to hide this way. We must provide a much simpler method for the User to input the OrderIDs as parameters to the Program and run the Report based on that.

    We will create a parameter table with the key in OrderIDs, read it in the program, compare it to the Report Order IDs, and skip those lines on the Report for matching cases.

    1. Create a Table named OrderParam with a single Field named OID with Data Type Number and Field Size Long Integer.
    2. Select Primary Key from the Edit Menu to define this field as a Primary Key Field. This will prevent duplicate values from going into the Parameter Table and make it easier to cross-check OrderIDs from the Report.

      Save the Table and open it in Data Sheet View.

    3. Key in the OrderIDs 10702, 10625, and 10573 (or any other OrderIDs you would like to hide) in the OrderParam Table.

      Design a Continuous Form for the Table and create a CommandButton at the Footer Section of the Form with HyperLinks to the Orders Report so that the User can launch the Report from the Parameter Screen itself.

      The VBA Code

    4. Copy and paste the following Code in Report#39's Code Module, replacing the earlier Code:
      'Global declarations
      Dim cdb As Database, rst As Recordset
      
      Private Sub Report_Open(Cancel As Integer)
           Set cdb = CurrentDb
          Set rst = cdb.OpenRecordset("OrderParam")
          rst.Index = "PrimaryKey"
      End Sub
      
      Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
      rst.Seek "=", [OrderID]
      If Not rst.NoMatch Then
         Report.Section(acDetail).Visible = False
      Else
          Report.Section(acDetail).Visible = True
      End If
      End Sub
      
      Private Sub Report_Close()
         rst.Close
         Set rst = Nothing
         Set cdb = Nothing
      End Sub
    5. Save the Report with the new Code and open it in Print Preview.
    6. Look for the OrderIDs in the OrderParam Table to check whether they are really suppressed from the Report or not.

    Let us examine what we did in the above Code.

    The VBA Code Review.

    • We have declared the Database and Recordset objects at the global level of the report module so they can be referenced in all other subroutines.

    • In the report’s Open event procedure, we open the OrderParam table, activate its primary key index, and keep it in memory.

    • During the Detail Section Format event procedure, we cross-check each OrderID against the contents of the OrderParam table. If an OrderID matches a value in the table, the corresponding report line is hidden. This process continues for all records in the report.

    • The OrderParam recordset is closed in the report’s Close event procedure.

    Because this checking process references an external table for each record in the report’s source data, there may be a slight delay before the preview or print action completes.

    Next, we will explore how this method works with Customer Level Summary Totals in the Customer Footer Section of the Report.

Share:

Network And Print Page Setup-3

Network And Print Page Setup-3.

Shared MS Access Reports are typically designed for a specific printer on the network. When users attempt to print the report to a different printer, the report automatically adopts that printer’s default page settings—such as paper size, page orientation, and margins—which may cause the report to print incorrectly.

In earlier posts, we learned how to modify page setup values (paper size, page orientation, and margin settings) of reports through VBA code before printing them on network-based printers.

However, the topic remains incomplete without discussing how to change column settings (accessible via File → Page Setup → Columns) programmatically for multi-column reports.

The column settings defined in the Columns tab in the Page Setup dialog are controlled by the PrtMip property of the report. If you followed the previous tutorial on margin settings, you have already created the user-defined data types needed to copy and modify the PrtMip property values in memory. In that case, all you need now is a new Subroutine that adjusts the report’s column settings for a multi-column layout and opens it in Print Preview.

Readers have not visited the earlier Pages.

For the benefit of readers who have arrived directly on this page, I will reproduce the first part of the code below—the segment that declares the two user-defined data types.

Open the VBA Editor (press Alt + F11), insert a new Standard Module (Global Module) by selecting Insert → Module, and then copy and paste the following code (from both code windows) into the module, immediately below the global declaration line:

Option Compare Database

If you have already copied this first part while working on the Margin Settings example, then simply take the code from the second code window below and paste it into the same module where you previously placed the PrtMip property data type declarations.

Alternatively, you may paste it into a different Global Module if you prefer to keep the code segments separate.

Private Type str_PRTMIP
    strRGB As String * 28
End Type

Private Type type_PRTMIP
    xLeftMargin As Long
    yTopMargin As Long
    xRightMargin As Long
    yBotMargin As Long
    fDataOnly As Long
    xWidth As Long
    yHeight As Long
    fDefaultSize As Long
    cxColumns As Long
    yColumnSpacing As Long
    xRowSpacing As Long
    rItemLayout As Long
    fFastPrint As Long
    fDatasheet As Long
End Type

Public Sub PageColumns(ByVal strName As String)

    Dim PrtMipString As str_PRTMIP
    Dim PM As type_PRTMIP
    Dim rpt As Report
    Const PM_HORIZONTALCOLS = 1953
    Const PM_VERTICALCOLS = 1954
    Const TWIPS = 1440

   ' Open the report.
    DoCmd.OpenReport strName, acDesign
    Set rpt = Reports(strName)
    PrtMipString.strRGB = rpt.PrtMip
    LSet PM = PrtMipString

    ' Create two columns.
    PM.cxColumns = 2

    ' Set 0.25 inch between rows.
    PM.xRowSpacing = 0.25 * TWIPS

   ' Set 0.5 inch between columns.
    PM.yColumnSpacing = 0.5 * TWIPS
    PM.rItemLayout = PM_VERTICALCOLS

    ' Update property.
    LSet PrtMipString = PM
    rpt.PrtMip = PrtMipString.strRGB

    DoCmd.Close acReport, strName, acSaveYes
    DoCmd.OpenReport strName, acViewPreview

    Set rpt = Nothing

End Sub

Create a Sample Two-Column Labels Report.

The next step is to create a multi-column sample report to test our program. We’ll design an Address Labels report using the Employees table imported from the MS Access sample database Northwind.mdb.

  1. Import the Employees Table:
    Go to File → Get External Data → Import and import the Employees table from:

    C:\Program Files\Microsoft Office\Office11\Samples\Northwind.mdb
  2. Create the Report:
    Click on the Employees table, then select Report from the Insert menu.

  3. Choose the Design View:
    From the list of displayed options, select Design View.

  4. Set Up the Report Layout:
    We will design the address labels manually. However, you may use the Label Wizard instead.

  5. Remove Page Header/Footer:
    From the View menu, uncheck the Page Header/Footer option to remove those sections from the report design.

  6. Design the Label:
    Create a sample label layout in the Detail section of the report, as shown in the image below.

  1. Sample Label and Page Setup in one Image.


    The Design Task.

  2. Create four text boxes, each approximately 2.75 inches wide and 0.25 inches high, and arrange them close together as shown in the design layout.

    Open the Property Sheet for each text box (View → Properties) and do the following:

    • In the first and third text boxes, enter the appropriate expressions.

    • In the second and fourth text boxes, set the Control Source to the Address and Country fields, respectively.

  3. Click outside the Text boxes and drag the mouse over them to select all four Text Boxes together.

  4. Display the TextBox Property Sheet (View -> Properties).

  5. Change the Border Color Property value to 0.

  6. Open the Page Setup Dialog Box from the File Menu and select the Columns Tab.

  7. Change the values on the controls as shown in the image above.

    Look closely at the Columns Tab settings to understand how these values affect the printed layout of the labels.

    The labels will be printed in two columns, with half an inch of horizontal spacing between them. Set the vertical spacing between labels to a quarter of an inch. Each label is 3.25 inches wide and 1.4 inches high, including the blank space surrounding the text boxes.

    The Column Layout setting—Across, then Down-determines how the labels are arranged on the report. With the current setting, labels are placed across the page first, and then continue down to the next row. However, we prefer the opposite layout—Down, then Across—so that the labels fill the left column first before continuing into the second column. We’ll modify this behavior through our program.

  8. You may set the Margin Values to 0.5 Inches on all four sides of the Margins Tab.

  9. Select A4 Paper Size and Orientation to Portrait on the Page Tab and close the Page Setup Dialog Box.

  10. Click on the Sorting and Grouping Toolbar Button above. You can find this icon at the top right (next to the Toolbox Button icon) in the above image.

  11. Select FirstName in the Field/Expressions Column and Ascending in the Sort Order Column.

  12. Save the Report with the name MyLabels or any other name you prefer.

  13. Open the Report in Print Preview and check the arrangement of Labels and values in them. The Employee Names were sorted in the First Name order on the Report. The Label arrangement is now Across, Down order.

    Preparing for Test Run of Program

  14. Close the Report Preview and open it in Design View.

  15. Open the Page Setup Dialog Box and select the Columns Tab.

  16. Change the Row Spacing Value to 0 Inches and the Column Spacing Value to 0.1 Inches.

  17. Leave the Column Layout Value (Across, then Down) setting.

    The Column Layout Value and the Column & Row Spacing Values will change through the Program.

  18. You can run the Program PageColumns() directly from the Debug Window (Immediate Window) for testing. Press Alt+F11 Keyboard Shortcut to display the VBA Editing Window, and press Ctrl+G to bring up the Debug window.

  19. Type PageColumns "MyLabels" in the Debug Window and press the Enter Key.

Using the PageColumns() program, we have now changed the report’s column layout to Down, Across, and restored the column and row spacing of the labels to the original values specified earlier in the Page Setup dialog box.

You can run the Program through a Button Click Event Procedure from your Main Switchboard by adding the following lines of sample code:

Private Sub cmdPreview_Click()
 PageColumns "YourReportName"
End Sub

You can call the PageColumns() Program and pass the Report Name as a parameter when the user attempts to Print Address Labels. This will ensure that the Address Labels are printed with the correct settings on any Printer on the Network.

NB: The User must select the printer (if more than one Printer is installed on the user's machine) and set it up as the Default Printer before attempting to print the Report.

Share:

Network And Print Page Setup-2

Continued from Last Week's Page.

In an earlier article, we discussed how to automatically change the paper size and page orientation of MS Access reports through VBA code for any printer on the network. We achieved this by copying the PrtDevMode property values in memory, modifying them to match the desired paper size and page orientation, and then updating them back into the report’s page settings before printing it on the user’s default printer.

Working with the PrtMip Property.

We will have a similar exercise to change the Margin Settings of the MS-Access Report through the program. This time, we need to work with the PrtMip Property of the Report to change the Margin Values.

The procedure is similar to the previous example. The steps taken in the Program are as follows:

  • Open the Report in Design View.

  • Copy the PrtMip Property Values of the Report into a 28-byte-long String Variable and move it into a redefined structured data area for modification.

  • Change the required Margin Values in Memory.

  • Update them back into the Report's PrtMip Property.

  • Save the Report with the changes and open it in Print Preview.

Prepare for a Demo Run.

So let us start.

  1. Open one of your Databases with Reports in it.

  2. Display the Visual Basic Editing Window (Alt+F11).

  3. Insert (Insert -> Module) a new Standard Module (Global Module).

  4. Copy and paste the following code into the new Module and save it. 

    Private Type str_PRTMIP
        strRGB As String * 28
    End Type
    
    Private Type type_PRTMIP
        xLeftMargin As Long
        yTopMargin As Long
        xRightMargin As Long
        yBotMargin As Long
        fDataOnly As Long
        xWidth As Long
        yHeight As Long
        fDefaultSize As Long
        cxColumns As Long
        yColumnSpacing As Long
        xRowSpacing As Long
        rItemLayout As Long
        fFastPrint As Long
        fDatasheet As Long
    End Type
    
    Public Sub SetMargins(ByVal strName As String)
    
    Dim PrtMipString As str_PRTMIP
    Dim PM As type_PRTMIP
    Dim rpt As Report
    Const TWIPS As Long = 1440
        ' Open the report.
        DoCmd.OpenReport strName, acDesign
        Set rpt = Reports(strName)
        PrtMipString.strRGB = rpt.PrtMip
        LSet PM = PrtMipString
    
        ' Set margins.
        PM.xLeftMargin = 0.75 * TWIPS
        PM.yTopMargin = 0.5 * TWIPS
        PM.xRightMargin = 0.5 * TWIPS
        PM.yBotMargin = 0.5 * TWIPS
    
       ' Update property.
        LSet PrtMipString = PM
        rpt.PrtMip = PrtMipString.strRGB
    
        DoCmd.Close acReport, strName, acSaveYes
        DoCmd.OpenReport strName, acViewPreview
    
        Set rpt = Nothing
    
    End Sub
  5. Open one of your existing Reports in Design View.

  6. Select File -> Page Setup -> Margins.

  7. Change all four sides (Left, Right, Top, and Bottom) of the Margin settings to 1 Inch.

  8. Save and Close the Report.

  9. Open the Main Switchboard Form of your Application or create a new Form.

  10. Create a new Command Button on the Form.

  11. While the Command Button is in the selected state, display the Property Sheet (View ->Properties).

  12. Change the Name Property Value to cmdPreview and change the Caption Property Value to Print Preview.

  13. Display the Code Module of the Form (View -> Code).

  14. Copy and paste the following lines into the Code Module of the Form. 

    Private Sub cmdPreview_Click()
      SetMargins "MyReport"
     End Sub
  15. Replace the name MyReport with your own Report Name.

  16. Save and close the Form.

  17. Open the Form in normal view and click on the Command Button to run the Program and change all four margins of the Report to new values, and open the Report in Print Preview.

Close the Report and open it again in Design View and check whether the margin settings have really changed through the program or not.

Note: If any value falls below the allowable range defined by the printer driver settings, the printer may automatically adjust it to the minimum acceptable value. As a result, you might notice that some settings appear higher than the values you originally specified.

Running SetMargins() Program from PaperandOrient() Sub-Routine.

You can do a sample run of the Program by typing SetMargins "YourReportName" in the Debug Window directly, without the use of a Form or a Command Button.

You can run this program from within the earlier PaperAndOrient() Program to change the Margins also along with the Paper Size and Page Orientation. All three sets of values can be changed by calling the PaperAndOrient() Program alone.

The modified PaperAndOrient() Program is given below:

Public Sub PaperAndOrient(ByVal strName As String)
    Const DM_PORTRAIT = 1
    Const DM_LANDSCAPE = 2
    Const DM_PAPERSIZE = 9
    Dim DevString As str_DEVMODE
    Dim DM As type_DEVMODE
    Dim strDevModeExtra As String
    Dim rpt As Report

   ' Opens report in Design view.
    DoCmd.OpenReport strName, acDesign
    Set rpt = Reports(strName)

    If Not IsNull(rpt.PrtDevMode) Then
        strDevModeExtra = rpt.PrtDevMode
        DevString.RGB = strDevModeExtra
        LSet DM = DevString
        DM.lngFields = DM.lngFields Or DM.intOrientation

        ' Initialize fields.
        DM.intPaperSize = DM_PAPERSIZE
        If DM.intOrientation = DM_PORTRAIT Then
            DM.intOrientation = DM_LANDSCAPE
        End If

       ' Update property.
        LSet DevString = DM
        Mid(strDevModeExtra, 1, 94) = DevString.RGB
        rpt.PrtDevMode = strDevModeExtra
    End If
    SetMargins strName
    DoCmd.Close acReport, strName, acSaveYes
    DoCmd.OpenReport strName, acViewPreview
    Set rpt = Nothing

End Sub
 
Public Sub SetMargins(ByVal strName As String)
    Dim PrtMipString As str_PRTMIP
    Dim PM As type_PRTMIP
    Dim rprt As Report
    Const TWIPS As Long = 1440

    Set rprt = Reports(strName)
    PrtMipString.strRGB = rprt.PrtMip
    LSet PM = PrtMipString

   ' Set margins.
    PM.xLeftMargin = 0.75 * TWIPS
    PM.yTopMargin = 0.5 * TWIPS
    PM.xRightMargin = 0.5 * TWIPS
    PM.yBotMargin = 0.5 * TWIPS

   ' Update property.
    LSet PrtMipString = PM
    rprt.PrtMip = PrtMipString.strRGB

    Set rprt = Nothing

End Sub

The Measurement Unit is Twips.

    The measurements of reports and their objects are handled internally in twips, rather than in inches or millimeters. Although you can specify measurements in standard units such as inches, centimeters, or other regional formats on the property sheets of reports, forms, or controls, MS Access automatically converts these values into twips internally. However, when working in VBA, this conversion must be done manually before changing the property values of objects.

  • 1 Inch = 1440 Twips
  • 1 Inch = 72 Points
  • 1 Point = 20 Twips OR 1 Twip = 1/20 Point

For simplicity, we have used constant values in the program for page size, orientation, and margins. However, you can modify the code to pass these values as parameters—along with the report name—when calling the program for each report. This approach offers greater flexibility, allowing the same program to handle reports with different page settings.

Next, we’ll explore how to modify the values on the Columns tab of the Page Setup dialog box, available from the File menu.

Earlier Post Link References:

Share:

Network And Report Page Setup

Network And Report Page Setup.

When an MS Access Application is installed on a network, security is one of the major issues that the Database Developer has to tackle. This includes the security of data, objects within the database, and the Database file itself. To learn more about securing a database on a Network, visit the following link:

Microsoft Access Security.

We’re now going to address another common issue often faced by users, which is usually resolved temporarily through alternative methods. When MS Access reports are designed for a specific printer on the network and all users share that printer, there are generally no problems. However, if users attempt to print the report on a different printer—either another network printer or a local printer—the report may not print correctly. Differences in default paper size, page orientation, or margin settings between printers can cause the report layout to appear incorrect when printed.

To work around this issue, users typically need to preview the report, open the Page Setup menu if necessary, and manually adjust the paper size, page orientation (portrait or landscape), and margins before printing. This option is only available if the Report Page Setup command is accessible to the user. If the application uses customized menus and toolbars, this option may not be visible, complicating the process further.

For more details on Customized Menus and Toolbars, visit the following Links:

Reports PrtDevMode Property.

    To make printing easier for users, we can modify the PrtDevMode property of a report with VBA to automatically adjust critical parameters—such as paper size, page orientation (portrait or landscape), and margins—before sending the report to the printer. This ensures that the report prints correctly on any printer.

    The PrtDevMode property is a 94-byte structure containing multiple parameters that can be modified via code to control the printer behaviour.

    For this example, we’ll focus on two simple parameters for our report. Our sample report is designed in landscape mode and is to be printed on A4 paper (210 × 297 mm). We must modify the following member parameters of the PrtDevMode property of the default printer:

  • Orientation - Valid Values: 1 = Portrait, 2 = Landscape.
  • PaperSize  9 = A4 (210 x 297 mm)

Working with the Report.PrtDevMode Property Values

The above options (Orientation and Paper size) appear on the Page tab in the Page Setup Dialog box in the File Menu. We are trying to change these values at run-time through the Program.

Open a new Standard Module (Global Module) in your Database, and copy the following code into the module and save it.

Private Type str_DEVMODE
    RGB As String * 94
End Type

Private Type type_DEVMODE
    strDeviceName As String * 16
    intSpecVersion As Integer
    intDriverVersion As Integer
    intSize As Integer
    intDriverExtra As Integer
    lngFields As Long
    intOrientation As Integer
    intPaperSize As Integer
    intPaperLength As Integer
    intPaperWidth As Integer
    intScale As Integer
    intCopies As Integer
    intDefaultSource As Integer
    intPrintQuality As Integer
    intColor As Integer
    intDuplex As Integer
    intResolution As Integer
    intTTOption As Integer
    intCollate As Integer
    strFormName As String * 16
    lngPad As Long
    lngBits As Long
    lngPW As Long
    lngPH As Long
    lngDFI As Long
    lngDFr As Long
End Type

Public Sub PaperAndOrient(ByVal strName As String)
    Const DM_PORTRAIT = 1
    Const DM_LANDSCAPE = 2
    Const DM_PAPERSIZE = 9
    Dim DevString As str_DEVMODE
    Dim DM As type_DEVMODE
    Dim strDevModeExtra As String
    Dim rpt As Report

   ' Opens report in Design view.
    DoCmd.OpenReport strName, acDesign
    Set rpt = Reports(strName)

    If Not IsNull(rpt.PrtDevMode) Then
        strDevModeExtra = rpt.PrtDevMode
        DevString.RGB = strDevModeExtra
        LSet DM = DevString
        DM.lngFields = DM.lngFields Or DM.intOrientation
        'Initialize fields.
        DM.intPaperSize = DM_PAPERSIZE
        If DM.intOrientation = DM_PORTRAIT Then
            DM.intOrientation = DM_LANDSCAPE
        End If

        ' Update property.
        LSet DevString = DM
        Mid(strDevModeExtra, 1, 94) = DevString.RGB
        rpt.PrtDevMode = strDevModeExtra
    End If
    DoCmd.Close acReport, strName, acSaveYes
    DoCmd.OpenReport strName, acViewPreview
    Set rpt = Nothing

End Sub

The User-Defined Types str_DEVMODE and Type_DEVMODE

At the beginning of the code, two new user-defined data types, str_DEVMODE and Type_DEVMODE, are declared. The report’s PrtDevMode property value is copied into this structured data area, allowing us to modify specific elements and update them back into the report before printing.

Within str_DEVMODE, named RGB is defined as a 94-byte string. This 94-byte area contains 26 parameters of various data types and sizes, which are individually defined under the type_DEVMODE structure. By transferring the data from str_DEVMODE (a single block of 94 characters) into type_DEVMODE, we can modify individual parameter values before writing them back into the report’s Page Setup.

NB: If the Database is implemented with Microsoft Access Security, then all Users must have the Report Design Change Authority to run this procedure.

Preparing for a Trial Run.

  1. To try out our Program, open one of your Reports with Landscape Page Orientation in Design View.

  2. Select Page Setup from the File Menu.

  3. Select the Page Tab on the Dialog Box.

  4. Change Orientation to Portrait.

  5. Change Paper Size to A6.

  6. Save the Report and open it in Print Preview to check how it looks with the change.

  7. Close the Report after viewing.

  8. Create a Command Button on an existing Form or on a new Form and keep the Form in Design View.

  9. Display the Property Sheet of the Command Button (Alt+Enter or View -> Properties).

  10. Change the Name Property Value to cmdPreview.

  11. Copy and paste the following code into the Code Module (View -> Code) of the Form.

    Private Sub cmdPreview_Click()
         PaperAndOrient "MyReport"
    End Sub
    
  12. Replace "MyReport" with your own Report Name.

  13. Save the Form and open it in a normal view.

  14. Click the cmdPreview button to run the program, to change the Page setup correctly, and open it in Print Preview.

Type PaperAndOrient "MyReport" in the Debug Window (Ctrl+G) and press Enter to run the Program directly without the Form and Command Button.

Open the Report again in design view and check whether the erraneous changes that you have made manually in the Page Setup Dialog Box, to test the program, have now been corrected through the program or not.

Next, we will see how to change the values on the Margins tab of the Page Setup Dialog Box through the Program.

Earlier Post Link References:

Share:

PRESENTATION: ACCESS USER GROUPS (EUROPE)

Translate

PageRank

Post Feed


Search

Popular Posts

Blog Archive

Powered by Blogger.

Labels

Forms Functions How Tos MS-Access Security Reports msaccess forms Animations msaccess animation Utilities msaccess controls Access and Internet MS-Access Scurity MS-Access and Internet External Links Queries Array Class Module msaccess reports Accesstips msaccess tips WithEvents Downloads Objects Menus and Toolbars MsaccessLinks Process Controls Art Work Collection Object Property msaccess How Tos Combo Boxes ListView Control Query VBA msaccessQuery Calculation Dictionary Object Event Graph Charts ImageList Control List Boxes TreeView Control Command Buttons Controls Data Emails and Alerts Form Custom Functions Custom Wizards DOS Commands Data Type Key Object Reference ms-access functions msaccess functions msaccess graphs msaccess reporttricks Command Button Report msaccess menus msaccessprocess security advanced Access Security Add Auto-Number Field Type Form Instances ImageList Item Macros Menus Nodes Recordset Top Values Variables msaccess email progressmeter Access2007 Copy Excel Expression Fields Join Methods Microsoft Numbering System RaiseEvent Records Security Split SubForm Table Tables Time Difference Utility WScript Workgroup Wrapper Classes database function msaccess wizards tutorial Access Emails and Alerts Access Fields Access How Tos Access Mail Merge Access2003 Accounting Year Action Animation Attachment Binary Numbers Bookmarks Budgeting ChDir Color Palette Common Controls Conditional Formatting Data Filtering Database Records Defining Pages Desktop Shortcuts Diagram Disk Dynamic Lookup Error Handler Export External Filter Formatting Groups Hexadecimal Numbers Import Labels List Logo Macro Mail Merge Main Form Memo Message Box Monitoring Octal Numbers Operating System Paste Primary-Key Product Rank Reading Remove Rich Text Sequence SetFocus Summary Tab-Page Union Query User Users Water-Mark Word automatically commands hyperlinks iSeries Date iif ms-access msaccess msaccess alerts pdf files reference restore switch text toolbar updating upload vba code