Thursday, 15 December 2011

Enable and Disable a Form Control using VBA

Earlier this week I was working on a design for an Order Management Database, and one of the tasks I dealt with involved dynamically Enabling or Disabling one of the form's Command Buttons' using VBA.  This gave me the idea for the present Access tip.

The command button was located on a Customer Details form which had an Orders Subform in the lower section.  The reason I wanted to Enable or Disable the Command Button (located on the parent form), was that it was used to Delete the selected record highlighted in the Orders Subform's datasheet. I needed to get the Delete Button to enforce the business rule whereby once an order has been confirmed, it should not be deleted. As such, if the user highlighted a confirmed order in the subform, the Delete button is dynamically disabled, and vice versa.

To do this I made use of the Order Subforms ON CURRENT event.  This event is triggered whenever the focus moves from one record to another, or when the first record receives the focus as the form opens.  As such, if a user selects a record in the subform datasheet by clicking on one of the rows, the subform's ON CURRENT event fires.  This is the code I wrote to determine whether the Delete button should be Enabled or Disabled.

If IsNull(DLookup("OrderConfirmed", "tblOrders", "OrderId = " & Me!OrderId)) = False Then
        Forms!frmcustomer!ctlDeleteOrder.Enabled = False
Else
        Forms!frmcustomer!ctlDeleteOrder.Enabled = True
End If

As you can see, I have used an If ... Then ... Else Statement to determine whether or not the Order has been confirmed.  In order to create the conditional expression, I used the IsNull and DLookUp functions together to see if  the OrderConfirmed field of tblOrders contained a date. The IsNull function returns a boolean value, True or False, to indicate whether its parameter (in this case the result of a DLookUp function) is or is not null;  and the DLookUp function, looks up the value contained in the OrderConfirmed field of tblOrders where OrderId matches that of the current record displayed on the Orders subform.  

Since the presence of a date in the OrderConfirmed field indicates that the order has been confirmed, the IsNull function returning the value of FALSE (remember this is a double negative!), tells us the order has indeed been confirmed, and vice versa.   As such, when the condition of the first line of the If Statement is False, the Delete Command Button on the main form should be disabled.  This is done by referencing the Delete Command Button's ENABLED property, and setting it to FALSE:

 Forms!frmcustomer!ctlDeleteOrder.Enabled = False

And if the result of the If ... Then ... Else Statement had returned TRUE, the Delete Command Button is Enabled by setting it's ENABLED property to TRUE:

Forms!frmcustomer!ctlDeleteOrder.Enabled = True


Friday, 9 December 2011

An Expression to Obtain a Full Name from Three Separate Fields

There is a very good reason why we separate name fields when we create an Access Table.  If we store the Title, First Name and Surname in a single field, we limit the capability of our application to interrogate this part of our data.  For example, if we store a full name in a single field, we would not be able to sort a list of names into alphabetical order (because Access cannot differentiate between a title, first name and surname). Moreover, we would also not be able to extract just the title and surname in order to address a letter. This is because the individuals first name comes in between, thereby preventing us from using the name in a mail merge operation.

As such, there is a general database design convention of storing all the elements of a name in separate fields. This gives us maximum flexibility and control when we come to process this data.  What's more, we still have the capability of joining (concatenating) the full name back together again through use of an expression in a query's calculated field, for example.  So how is this done exactly?

Imagine we have a list of names stored in a table.  We have separate fields for Title, FirstName, and Surname.

Figure 1:  An Access Table containing a list of names
stored in separate fields.
The expression we are going to use to concatenate the name into a single whole is as follows:

        [Title] &" " & [FirstName] & " " & [Surname]


The three fields are separated by two ampersands (&), and a string containing a single space in between.  The ampersand concatenates the various elements of the name, and the empty space between the quotation marks simply creates a space between the three fields when joined together.  So we have a total of five separate elements concatenated by the ampersand operator - ie Title space FirstNamespace Surname.

As mentioned above, this expression can be used in a calculated field of an Access Query.  To do so, just enter an alias (ie the name we are going to call the calculated field) with a colon in front of the expression.  For example:


        FullName: [Title] & " " & [FirstName] & " " & [Surname]


This is entered into the FIELD row of the query design grid as follows:

Figure 2: Expression to concatenate a full name
entered into the Query Design Grid.
Then, when we run the query we get a list of full names, each one appearing as a single field:

Figure 3: Concatenated Names appearing in
Query Result.





Thursday, 1 December 2011

Adding a Group and Sort to an Access Report

Adding a Group and Sort to an Access Report has the potential to make our data much easier to read.  This is because our information may appear in a clear and more logical format. Take a list of Contacts for example.  We may have a number of contacts working for the same organisation: wouldn't it be convenient for our contact list to be grouped by organisation? Then, depending on how many contacts we have within each organization, it may also make sense to sort each organizations' contact's into alphabetical order.

So instead of having a list like this:

Figure 1: Contact list without Group or Sort.


We have a list like this:

Figure 2: Contact list grouped by Company and sorted by surname and first name.

Both contain exactly the same data, but the list in figure 2 has been grouped by company and sorted by surname and first name (within each group).  You can download these reports by clicking this link: Group and Sort Sample Database.

Lets take a look at how these reports were created.

I began by creating the report from figure 1. I then opened it in DESIGN VIEW and added the Group and Sorts before re-aligning the columns.  Here are the step by step instructions:

