DataWright Information Services

Consulting and Resources for Excel and Access




Letting users add a new record to a combo box

Often your users will need to add new items to a combo box. How
you do that will depend on several factors.

  • If the items in the combo are made up of a value list, you
    will need to add new items to that list.
  • If the combo is based on a query or table, you will need to
    add a new record to that underlying table. That can be done
    behind the scenes, or by popping up a form for the users to fill
    out; the second option is best if the new record requires
    information in several fields (such as a new client).

Adding new items to a value list

In most cases you will use a table as the RowSource for a combo
box. That simplifies filtering and sorting the RowSource, for
example when you create cascading (dependent) combo boxes. However,
you don’t always know what data needs to be displayed. For example,
scheduling systems will often require several dates from the current
date (the next 4 Mondays or the next 14 days).

Using a list-filling callback function

To create a list of the next n Mondays, Access provides
a callback function called ListMondays. You can find it in the Help,
but the documentation isn’t all that good. The function is shown
below: it needs to be pasted into a standard module.

Function ListMondays(fld As Control, id As Variant, _
  row As Variant, col As Variant, code As Variant) _
  As Variant

  Dim intOffset As Integer
  Select Case code
    Case acLBInitialize ' Initialize.
      ListMondays = True
    Case acLBOpen ' Open.
      ListMondays = Timer ' Unique ID.
    Case acLBGetRowCount ' Get rows.
      ListMondays = 12
    Case acLBGetColumnCount ' Get columns.
      ListMondays = 1
    Case acLBGetColumnWidth ' Get column width.
      ListMondays = -1 ' Use default width.
    Case acLBGetValue ' Get the data.
      intOffset = Abs((9 - Weekday(Now)) Mod 7) - 28

    ListMondays = Format(Now() + _
      intOffset + 7 * (row - 1), "dd-mmm-yyyy")
  End Select
End Function

To adjust this function for your needs, there are only 3 rows
that you need to alter. They are:

Change the number of rows to display (in this case, 12)

    Case acLBGetRowCount ' Get rows.
      ListMondays = 12

Change the day to use, and the offset value that determines
the starting date

    Case acLBGetValue ' Get the data.
      intOffset = Abs((9 - Weekday(Now)) Mod 7) - 28

The expression in parentheses sets the day of the week. For
example, the 9 in the above expression ensures that you will get a
Monday as the first date in the sequence. The logic goes like this:

  • Weekdays go from Sunday (1) through Saturday (7). By adding
    7 to the weekday you make sure that subtracting Weekday(Now)
    from your target weekday, you will always have a positive
    number. In case the result is >=7, dividing by 7 and taking the
    remainder (the Mod 7 part) will give a value between 0 and 7.
  • Offsetting by -28 moves you back 4 weeks. You will need to
    adjust this value to get the correct first date.
  • For example, assume that today is Friday 2 November. The
    intOffset calculation is Abs((9 – 6) Mod 7) – 28, or -25. If I
    pick Monday 5 November instead (3 days later), the calculation
    becomes Abs((9 – 2) Mod 7) -28, or -28. The starting point is
    the same day for any day from Tuesday to the following Monday.

Build the list of dates

    ListMondays = Format(Now() + _
      intOffset + 7 * (row - 1), "dd-mmm-yyyy")

It’s not obvious from the construction of this function but this
is the loop that builds the list. Change the date format to suit
your requirements.

To use this function, the Row Source Type of the combo box needs
to be changed so that it reads ListMondays — the name of the
callback function. Leave the Row Source blank.

Using the AddItem method

In Access XP and later, you can use AddItem to build a value
list. Typically, you would create the list when the form loads, as
in the sample code below which populates a combo box with the months
of the year.

Private Sub Form_Load()
  Dim intMonth As Integer

  'set the rowsource type
  Me.cmbMonth.RowSourceType = "Value List"

  'clear the current list and set the column count to 1
  Me.cmbMonth.RowSource = vbNullString
  Me.cmbMonth.ColumnCount = 1

  'populate the list
  For intMonth = 1 To 12
    Me.cmbMonth.AddItem Format(DateSerial(Year(Now()), intMonth, 1), "mmmm")
  Next intMonth
End Sub

Building a string for the RowSource

If you are using an older version than Access XP, you can’t use
the AddItem method. In that case you need to build the list by
adding the delimiting semi-colons, and then setting the RowSource to
the new list. The equivalent code to the last example is shown
below:

