Thursday, April 4, 2013

Using VBA to require certain fields be filled in on an unbound form in MS Access.



On some of the data entry forms that I use there are fields (text box controls) that are required and others that are optional. Since I normally use unbound forms I can easily identify blank required fields and remind the user to fill them in.

On this form has three controls that are for required fields:




If the user leaves the Collection Number, the Scope/Content Note, or the Languages blank I need the code to identify the blank required fields and prompt the user to filled them in. This is pretty easily done by using the function Nz() to convert the null values from the required fields to “DataError” and the optional ones to a zero-length string.

In the On_Click event of the Save button (cmdSave_Click), after dimensioning the variables I pick up the values from the controls and use “DataError” to isolate where the user needs to add data:

    strCollNum = Nz(Me.txtCollNum, "DataError")
    strTitle = Nz(Me.txtTitle, "")
    strScopeContent = Nz(Me.txtScopeContent, "DataError")
    strType = Nz(Me.txtType, "")
    strFormat = Nz(Me.txtFormat, "")
    strExtent = Nz(Me.txtExtent, "")
    strDate = Nz(Me.txtDate, "")
    strLocation = Nz(Me.txtLocation, "")
    strRestrictions = Nz(Me.txtRestrictions, "")
    strRelation = Nz(Me.txtRelation, "")
    strLanguage = Nz(Me.txtLanguage, " DataError ")
    strSubjects = Nz(Me.txtSubjects, "")
    strKeywords = Nz(Me.txtKeywords, "")
    strNotes = Nz(Me.txtNotes, "")

        'Put the three required fields together in one string, strError.
        strError = strCollNum & strDesc & strLanguages
        'Test the strError to see if it contains DataError.
        If strError Like "*DataError*" Then
        'If strError contains DataError test each required field.
            If strCollNum Like "DataError" Then
            'For a DataError make the back color for the text box red.
                Me.txtCollNum.BackColor = RGB(255, 0, 0)
            Else:
            'Otherwise the back color for the text box should be the original blue.
                Me.txtCollNum.BackColor = RGB(199, 208, 227)
            End If
            If strDesc Like "DataError" Then
            'For a DataError make the back color for the text box red.
                Me.txtDescription.BackColor = RGB(255, 0, 0)
            Else:
            'Otherwise the back color for the text box should be the original blue.
                Me.txtDescription.BackColor = RGB(199, 208, 227)
            End If
            If strLanguages Like "DataError" Then
            'For a DataError make the back color for the text box red.
                Me.txtLanguage.BackColor = RGB(255, 0, 0)
            Else:
            'Otherwise the back color for the text box should be the original blue.
               Me.txtLanguage.BackColor = RGB(199, 208, 227)
            End If
            'Prompt the user to fill in the missing date and exit the sub.
            MsgBox "Red fields are required. Please complete form and re-save.", vbCritical
            Exit Sub       
       Else:
            'The code to write the data to the tables would go here to execute when all three 
             'required fields contain data.
      End If

Here is what the form looked like when I tried to save it after entering a collection number without a Scope/Content Note or Language.



Since the sub was exited after the message box was presented the user will be returned to the form when the message box is closed: the record cannot be saved unless the required fields contain data. Whether or not they contain valid data is another question, but using a similar set of tests the data can be validated and rejected if not valid.

Thursday, September 13, 2012

Filling in folder numbers in an MS Excel container list.


Here at the Kheel Center we have quite a few legacy container lists in Excel spreadsheets that list the folder titles for each box, without giving the folder numbers.

A
B
Box
Folder Title
1
Folder 1
1
Folder 2
1
Folder 3
2
Folder 4
2
Folder 5
3
Folder 6
3
Folder 7


Before converting the container list to EAD I wanted to insert the folder numbers. I was able to do so using a fairly simple If-Then-Else statement. First insert a new column between the Box and Folder Title columns.

A
B
C
Box
Folder
Folder Title
1

Folder 1
1

Folder 2
1

Folder 3
2

Folder 4
2

Folder 5
3

Folder 6
3

Folder 7

In the first cell of the Folder column, cell B2, enter the following formula:

=IF(A2-A1=1,1,B1+1)

What this does is subtract A1 from B2, which will give you either a 1 or a 0. If the value is 1 then the current row is the first folder of the box. If the value is 0 then the current folder is the next folder in the box and should have a folder number that is one greater than the one above it. When you have the formula done double-click on the little handle at the lower right corner of the cell. This is the fill-down function and will run the formula down the entire column.