Stage One - Create a Basic Report
  1. Highlight the Contacts Table in the NAVIGATION PANE.  The table in the sample database is called tblContacts, and it is the RECORD SOURCE for both Reports.
  2. Click the REPORT icon (located in the REPORTS group of the CREATE ribbon).  This is the quickest way to create a report based on a particular record source.
  3. When the report opens in LAYOUT VIEW, click this symbol:   (it should be located at the top left corner of the ID column).  This highlights all the cells which are currently joined together. 
  4. Then click the REMOVE icon to separate them.  This icon is located in the CONTROL LAYOUT group of the ARRANGE ribbon.  It will now be possible to move individual text boxes and labels independently when we go to DESIGN VIEW.  However, before we do that, we shall first add the Group and Sorts.

Stage Two - Adding the Group and Sorts

  1. Select the HOME ribbon, and then pick DESIGN VIEW from the VIEWS group.
  2. Make sure the GROUP, SORT AND TOTAL icon is highlighted (it is located in the GROUPING AND TOTALS group of the DESIGN ribbon).  You should see the GROUP, SORT AND TOTALS pane open below the DESIGN GRID.
  3. Click ADD A GROUP from the GROUP, SORT AND TOTALS pane (see figure 3 below).
  4. Then select the Company field from the list which appears.  This creates a Company Header (see figure 4 below).
  5. Highlight the Company text box and make sure no other controls are highlighted.
  6. Cut and past the Company text box so that it is positioned close to the left margin within the Company Header.
  7. Reposition the labels and text boxes so that they are aligned in a neat logical fashion (see figure 4 below).
  8. Next click ADD A SORT in the GROUP, SORT AND TOTALS pane.  
  9. Then select Surname from the list.  Notice how a new level has been created below the Company Group.  This is because we want Access to apply the group first and then sort the surnames within the group.
  10. Click ADD A SORT again.
  11. Then select Firstname from the list. This creates another sort, but this time on Firstname.  Notice how this sort is on a level below the first sort.  This is because we want Access to begin by sorting the Surnames, and then if there are duplicates, to sort on FirstName.  This follows the general convention of placing the whole name in alphabetical order.
Figure 3: The Group, Sort and Total pane.

Figure 4: Report Design View showing a Group Header for the Company field.

You can now open the report in REPORT VIEW to see the grouped and sorted results.

Thursday, 24 November 2011

Using Events and Manipulating Property Settings - Learning Access VBA - Tutorial 4


This is the last post in the series of introductory tutorials on Learning Access VBA.  So far we have covered a wide range of areas - the VBA Development Environment, variables, referencing form controls, the conditional IF ... THEN... ELSE statement, and Loops.  There is still much more we could have covered, even at an introductory level (arrays for example).  However, I wanted to end the series by focusing on how we use VBA to automate the access database applications that we create.  To do this we are going to examine how to trigger our VBA code through tapping into events and manipulating form property values.

Using Events

If you have been following the Learning Access VBA series, you will already be familiar with the Command Button's ON CLICK event.  The user clicks the command button at run time thereby firing the ON CLICK event, which in turn triggers any code that we have written in that event's sub routine. There are many more events which we can also use to trigger our code.  A text box control has, for example, an ENTER event and an EXIT event.  The ENTER event fires when the user moves the cursor into the text box, and the EXIT event fires when it moves out again.  The code that you may attach to such events depends entirely on the particular needs of the application.  The point is, they are there to use if we have written any code to trigger in response to the particular event in question.

So far I have only mentioned events for individual controls.  Some of the more important events, however, occur at Form or Report level.   These include the Forms ON CURRENT, BEFORE UPDATE and ON LOAD events.  The ON CURRENT event fires just before a form displays a new record; BEFORE UPDATE occurs just before the record is saved; and the ON LOAD event occurs as a form loads data contained in a table, or derived from a query.

You can see the whole range of form or reports events by opening the PROPERTIES pane (whilst in design view) and clicking the EVENTS tab.  In today's exercise, however, we are just going to focus on the form ON OPEN event.  This fires as the form opens, but before any data is loaded from the forms RECORD SOURCE.  As such it occurs before the ON LOAD even which was mentioned above.  Consequently, this is a good event to use for triggering code for tasks like prompting the user for parameters, applying filters, or even changing the form's RECORD SOURCE property.  In the exercise we shall be doing, we are going to use the ON LOAD event to determine whether a form opens in DATA ENTRY, EDIT or read only mode. Before we begin, lets first take a look at how we can manipulate form properties using VBA code.
Figure 1: Some of the Events listed in the
EVENTS tab of the PROPERTY SHEET.

Manipulating Properties

You may already have experience of setting properties in Design View using the PROPERTIES sheet .  With VBA we can also read, test and dynamically change property setting during runtime.  The key to doing this is understanding how to correctly reference the property of the control, form or report.  This task is easier when our code is contained within the VBA Module of the form or report being referenced.  Suppose we want to reference the DATA ENTRY property of a form called frmCustomers.  If we were referencing the property from the VBA module attached to frmCustomers we can use the ME keyword.  This acts as a short cut when referencing the attached form.  This is how it works:

Me.DataEntry


If, on the other hand, we were attempting to make the same reference from a module outside of frmCustomer we would need to write the full reference like this:


Forms![frmCustomers].DataEntry


As you can see, we not only need to write the name of the form in question but we also need to specify the FORMS collection object to which it belongs.  It is also worth pointing out that in VBA we use the exclamation mark (!) to separate two objects when the preceding object belongs to the one coming after, but we use the full stop (.) to separate an object from one of its properties.  Any formreport or control name in the reference also need to be contained within square brackets if the name contains a space or a reserved word (but this is not essential if the name is constructed without these 'problematic' elements).