Private Sub Form_Load()
  Dim intMonth As Integer
  Dim strItems As String

  'set the rowsource type
  Me.cmbMonth.RowSourceType = "Value List"

  'clear the current list and set the column count to 1
  Me.cmbMonth.RowSource = vbNullString
  Me.cmbMonth.ColumnCount = 1
  strItems = vbNullString

  'populate the list
  For intMonth = 1 To 12
    strItems = strItems & ";" & Format(DateSerial(Year(Now()), intMonth, 1), "mmmm")
  Next intMonth

  'remove the first semi-colon from the list
  strItems = Mid(strItems, 2)

  'reset the RowSource to the newly created string
  Me.cmbMonth.RowSource = strItems
End Sub

Some things to note about value lists:

  • The first line of code ensures that the correct rowsource
    type is used. Without this the code will fail, if for any reason
    the rowsource type has been changed to Table / Query or a custom
    list-filling function.
  • The second and third lines of code are housekeeping. They
    ensure that the list is empty before we start adding items, and
    that only 1 column is displayed.
  • There is a 2048 character limit in value lists for Access XP
    and earlier. This restricts the number of items that you can
    display; if you need more than this, you will need to use a
    table or query as your rowsource.
  • If you want to start the month list somewhere other than
    January (eg, for a financial year), you can easily modify the
    code by adding an offset value to intMonth:
    Me.cmbMonth.AddItem Format(DateSerial(Year(Now()), intMonth + 6, 1), "mmmm")

In the example above, the first month in the list is July and the
month names wrap around to June: suitable for the Australian
financial year.

Adding a record to a table

If you are only populating a single field, this routine will
create a new record in the table and refresh the combo box to
display the new entry. It has been written to be generic: you will
need to change the names of four items in the code to make it work
for your situation.

Note: The following settings are required for the code to work:

  • The Limit To List property of the combo box must be set to
    Yes
  • You need to set a
    reference
    to the Microsoft DAO 3.6 Object Library
Private Sub YourCombo_NotInList(NewData As String, Response As Integer)
  'LimitToList property must be set to Yes.
  'Requires a reference to the Microsoft DAO 3.6 Object Lirary

  On Error GoTo ErrorHandler

  'strings used for the MsgBox
  Dim strTitle As String
  Dim strMsg1 As String
  Dim strMsg2 As String
  Dim strMsg As String
  'buttons to display on the MsgBox
  Dim intMsgDialog As Integer
  'result returned from the MsgBox
  Dim intResult As Integer
  'object variables
  Dim cbx As Access.ComboBox
  Dim dbs As DAO.Database
  Dim rst As DAO.Recordset
  'field and table names
  Dim strTable As String
  Dim strEntry As String
  Dim strFieldName As String

  'The name of the lookup table -- edit to suit
  strTable = "YourTable"

  'The type of item to add to the table -- edit to suit
  strEntry = "Descriptive text"

  'The field in the lookup table in which the new entry is stored -- edit to suit
  strFieldName = "YourField"

  'The combo box that you are updating -- edit to suit
  Set cbx = Me![YourComboBox]

  'Display a message box asking whether the user wants to add a new entry.
  strTitle = strEntry & " is not in the list"
  intMsgDialog = vbYesNo + vbExclamation + vbDefaultButton1
  strMsg1 = "Do you want to add "
  strMsg2 = " as a new " & strEntry & " entry?"
  strMsg = strMsg1 + NewData + strMsg2
  intResult = MsgBox(strMsg, intMsgDialog, strTitle)

  If intResult = vbNo Then
    'Cancel adding the new entry to the lookup table.
    Response = acDataErrContinue
    cbx.Undo
    Exit Sub
  ElseIf intResult = vbYes Then
    'Add a new record to the lookup table.
    Set dbs = CurrentDb
    Set rst = dbs.OpenRecordset(strTable)
    rst.AddNew
    rst(strFieldName) = NewData
    rst.Update
    rst.Close

    'Continue without displaying default error message.
    Response = acDataErrAdded
  End If

ErrorHandlerExit:
  Exit Sub

ErrorHandler:
  MsgBox "Error No: " & Err.Number & "; Description: " & _
  Err.Description
  Resume ErrorHandlerExit

End Sub