Learn Microsoft Access Advanced Programming Techniques, Tips and Tricks.

Unsecured Database and Users Log

Introduction

When performing data entry or editing important database tables, it is common practice to save a timestamp along with the user name for each record to mark the event. This is typically implemented using the form's Before Update event procedure. A sample procedure is provided below:

Private Sub Form_BeforeUpdate(Cancel As Integer)
     Me![EditedBy] = CurrentUser
     Me![EditedDt] = Now
End Sub

These fields will be added to the Data Entry/Editing Form from the Table, but will be kept hidden or disabled to prevent manual changes. The familiar =Now() function retrieves the current date and time, while the CurrentUser() Function provides the username of the person currently logged into the database. In this context, we are focusing on the usage of the CurrentUser() function.

The CurrentUser() function returns the correct username in a secured database—that is, a database configured with Microsoft Access security features and shared on a network. When an authorized user opens an instance of the database on their workstation, they must enter their authenticated username and password to gain access. Consequently, the value returned by CurrentUser() will always correctly identify the logged-in user.

The Admin User.

However, if the database is not secured with Microsoft Access Security, the CurrentUser() function will always return the value Admin. Any user who opens an unsecured database on a network is automatically logged in by MS-Access as the Admin user, a member of the Admins group, without being prompted for a user name or password.

We will not delve into Microsoft Access Security issues here (I have already dedicated about nineteen pages to this topic under the “Microsoft Access Security” section on the Main Menu of this site). Instead, we will focus on how to capture the network user name and workstation name correctly when working with an unsecured database shared on a network.

Fortunately, there is a simple trick to capture the network user ID and workstation ID and record these values in your table fields. No lengthy VBA program is required. But first, let’s see where this information is stored and how you can retrieve your own network user ID and workstation ID from your computer’s memory.

Finding Computer Name

  1. Select Run from the Start Menu.
  2. Type Cmd and click OK. A DOS window will open up.
  3. Type SET and press the ENTER key.

A long list of environmental variables will appear in the DOS (Disk Operating System) window, the core system underlying the Windows Operating System. These variables are loaded into memory when you start your computer or after logging off and back on, and they play an important role in the smooth execution of your day-to-day tasks.

However, for our purpose, we are interested in only two specific values from this list. Look for the following entries:

COMPUTERNAME=your Computer Name

USERNAME=your Network User ID

These two entries may not appear close to each other in the list of Environment Settings — they can be located anywhere within it. If necessary, use the scroll bar to move up or down through the list until you find them. Once you’ve confirmed their presence, type EXIT and press ENTER to close the DOS window.

In Microsoft Access, there is a built-in function called Environ() that allows you to retrieve these values directly from memory and use them anywhere in your database — such as in forms, reports, or VBA procedures.

Example-1: X = ENVIRON("USERNAME")

This will bring the Network User ID of the User from memory.

Example-2: X = ENVIRON("COMPUTERNAME")

This will get the WorkstationId (Computer Name) from the Environment String that we have seen in Memory.

In fact, we can simplify the use of the Environ() function by creating two small, reusable functions and adding them to a global module in our Function Library. Once defined, these functions can be used anywhere in our application — just like the built-in CurrentUser() function — to retrieve the Network User Name and Workstation (Computer) Name directly from memory.

Since CurrentUser is a built-in function, we will use something different that we can memorize easily. We can even use the Parameter Values of Environ() function as our own function names.

Add the following Functions in the Global Module (Standard Module) of your Function Library Database or in the shared database itself:

Public Function UserName() As String
    UserName = Environ("UserName")
End Function

Public Function ComputerName() As String
    ComputerName = Environ("ComputerName")
End Function

After adding these Functions to the Global Module, you can call them from wherever you want, like:

Private Sub Form_BeforeUpdate(Cancel As Integer)
On Error Resume Next
      If Err then
        Cancel = True
      Else
       Me![EditedBy] = UserName
       Me![EditedDt] = Now
      End IF
End Sub

OR