Now we know how to create property references, we can use them in our code to read, test or dynamically change property values. For example:


To store a property setting in a variable called varDataEntry:
    varDataEntry = Me.DataEntry


To test the value of a property setting:
    If Me.DataEntry = true Then


To change the value of a property setting:
    Me.DataEntry = False


Exercise

In the following exercise we are going to create a switchboard form with three command buttons - these are ctlAdd, ctlEdit and ctlRead. All three buttons open the same form, frmContacts, but the first button opens the form in Data Entry mode, the second in Edit mode and the third as Read Only.  So how does it do this?

Figure 2: frmSwitchboard
When the user clicks one of the command buttons, it fires the command button's ON CLICK event where there is some VBA code to open the form with the DoCmd.OpenForm method.  One of the parameters of the OpenForm method passes an OpenArgs parameter to frmContact.  The value of this parameter (which is a string) is then stored by Access in frmContacts OPENARGS (Open Argument's) property.

As frmContacts opens, the ON OPEN event fires, triggering another block of VBA code.  This code tests the value of the forms OPENARGS property using the IF ... THEN ... ELSE statement. This is to ascertain whether the value contained in the OPENARGS property is  "Add", "Edit" or "Read".  These were passed by the OpenForm method as mentioned above.  If the OPENARGS property value is "Add" our code will set the form's DATA ENTRY property to TRUE; if the value is "Edit", DATA ENTRY is set to FALSE; if "Read", the DATA ENTRY, ALLOW ADDITIONS, ALLOW DELETIONS, and ALLOW EDITS properties are all set to FALSE.

You can download the completed solution by clicking this link: Events and Properties Exercise.  The instruction for creating the exercise follow below:

Stage One - Creating the Switchboard

  1. Create an unbound switchboard form called frmSwitchBoard.
  2. Add three Command Buttons called ctlAdd, ctlEdit, and ctlRead.
  3. Attach the following code to the Command Button's ON CLICK event - see the first tutorial in the Learning AccessVBA series if you need help doing this.
Private Sub ctlAdd_Click()
    DoCmd.OpenForm "frmContacts", , , , , , "add"
End Sub


Private Sub ctlEdit_Click()
    DoCmd.OpenForm "frmContacts", , , , , , "edit"
End Sub


Private Sub ctlExit_Click()
    Application.Quit   
End Sub


Private Sub ctlRead_Click()
    DoCmd.OpenForm "frmContacts", , , , , , "read"
End Sub

NB You may have noticed there are a number of blank parameters passed in our use of the OpenForm method - hence the comma's .  That is because many of the parameters are optional.  We are just passing two parameters - the name of the form to be opened, and the OpenArgs parameter as discussed above.   Incidentally we could have used the DataMode parameter to accomplish the object of this exercise more directly, but I wanted  to demonstrate the working of the form ON LOAD event.

Stage Two - Creating frmContacts


Before you create frmContacts you will first need to create the table - tblContacts - which is going to be its record source.  It's not particularly import which fields are used, but the example database I created has four fields - ID, FirstName, Surname and Company, and the following sample data:

Figure 3: tblContacts with sample data.
Figure 4: frmContacts

  1. Create a new form called frmContacts
  2. Set the forms RECORD SOURCE property to tblContacts.
  3. Add the four fields of tblContacts to the form
  4. Add the following code to the forms ON OPEN event:

Private Sub Form_Open(Cancel As Integer)

    If Me.OpenArgs = "add" Then
        Me.DataEntry = True
    ElseIf Me.OpenArgs = "edit" Then
        Me.DataEntry = False
    ElseIf Me.OpenArgs = "read" Then
        Me.DataEntry = False
        Me.AllowAdditions = False
        Me.AllowDeletions = False
        Me.AllowEdits = False
    End If 
  
End Sub


When you have done this, save your work, and open the switchboard to try out the different buttons.

This concludes the Learning Access VBA series of tutorials.  I hope you have found them helpful, and that they have given you a basic introduction to Access programming .  I intend to cover more advanced VBA topics next year.

Friday, 18 November 2011

Introduction to VBA Loops - Learning Access VBA Tutorial 3

This is the third tutorial in the Learning Access VBA series. In the first tutorial we learnt how to create a simple VBA Sub Procedure and set up up Variables; and in the second, we looked at Evaluating Conditions with the IF THEN ELSE statement.  Today we are going to move on to VBA Loops.

Loops are are common feature of most programming languages, and VBA is no exception.  Loops enable lines within in a defined section of code to be repeated as many times as is necessary, until a specific point is reached when the program flow may move on to the next section of code.  There are different kinds of loops in VBA.  The FOR ... NEXT loop, for example, repeats the loop a set number of times.  We would use  this if we wanted the loop to repeat, say, 10 times, and then move on.  With the DO ... UNTIL loop, on the other hand, the defined section of code would repeat indefinitely, until a specified condition arises.  This may be something to use if you want to repeat the loop until a user enters a specific value into an input box.


The FOR ... NEXT Loop

Let take a look at how the FOR ... NEXT loop is constructed.

For I = 1 To 10
    lines of code to be repeated
Next I

The FOR NEXT loop has a counter which is referred to above as I.  This is basically a variable name - we could have called it a different name if we had wanted.  The first line of the statement (For I = 1 to 10) sets the counter at 1, and tells the procedure to continue repeating the lines of code until the counter reaches 10.  Each time the program flow executes the NEXT keyword at the end of the statement, the counter is increased by 1.  As it does so, it also checks whether the counter has reached 10 (or whatever level it had been set).  If it has not, the lines of code repeat another time until the Next keyword executes again, increasing the count by another 1.  Once it reaches the level set, the program flow continues past the NEXT keyword into the next section of code.