Note: this formula will give an error for all the folders in the first box: you are trying to subtract text (“Box”) from a number in the first cell.

A
B
C
Box
Folder
Folder Title
1
#VALUE!
Folder 1
1
#VALUE!
Folder 2
1
#VALUE!
Folder 3
2
1
Folder 4
2
2
Folder 5
3
1
Folder 6
3
2
Folder 7

To correct the error simply temporarily replace the “Box” in A1 with a zero.

A
B
C
0
Folder
Folder Title
1
1
Folder 1
1
2
Folder 2
1
3
Folder 3
2
1
Folder 4
2
2
Folder 5
3
1
Folder 6
3
2
Folder 7

Select column B and copy, then Paste Special=>Values right over it. This will convert the formulas in column B to the values shown. After that you can change cell A1 back to “Box”.

You now have a container list with both box and folder numbers.

Friday, June 1, 2012

Next and Previous Buttons for MS Access Bound Forms

Normally I prefer to use unbound controls on my forms since I feel it gives me more flexibility and control, however on a recent project I needed to use bound controls. This form has 13 subforms in a tab control for 13 different audio-visual media formats. I couldn’t allow the users to create new records using the navigation buttons on the subforms since that would create records in the table with no way of linking them back to the proper subform. I, instead, added a “New Item” button to each subform, which creates the new record and inserts the media format into it, and deleted the navigation buttons. This meant I needed Next and Previous buttons on the subforms.

Back on 07Ju11 I explained how to make Next and Previous buttons on an unbound form. It’s a little different on a bound form. I did, however, want some of the same behavior. I don’t like the Previous button to show if you are on the first record or the Next button to show if you are on the last record. This prevents the user from click a button when there is nowhere to go. The code below is how I control the display of the buttons. I ended up including Previous, Next, and Last buttons, a First button could also be included if you like. I also included a text box to let the user know that they are on “record 1 of 7”, or wherever they are.

For the sake of simplicity I used the exact same code for all three buttons:

     Private Sub cmdPrevious _Click()
     'Private Sub cmdNext _Click()
     'Private Sub cmdLast _Click()

          Dim intIndex As Integer
          Dim intCount As Integer

          'For the Previous button use:
          DoCmd.GoToRecord , , acPrevious
          'For the Next button use:
          'DoCmd.GoToRecord , , acNext
          'For the Last button use:
          'DoCmd.GoToRecord , , acLast

          'Get the count of records in the form's record set.
          intCount = Me.Recordset.RecordCount
          'Debug.Print " Count = " & intCount
          'Get the cursor position in the recordset.
          'Since the cursor position for the first record is zero
          'add 1 to the cursor position so that the last one will equal the record count.
          intIndex = Me.Recordset.AbsolutePosition + 1
          'Debug.Print "Postion = " & intIndex

          If intCount = intIndex Then
          'The last record is being displayed, only the Previous button should show.
          'In order to change the visible status of the Next and Last buttons, one which has the focus,
          'make sure the Previous button is visible and move the focus to it.
               Me.cmdPrevious.Visible = True
               Me.cmdPrevious.SetFocus
               Me.cmdNext.Visible = False
               Me.cmdLast.Visible = False
          ElseIf intIndex = 1 Then
          'The first record is being displayed, only the Previous button should show.
          'In order to change the visible status of the Previous button, which has the focus,
          'make sure the Next button is visible and move the focus to it.
               Me.cmdNext.Visible = True
               Me.cmdNext.SetFocus
               Me.cmdPrevious.Visible = False
               Me.cmdLast.Visible = True
          ElseIf intIndex > 1 And intIndex < intCount Then
          'The displayed record is not the first or last record, all buttons should show.
          'Since you are not changing the visible status of the button used 
          'the focus does not need to be reset.
               Me.cmdPrevious.Visible = True
               Me.cmdNext.Visible = True
               Me.cmdLast.Visible = True
          End If

         'Give the user an indication of where they are in the record set.
          Me.txtItemCount = "Item " & intIndex & " of " & intCount

     End Sub

Tuesday, May 15, 2012

Microsoft Access and Windows 7 Problem Solved


In my last post I noted that we were having a problem where the connection to the server was being lost, which would make Access stop working. Our IT people determined that a utility in Windows 7 was automatically refreshing the network connections and that was dropping the connections. After they turned off that utility the problem has not shown up again, so it was not the Access Based Enumeration Directory Structure Server that was causing the problem.