If Msgbox("Hi, " & UserName & ", Shutdown Application..?", vbQuestion+vbDefaultButton2+vbYesNo,"Shut Down")=vbYes then
     docmd.Quit acQuitSaveAll
End If 

Similarly, the ComputerName() Function gets the Workstation ID of the User from Memory.

Monitoring Intrusions

These are also useful to monitor unauthorized use of third parties, or somebody not entrusted to maintain the open database,  from the Network Drive, by sending an Alert Message to you from the Startup Screen or the Main Switch Board Form's Open Event Procedure, like the example given below:

Private Sub Form_Open(Cancel As Integer)
       Call Shell("NET SEND YourNetworkID from: " & ComputerName & " User: " & UserName & " opened: " & Currentdb.Name & " at: " & Now())
End Sub

Note: NET SEND was a Windows XP Command; its new Version is MSG Command.

Refer to the following Posts for more advanced techniques:

  1. Record the Open/Close events, and activity of Databases in a log Text File and send alerts to your machine in a pop-up message box, which you can turn on or off as you need them.

    Database Open/Close Event Alerts.

  2. Sending Alerts to other User Workstations with useful information that they need to know about, immediately after processing activities take place on one side of the User Groups.

    Sending Alerts to Workstations.

  3. How many users are currently online using a particular database, and how to communicate with them by sending Alerts to their Workstations?

    Who is online?

  4. Send E-Mails to remote Users with attachments of important Reports in Snapshot Format.

    Automated E-Mail Alerts

Earlier Post Link References:

Share:

Msaccess Report and Page Totals

Introduction

When designing reports in Microsoft Access, you might have noticed that functions like Sum, Count, or Avg don’t work in the Page Header or Page Footer sections. These aggregate functions only operate in the Report Header/Footer or Group Header/Footer areas — not on individual pages.

That might sound limiting, but there’s a workaround. You can still use simple expressions in the Page Header or Footer to display useful, dynamic information — no VBA code required.

For example, when you build a report using the Report Wizard, Access automatically adds expressions in the Page Footer to show the current date, time, and page numbers.

  • The expression =Now() prints the date and time.

  • The formula "Page " & [Page] & " of " & [Pages] displays the page numbering.

While these expressions can’t calculate page-wise totals, you can use them to show running sum values on each page — both in the header and footer. It’s a simple, elegant way to make your reports more informative without writing a single line of VBA.

The Targeted Readers of this website.

By going through this website, one might get the impression that to work with MS Access, you must know and use VBA code. This is not true. Many tasks can be automated using Macros. The first MS Access application I developed in 1997 for our Audit Department didn’t contain a single line of VBA code—and it’s still in use today. The only changes I’ve made to that application over the years were converting it from MS Access Version 2 to MS Access 97, then to MS Access 2000, and giving the main switchboard form a facelift.

You can find plenty of material on the Internet covering the basic usage of MS Access (or at least that was my impression when I started this website). With these resources, you can quickly learn the essentials—Table Design, Relationships, Queries, Forms, Reports, and Macros. Once you’ve mastered the basics and are ready for something more advanced, interesting, and exciting, this website is designed especially for you.

When you’re ready to move beyond the basics, explore the contents here. VBA code is the main driving force behind all the examples presented on this site.

Trial Run Without VBA Code