So let's try an exercise to see how the FOR ... NEXT loop works in practice.

  1. Create a new unbound form in Design View.
  2. Add a Command Button.
  3. Add the VBA code (listed below) to the button's OnClick Event (please see the first Learning VBA Tutorial if you need help with this, or any of the previous steps):
  4. Click the SAVE icon.

Dim I As Integer
Dim x As Integer    
Stop
For I = 1 To 10    
    Randomize
    x = (9 * Rnd() + 1)
    Debug.Print "Counter = " &  I  & ";  Random Number = " &  x        
Next I

Before we try out the code, we are going to open the VBA Editor's Immediate Window.  To do this,  click the VBA VIEW menu, and then click IMMEDIATE WINDOW from the menu list.  When we run the code, the STOP statement will re-open the VBA editor during runtime, to allow us to Step Into each
line of code as it executes.  Then when the program flow reaches the Debug.Print line, the Counter value and the value of variable x (a random number generated by the code) is displayed in the immediate window.

When you are ready, open the Access form and click the command button we created earlier.  The VBA editor will then open.  The current line being executed is highlighted in yellow.  To advance to the next line we click Step Into from the DEBUG menu (alternative use the F8 key which is far more convenient).  Then watch what happens as the program flow progresses through the loop.  Also take note of the counter value which is displayed before the random number in the immediate window.

Figure 1: The VBA Editor with Immediate Window.  The editor was opened during
run time due to the STOP command which acted as a breakpoint.
Another way of adding a break point is to click on a line
and then select TOGGLE BREAKPOINT from the DEBUG menu.
The DO ... UNTIL Loop

Let's take a quick look at the how the DO ... UNTIL loop is constructed.

Do Until variableName = thisValue
    lines of code to be repeated
Loop

The first line of the DO ... UNTIL loop is testing whether the specified condition is true or not.  If it is not true, the lines of code within the loop are executed until the LOOP keyword is reached at the end of the statement. Then the program flow returns to the first line of the statement where the condition is tested again.  If the lines of code within the loop had changed the value of the variable being tested so the condition becomes true, the program flow exits the loop at this top line.

Here is an example of a DO ... UNTIL Loop that you may  like to try for yourself.  Just change the code from the first exercise so that this runs in its place.


Dim varNumber As Integer
Dim varResponse As Integer
    
Randomize
varNumber = 9 * Rnd() + 1


Do Until varResponse = varNumber
        
    varResponse = InputBox("Enter a Number Between 1 and 10", "Guess A Number")
        
    If varResponse > varNumber Then
        MsgBox "Your Guess is too High"
    ElseIf varResponse < varNumber Then
        MsgBox "Your Guess is too Low"
    Else
        MsgBox "Congratulations: Your Guess is Correct."
    End If
        
Loop
    
    MsgBox "Game Over!!!"

(NB for the sake of clarity, this code does not deal with the possibility that the user may click cancel or enter an empty value in the input box.  If this happens an error results, and the procedure crashes!)

This code is for a simple Guess the Number Game.  The user clicks the command button to begin.  The code then generate a random number and stores it in a variable called varNumber.  Once the program flow gets to the loop, the variable varResponse is tested to determine whether it is equal to the random number stored in varNumber.  If it is not, the lines of code within the loop are executed.  The user is asked to guess the number in a pop up input box.  The code then enters an IF ... THEN ... ELSE statement where the user is informed whether his guess is too high, too low, or is indeed correct.  When the LOOP keyword is reached, the program flow returns to the DO UNTIL keyword where the value of the guess is tested against the random number to determine whether the lines of code should be repeated, or whether the program flow can continue past the LOOP keyword.

When you try this code, please feel free to enter a BREAKPOINT so you can follow the program flow for yourself.  You can do this by entering the STOP statement as we did in the first exercise.  Alternatively, click the line where you want to insert the breakpoint, and then click TOOGLE BREAKPOINT from the DEBUG menu.  The line is then highlighted in red., but the action is the same.

Friday, 11 November 2011

Evaluating Conditions with the If...Then...Else Statement - Learning Access VBA. Tutorial 2.

This the second post in the Learning Access VBA series.  Last week I introduced you to the VBA Development Environment where we we also learnt a little about variables.  We completed a simple exercise which referenced the values entered by the user into two text boxes, stored the values in variables, and added the values contained therein together. The result was then displayed in a third text box.  This week we are going to move on, and look at how VBA deals with Conditions using the If ... Then ... Else Statement.

So what do we mean by the term conditions or the Conditional?  Basically as our VBA code is executed (that is to say, as the code runs, line by line, processing each statement in turn) the program flow may well reach various points where it needs to branch according to whether a given condition has or has not been met.   For example, we may want our code to run a short block of nested code if the value of a specified variable is greater than a given number.  If the condition is met, the program flow runs the statement contained in the nested code below; whereas, if the condition is not met, the program flow may branch to the next statement after the nested code - the nested code is, in effect, skipped.

Lets take a look at how the If ... Then ... Else Statement is constructed:

IF conditon is met THEN
        nested code1
ELSE
        nested code2
END IF
 
The IF keyword tests whether the condition is true or not.  If it is true, the THEN keyword directs the program flow to the code contained in nested code1ELSE is optional. If we use it, ELSE directs the program flow to that contained in nested code2 if the condition after the IF keyword had not been met.  Had we not used the ELSE keyword (with nested code2), the program flow would have skipped nested code1 and gone straight to the END IF keyword.

So, for example, our code may read: 


IF varFirstNumber > varSecondNumber THEN
        MsgBox("First Number is Greater than Second Number")
ELSE
        MsgBox("First Number is not Greater than Second Number")
END IF


This tests whether the number contained in variable varFirstNumber is greater than that contained in varSecondNumber.  If the condition was met, the nested code below the IF keyword would run.  This displays a message box saying "First Number is Greater than Second Number".  If the condition was not met, the nested code below the ELSE statement would have run instead.  This would have displayed a message box saying "First Number is Not Greater than Second Number".  The END IF keyword closes the If ... Then ... Else Statement.


There is one other optional keyword which is well worth mentioning - that is ELSEIF.  When we only use IF/THEN and ELSE, we have just two possible branches in the program code.  However, suppose there are three or more possible conditions?  For example, suppose we want to test whether the variable varFirstNumber is (a) greater than, (b) less than, or (c) equal to, the second variable?  ELSEIF is a good way to do this.  Lets see how we would code this scenario:



IF varFirstNumber > varSecondNumber THEN
        msgbox("First Number is Greater than Second Number")
ELSEIF varFirstNumber < varSecondNumber THEN
        msgbox("First Number is Less than Second Number")
ELSE
        msgbox("First Number is Equal to Second Number")
END IF



As we can see, the ELSEIF keyword has allowed us to enter a second specified condition to test, in the event of the initial condition proving false.  


As such, when the program flow reaches the If ... Then ... Else Statement, the initial condition is tested by the first IF keyword; if this is true, the nested code in the line directly below is executed, and then the program flow branches to the END IF keyword at the bottom of the statement.  However, if the first condition had been false, the first block of nested code is skipped, and the program flow goes to the ELSEIF keyword and the second conditional expression is then tested; if this proves true, the second block of nested code is executed.  However, if the 2nd expression had also proven false, then the 2nd nested code would also be skipped, and the program flow would move down to the ELSE keyword.  There is no condition to test here, so the 3rd block of nested code is executed unconditionally.


(NB In the above scenario there were three possible outcomes.  However, it is also worth mentioning that by adding additional ELSEIF keywords into the statement, the number of possible conditions to test is potentially endless.)


Compare Numbers Exercise


Let's end this tutorial by applying what we have learnt above to the following exercise.    

We are going to create a form with two text boxes named txtFirstNumber and txtSecondNumber.  Then we will add a command button, ctlCompare, with some code attached to it's ON CLICK event.  We learnt how to do this in last weeks Learning Access VBA tutorial. However this time our code will compare the numbers entered into the two text boxes by the user at runtime, and display a message box informing the him/her whether the First Number is (a) Greater than, (b) Less than, or (c) Equal to, the Second.

This is a screenshot of our form in action:

Here the user has entered 10 in txtFirstNumber and 20 in txtSecondNumber.
After the user clicks the Compare command button, Access runs the VBA code
that compares the two numbers and displays the appropriate message.
Instructions

I won't go into great detail regarding instructions - we should have leant all the required skills from last weeks post on Learning Access VBA.  There is also more information about creating an unbound Access Form in the post about Creating an Access Form from Scratch.  I advise you to check out these posts if you need a reminder:
  1. Create a new unbound Access Form.
  2. Add two text boxes called txtFirstNumber and txtSecondNumber.
  3. Add a Command Button called ctlCompare.
  4. Select the Command Button and open the PROPERTIES SHEET.
  5. Click the Events tab of the PROPERTY SHEET.
  6. Click the three dots symbol  (...) on the right of the ON CLICK property cell.
  7. Open the VBA Editor by clicking CODE BUILDER from the CHOOSE BUILDER menu.
  8. Copy and paste the code listed below (excluding the first and last line which should have been added to the VBA editor automatically).
  9. Click the save Icon and close the VBA Editor.

Private Sub ctlQuestion_Click()

    Dim varFirstNumber As Integer
    Dim varSecondNumber As Integer
    
    varFirstNumber = Me!txtFirstNumber
    varSecondNumber = Me!txtSecondNumber
    
    If varFirstNumber > varSecondNumber Then
        MsgBox "First Number is Greater than Second Number"
    ElseIf varFirstNumber < varSecondNumber Then
        MsgBox "First Number is Less than Second Number"
    Else
        MsgBox "First Number is Equal to Second Number"
    End If
    
End Sub

To test out the code, open your form in FORM VIEW, enter a number in each of the text boxes, and click the COMPARE button.  NB for clarity there is no code to validate whether two numbers have been entered in the text boxes.  An error message will result if one or both of the text boxes is empty when the compare button is clicked.

The code begins by declaring two variables, and then assigns values to each one by referencing the two respective text boxes (see Learning Access VBA for more information about this process).  The If ... Then ... Else statement then tests whether the first number is greater than, less than or equal to, the second number.  A message box opens containing the result.

Friday, 4 November 2011

Learning Access VBA - A Beginners Guide

This post is all about getting started with the Access VBA programming language -  a first tutorial starting right at the beginning.  As you may already know, the Access Database comes with its own programming language called Visual Basic for Applications, or VBA.  It offers a powerful way to automate and generally control how an access database application operates.  However, whilst relatively easy to use, getting started can seem a little daunting to the newcomer.  As such, this tutorial is intended to help the beginner feel at home in the programming environment.