Therefore, let us try this experiment without the VBA Code. We need a simple Report to do that.

  1. Import the following Tables from the Northwind sample database (C:\Program Files\Microsoft Office\Office11\Samples\Northwind.mdb):

    • Order Details
    • Products
  2. Open a new Query in Design View, do not select a Table or Query from the displayed list, and click Close.

  3. Select SQL View from the View Menu to display the SQL Editing Window of the Query.

  4. Copy and paste the following SQL String and save the Query with the name Products_ListQ:

    SELECT [Order Details].OrderID, Products.ProductName, [Order Details].Quantity
    FROM [Order Details] INNER JOIN Products ON [Order Details].ProductID = Products.ProductID
    WHERE ((([Order Details].OrderID) Between 10248 And 10300));
    

    We have selected only a few records from the Order Details Table, with OrderID numbers between 10248 and 10300, so that the sample Report will have only a few pages in Portrait Mode.

  5. Click on the Product_ListQ Query and select a Report from the Insert Menu.

  6. Select Auto Report: Tabular from the displayed list and click OK to create the Report.

  7. Save the Report with the name Products_List.

  8. Open the Report in Design View and change the design to match the sample image given below.

  9. Add the Red-colored text boxes and Labels to the Design of the Report as explained below:

    • Make a copy of the Quantity field in the Detail Section and position it to the right as shown.

    • Display the Property Sheet (View ->Properties) of the copied control and change the Name Property value to QtyRunningSum.

    • Change the Running Sum Property Value to Over All.

    • Create a Text Box on the Page Header of the Report and change the Name property value of the Text Box to BF (stands for Brought Forward), the Border Color Property Value to 128, and the Font Weight Property Value to Bold.

    • Change the Caption value of the Child Label to B/F:.

    • Create a Text Box on the Page Footer of the Report, change the Name Property Value to Page-Total, Border Color Property Value to 128, and the Font Weight Property Value to Bold.

    • Change the Caption of the attached Child Label to Page Total, as shown in the sample design above.

    • Create another Text Box to the right, change the Name Property Value to CF (stands for carrying Forward), Border Color Property Value to 128, and the Font Weight Property Value to Bold.

    • Change the Name Property Value of the child label to lblCF and the Caption property value to C/F:.

    We have changed the Label's Name property value to lblCF so that we can address it in the Program to change the Caption value to TOTAL on the Last Page of the Report. At that point, it is not appropriate to show the label as C/F: (Carry Forward). We will do this with the Program while creating Page Totals on the Report.

    Before that, we will display values in the other TextBoxes created for Running Sum, BF, and CF values without a VBA Program.

    At a later stage, we will hide the Quantity field copied for calculating Running Sum, but keep it visible now, and use it for calculations on the other two controls, BF and CF, in the Page Header and Page Footer Sections of the Report, respectively.

  10. Save the Report.

  11. Open the Report in Print Preview and check how the Values are appearing in the copied Quantity Field with the Running Sum Property Value set to Overall All.

    Each Quantity value is summed up in this control, and the last line has the running sum value on the Page. If we put a reference in the QtyRunningSum Text Box in the CF Text Box in the Page Footer area, we can transfer this Value into that Control, showing the cumulative total value at the end of each page.

  12. Open the Report in design view and write the following expression in the Control Source Property of the CF Text Box:

    =[QtyRunningSum]

  13. Now, preview the Report again and check whether the last total value is appearing in the Page Footer area in the CF TextBox or not. Advance to the next page and check the value showing there, too.

    The next step is to create the Page Header Control BF Value. If you are smart, then you might have already made the change in the BF control. 

    You have two choices to put the Value in this Control, but both have a problem getting it right.

    The first choice is to put a reference to the CF TextBox control in the BF TextBox, like =[CF], to bring the Total Value from the previous page to the current Page Header.

    The second option is to place a reference in the QtyRunningSum text box, such as =[QtyRunningSum]. This approach is also logically correct, assuming that the BF control retrieves the cumulative total from the previous page. This is because the QtyRunningSum control in the Detail section of the current page is processed after the Page Header section’s Format and Print events.

    Read the following Articles where we have discussed the Format and Print Events of the Reports, and learn a few other Tricks we have tried earlier:

  14. Report Line hiding Tricks tried earlier

  15. Choose one of the two options mentioned above and set the Control Source property to either =[CF] or =[QtyRunningSum]. Personally, I prefer the first option, as we’ll be deleting the QtyRunningSum text box when we implement the program for the page total.

  16. Open the Report in Print Preview and check the Page Header Section BF Text Box Value.

    In both cases, you will get the same result: the starting running sum value of the first line in the Detail section of the current page. In other words, the value in the BF control equals the cumulative total from the previous page plus the quantity value of the first line on the current page. This happens because, even after the Page Header/Footer control values go through the Format and Print events, the CF and BF controls continue to update internally with the changing running sum values. If this were not the case, we could have easily calculated the page total by simply taking the difference between the CF and BF control values on the same page.

    So, if we subtract the first line Quantity value of the first line running sum QtyRunningSum or previous Page CF Text Box Value, we will get the BF TextBox value correctly.

  17. This is the reason why we depend on VBA Code to solve the Page Total problem.

  18. Change the expression in the BF Text Box in the Page Header Section as follows:

    • =[CF] - [Quantity]

      Or

    • =[QtyRunningSum] - [Quantity]

    We don't want the QtyRunningSum to display now, and we will hide it from the Detail Section.

  19. Click on the QtyRunningSum Text Box, display the property sheet, and change the Visible property value to No.

  20. Save the Report and open it in Print Preview.