Figure 1: The Visual Basic Icon
The first problem faced by the beginner is finding out how to get into this thing called VBA.  This is because, technically, VBA is a separate software package which works alongside Access.  That said, we can only open the VBA Editor (see figure 2 below) from within the Access database itself.  The most direct way of doing this is by clicking the VISUAL BASIC icon (located in the MACRO's group of the DATABASE TOOLS ribbon - see figure one above).  You might like to download the Learning Visual Basic Sample Database, and give this a try.

Learning Visual Basic Sample Database

Figure 2: The VBA Editor
The sample database consists of an unbound form which acts as a simple calculator.  There are three Text Boxes and two Command Buttons.  The user enters two numbers in the top two text boxes and clicks the  equals (=) buton.  This runs a block of VBA code (called a sub procedure) which reads the numbers from the two text boxes, adds them together, and displays the result in the third text box.  The user can then click the Clear button (C). This runs a different block of code to clear all the existing numbers from the text boxes so new numbers can be entered and calculated.

Figure 4: The Simple Calculator form
described in the paragraph above.
So lets take a look at how this is created.  We shall do so in two stages: first we shall create the form, then open the VBA Editor and enter the two blocks of code.

Creating the Form
  1. Click the FORM DESIGN icon (located in the FORMS group of the CREATE ribbon).
  2. Drag and drop three text boxes on the the form (if you need help, you might like to check out my tutorial on how to Create an Access Form from Scratch).
  3. Next drag and drop the two Command Buttons onto the design grid.  Make sure the USE COMMAND WIZARDS icon is not highlighted when you do this - otherwise just click the CANCEL button if the Command Button Wizard opens.
  4. Click on the label for the top text box and change the text to read "First Number".
  5. Repeat step 4 for the second and third labels, entering "Second Number" and "Result" respectively.
  6. Then click on the top text button and change the text to "C". This will be the Clear Button.
  7. Do the same for the lower text button, changing the text to "=".  This will be the Equals Button.
  8. Next click the PROPERTY SHEET icon (located in the TOOLS group of the DESIGN ribbon).
  9. Set the NAME property (located at the top of the OTHER TAB of the PROPERTY SHEET) for the first text box to txtFirstNumber.  
  10. Repeat step 9, calling the lower two text boxes txtSecondNumber and txtResult, and calling the Command Buttons ctlClear and ctlAdd. 
NB It is important to enter the NAME properties correctly.  Although we could have called them anything we wanted, our VBA code refers to these form controls via the NAME property.  As such we need to ensure that the names used on our form are going to match those to be used in the code.  This is an important principle to grasp as we use VBA alongside Access.

Entering the VBA Code
  1. Next click on the ctlAdd (=) Command Button whilst our form is still open in DESIGN VIEW.
  2. Open the PROPERTIES SHEET (if it is not already open) and click the EVENTS tab ctlAdd.
  3. Click the cell for the ON CLICK property.  Then click the three dots button (...) at the right of the cell.  This opens the CHOOSE BUILDER dialog box.
  4. Select CODE BUILDER from the menu items, and click OK.  This opens the VBA Editor in the exact location where we are going to enter the code which is triggered when the user clicks the button at runtime (the ON CLICK Event).  It even writes the first and last line of code which makes this a self contained block of code (a Sub Procedure).
  5. Enter the code (listed below) between the Private Sub and End Sub lines:

Private Sub ctlAdd_Click()

    Dim varFirstNumber As Double
    Dim varSecondNumber As Double
    Dim varResult As Double
    
    If IsNull(Me!txtFirstNumber) = True Or IsNull(Me!txtSecondNumber) = True Then
        MsgBox "Please Enter Numbers in Both Textboxes", vbInformation, "Missing Number(s)"
        Exit Sub
    End If
    
    varFirstNumber = Me!txtFirstNumber
    varSecondNumber = Me!txtSecondNumber
    
    varResult = varFirstNumber + varSecondNumber
    
    Me!txtResult = varResult
        
End Sub

Now repeat all of the last stage from 1 to 5, but this time for the ctlClear Command Button.  Enter the following code in the same manner as above:

Private Sub ctlClear_Click()

    Me!txtFirstNumber = Null
    Me!txtSecondNumber = Null
    Me!txtResult = Null
    
End Sub

When this is complete, you may save your code by clicking the SAVE icon, and close the VBA Editor.  To test the code simply open the form in FORM VIEW, enter the numbers to calculate, and click the ctlAdd button.  The correct result should display in the third text box once this is done.

How the Code Works


Please don't worry about being able to create this code yourself at this stage in the learning process.  The main purpose of this tutorial was to get you familiar with the programming environment, rather than how to construct the code.  I intend to do this in future tutorials.  However, we will go through each line of the first sub procedure just to give you a general idea of how the programming code works.

The code is triggered by the Command Button's ON CLICK event.  The user clicks the command button, Access 'raises' the ON CLICK event, and the block of code runs one line at a time.

Private Sub ctlAdd_Click()
The first line, the SUB Statement, was created automatically by Access when we opened the VBA Editor from the property sheet.  It's purpose is to mark the beginning of this block of code or Sub Procedure.  The PRIVATE part of the statement relates to the concept of Scope.  We won't worry too much about what this means at this stage, except to say it is concerned with where the sub can be 'called' from.  Private means just that - it is private to this particular form module, and can not be called from a module elsewhere in the application.  The part which says ctlAdd_Click is the name of the sub procedure. 



Dim varFirstNumber As Double
Dim varSecondNumber As Double
Dim varResult As Double
The next three lines of code declare three separate variables.  The purpose of variables are to enable values to be temporarily stored, processed, and retrieved.  More about this soon. At this stage we are just going to note that we have used the DIM Statement to define three variables called varFirstNumber, varSecondNumber and varResult.  We have also included AS DOUBLE to tell Access that the variables are going to store values of the DOUBLE data type (so we can calculate decimal numbers). 



 If IsNull(Me!txtFirstNumber) = True Or IsNull(Me!txtSecondNumber) = True Then
     MsgBox "Please Enter Numbers in Both Textboxes", vbInformation, "Missing Number(s)"
     Exit Sub
 End If
This next section of code is an IF Statement, and  forms a self contained block of code within the sub procedure itself.  It was something I added to ensure the user enters a number in each of the two text boxes. If it detects that there is a Null value it runs the nested code to display a message box, and then exits the sub. 

varFirstNumber = Me!txtFirstNumber
varSecondNumber = Me!txtSecondNumber

In this section of code, we are assigning each of the two variables with a value.  The equals sign assigns the variable name on the left of the sign with the value on the right.  In this case the top line is setting the value of the varFirstNumber variable to the value entered by the user in the txtFirstNumber text box control.  Note that the text box is referenced by means of the code: Me!txtFirstNumber.  The Me! part of this is telling access that the text box belongs to the form to which this VBA module is attached ie frmCalculator; and txtFirstNumber is the name we gave to the top text box on the form we created earlier.

varResult = varFirstNumber + varSecondNumber
This line is assigning the variable varResult with a value.  In this case the value being assigned is the sum of the values stored in varFirstNumber and varSecondNumber.  Put another way, this line is performing the addition calculation and storing the result in the variable varResult.

Me!txtResult = varResult
This line is using the value stored in the varResult variable to set the value to be displayed in the lower text box of the form, txtResult.  It references the text box in the manner already described above.  This time, however, the text box reference is on the right hand side of the equals sign, and the variable on the left.  This is because the value of the variable is being retrieved, and the value of the text box is being set.


End Sub
This line was generated automatically just like the first in this block of code.  However, this time it is marking the end of the sub procedure.


Try this Yourself
If you are feeling adventurous, you might like to try creating more Command Buttons for the Calculator form.  Try creating a button to multiply or subtract the values stored in the top two text boxes.  You can follow the same procedure as before.  However, when you copy and paste the code, change the addition operator (+) in the appropriate line for multiply (*) and subtract (-) respectively.

Friday, 28 October 2011

Using Conditional Formatting on an Exam Results Report


Conditional Formatting may be applied to Text Box and Combo Box controls on Access Forms and Reports.   It allows us to format the data contained in a particular Text or Combo Box, according to value of that same data.  For example we can create Conditional Formatting for a text box to display a currency value, which when less than 0, will display that value in a red font, instead of the normal black.

Moreover, as well as formatting the Text or Combo Box according to the value contained in that same control, we can also format the Text or Combo box according to the value contained in another field of the same record.  So, for example, we can display a Text Box containing a CompanyName in bold font if the value of its AnnualTurnover field is greater than or equal to, say,  £100,000.  The great thing is, the data contained in the AnnualTurnover field does not necessarily have to be displayed on the form or report, as long as it is stored in the form or reports underlying Record Source.  In order to do this, we would enter an expression for the condition.

So let's take a look at how we go about applying Conditional Formatting using the example of a Student Exam Results Report.  The report applies conditional formatting based on the students exam result.  Students records with a result above 74 % are displayed in green, and those below 40 %, in red. The formatting for those in between is set to blue by default. To enhance clarity, I have also applied a sort on the Results field, thereby ordering the records from highest result to lowest. If you wish, you can download the complete solution by clicking the link below:

Student Exam Results

Our example database contains a table, tblExamResults, and report, rptExamResults- the table being the Record Source for the report.  Our table contains a list of student names and their corresponding exam results:

Figure 1: tblExamResults - The Record Source for our Report.
The rptExamResults report is based on this table.  I created it by clicking the REPORT icon (located in the REPORTS group of the CREATE ribbon) whilst tblExamResults was highlighted in the NAVIGATION PANE.  I then just needed to tidy it up in DESIGN VIEW, and add the Conditional Formatting (on each text box on the report) in addition to a SORT on the Results field.  This is the finished result:


Applying Conditional Formatting.

To begin with, lets look at how we apply Conditional Formatting to the Result field of rptExamResults.
  1. Open the report in DESIGN VIEW
  2. Right click the Result Text Box.
  3. Click CONDITIONAL FORMATTING from the drop down menu.
  4. When the CONDITIONAL FORMATTING dialog form opens, set the conditions as described in the next stage of the process below.
Figure 2: The Conditional Formatting dialog form.  It is possible to have a maximum of
three different conditions plus the default formatting.  
Setting Conditions based on FIELD VALUE IS

The next stage is to set the actual conditions for the Result field of the report (see Figure 2 above). NB: when FIELD VALUE IS is selected, it refers to the value contained in the text box being formatted:
  1. First set the DEFAULT FORMATTING section so it displays a BLUE FONT.  Do this by selecting BLUE from the FONT/FORE COLOR icon
  2. Next set CONDITION 1 so that a BOLD GREEN FONT displays when the  FIELD VALUE IS GREATER THAN 74.  If necessary click the drop down list to select FIELD VALUE IS, and then do the same in the next combo box along to select GREATER THAN.  Then just enter the value 74 in the third box along, and select the BOLD and RED icons.  You should see a preview in the box below when done.
  3. Click the ADD >> button to show CONDITION 2.
  4. Repeat the process of stage 2 so that a BOLD RED FONT displays when FIELD VALUE IS LESS THAN 40.
  5. Click OK to close.
Setting Conditions based on EXPRESSION IS

The next stage is to set the conditions for the StudentId field.  Since we are not basing the condition on the value contained in the StudentId field itself, we need to construct an expression which refers to the value contained in the Results field. As such, we shall be selecting EXPRESSION IS from the appropriate drop down lists, rather than FIELD VALUE IS.
  1. Again, set the DEFAULT FORMATTING section so it displays a BLUE FONT.  Do this by selecting BLUE from the FONT/FORE COLOR icon
  2. Next set CONDITION 1 so that a BOLD GREEN FONT displays when the EXPRESSION IS [Results]>=75.  Do this by clicking the drop down list to select EXPRESSION IS, and then enter the expression in the elongated box (see Figure 3 below).  Then select the BOLD and RED icons as before.  
  3. Click the ADD >> button to show CONDITION 2.
  4. Repeat the process of stage 2 so that a BOLD RED FONT displays when EXPRESSION IS [Results]<40
  5. Click OK to close.

Figure 3: Condition based on EXPRESSION IS.  Note the elongated box where the expression is entered.  This appears when EXPRESSION IS is selected from the drop down list.
Finish the report by repeating this last stage for the FirstName and Surname fields using the EXPRESSION IS selection when creating the conditions.



Friday, 21 October 2011

Creating an Update Query to Update a Salaries and Wages Database

Imagine you have responsibility for the management of a Company's Salaries and Wages database.  The company employs 200 colleagues, and it is your job to process the annual pay increase. The problem you face is that different groups of employees receive a different percentage rate increase; and each employee within a group may have a different salary level to start with.  So what is an efficient way to update each employee's salary without having to having to go through each record manually?  A good solution would be to use an Update Query.

Update Queries are a type of Action Query, similar in nature to Delete Queries, which I blogged about last month.  However, whereas a Delete Query will find a group of records matching a given criteria and delete them from the table, the Update Query will find a group of records matching a given criteria and update one or more fields in each of those records.  So for example, we can use an Update Query to update the salaries of all staff with the job title, Telesales Administrator.

The great thing about using an update query is that we can update a field to an absolute value or a value based on a calculation.  So in addition to being able to update all Telesales Administrator salaries to a fixed value such as, say, £15000, we could also update all existing Telesales Administrator salaries by, say, 5%.  So in a scenario where each Telesales Administrator is on a different salary level to begin with, it makes sense to do the latter to update each unique salary figure.

Please feel free to download the completed solution to the above scenario by clicking this link: Example Update Query.  I recommend you begin by opening tblEmployees and take a look at the 12 sample records and then see how the records for Telesales employees change after the qryUpdateTelesalesSalaries Update Query is run.  You can also follow the instructions below to find out how to create this Update Query yourself.

Figure 1: tblEmployees.  As you can see, there are 6  Telesales Employees.
Notice how their salaries change after the Update Query is run.  

Creating an Update Query

So how do we create this Update Query?  It is easy.  We do it in two stages.  First of all we create a standard Select Query which uses a criteria to find the group of employee records who have Telesales as their job title.  Then we convert the query to an Update Query, and in the process, we enter the calculation which updates the existing salary level for each Telesales Administrator.   Lets do this step by step:

Stage One - Testing the Criteria with a Select Query
  1. Click the QUERY DESIGN icon (located on the QUERIES group of the CREATE ribbon).
  2. Select the tlbEmployees table from the SHOW TABLES dialog form.
  3. Double click the JobTitle and Salary fields from the employees table, so they appear as columns on the Query Design Grid.
  4. Enter ="Telesales" in the criteria row of the JobTitle column.
  5. Click the RUN icon to run the query.
The results of the query are shown in the screenshot below:

Figure 2: Results of the Select Query
in stage one.
As you can see, the query has found all six records where the employee's job title is Telesales.  We could have included their names and employee id's in the results, but this is not necessary.  Once we convert it to an Update Query and click run, we won't actually see the query results until we open the underlying table and note the updated fields in each of the records matching the criteria.

Stage Two - Converting to an Update Query
  1. Re-open the Select Query in Design View.  If the Select Query is already open in DATASHEET VIEW, simply click the DESIGN VIEW icon from the VIEWS group of the HOME ribbon.  Otherwise you can highlight the Query name in the NAVIGATION PANE, right click the mouse, and then click the DESIGN VIEW icon from the drop down menu.
  2. Once the Query opens in DESIGN VIEW, click the UPDATE QUERY icon from the QUERY TYPE group of the DESIGN ribbon. The Icon is then highlighted in orange. 
    You will also notice that the SHOW and SORT rows of the QUERY DESIGN GRID have disappeared, being replaced by a new row called UPDATE.
  3. All we need to do now is enter the calculation into the UPDATE row of the Salary column.  The calculation to enter is:  [salary]*1.05

    Figure 3: This is what the Query Design Grid should
    now look like for the Update Query.
  4. Click the RUN icon from the RESULTS group of the DESIGN ribbon.  
  5. Click YES when you  receive a warning message saying "you are about to update 6 row(s)".
Once you have run the query and clicked YES to the warning message, you won't see the changes until you re-open (or refresh) the underlying tblEmployees table.  When you do so, you will see that the Salaries for Telesales staff have been updated by 5% as expected (see figure 4 below).

Figure 4: The Updated tblEmployees Table.