Now, the Page Header Text Box BF and Page Footer Text Box CF Values are shown correctly. Move to the next page and check the values appearing there.

We will take a copy of this Report to our Page Total example. We will use VBA Code to calculate and update the Page Total and CF values on the new Report.

New Report with VBA Code.

  1. Create a copy of the Product_List Report and name it Product_List2.

  2. Open the Report in Design View.

  3. Modify the expression in the Control Source Property of the BF Text Box in the Page Header as =[CF].

    We will calculate and update only the CF Text Box Value through the Program in the Page Footer Section.

    Since we are not depending on the Running Sum value, we don't have to worry about the side effect we experienced in the earlier method. The correct value will be picked from the previous page control CF Text Box.

  4. Delete the QtyRunningSum Text Box from the Detail Section.

  5. Select Code from the View Menu to display the Class Module of the Report.

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

    The Report Class Module VBA Code

    Option Compare Database
    'gobal declarations
    Dim x_pagetotal As Long, x_CF As Long
    
    Private Sub Detail_Print(Cancel As Integer, PrintCount As Integer)
    If PrintCount = 1 Then
       x_pagetotal = x_pagetotal + [Quantity]
       If Retreat = False Then
             x_CF = x_CF + [Quantity]
       End If
    End If
    End Sub
    
    Private Sub PageFooterSection_Print(Cancel As Integer, PrintCount As Integer)
    If PrintCount = 1 Then
        [PageTotal] = x_pagetotal
        x_pagetotal = 0
        [CF] = x_CF
    End If
    If [Page] = [Pages] Then
        Report.Controls("lblCF").Caption = "TOTAL:"
    End If
    End Sub
    

    Review of Code.

    We have declared two variables, x_pagetotal and x_CF, in the global area of the module. In the Detail Section Print event procedure, we add the quantity values to both variables. In the Page Footer Print event procedure, we update the page total and the CF text box values, then reset the x_pagetotal variable to zero. At this point, we need to check whether the current page number is the same as the last page number. If it is, we change the lblCF label caption to “TOTAL:”.

  7. Open the Report in Print Preview and check the Page Total, BF, and CF Text Box Values. Move to the next page, and the control values are there.

The Report image with cut Sections of the Page Header/Footer areas showing the Control Totals on page number three is given below.

Share:

Detail and Summary from same Report

Introduction.

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 list of all open forms 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) on 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 TAB Key, and 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 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 code for 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 and 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 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 value is 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:

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 Class Module External Links Queries Array msaccess reports Accesstips WithEvents msaccess tips Downloads Objects Menus and Toolbars Collection Object MsaccessLinks Process Controls Art Work Property msaccess How Tos Combo Boxes Dictionary Object ListView Control Query VBA msaccessQuery Calculation 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 RaiseEvent Recordset Top Values Variables Wrapper Classes msaccess email progressmeter Access2007 Copy Excel Export Expression Fields Join Methods Microsoft Numbering System Records Security Split SubForm Table Tables Time Difference Utility WScript Workgroup 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 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