Power Tip: Making forms easier to use and maintain

Reader Štefan Masič provides today's Power Tip.

In today’s post I would like to share with you some tips about form design that I’m using consistently in all applications I develop. Maybe you can get some benefit from my approach to form design.

As we all know, analysis and preparation of user needs are essential in building all elements of an application. This approach results in forms that  are easy to use, are uniform throughout the whole application, and are easy to maintain.

Here are some design needs which must be satisfied on every Access form:

  1. The name of a form must be easily visible.
  2. The name of a form must be an expression of its functionality.
  3. User must easily identify all mandatory (required) fields.
  4. All mandatory fields should be visible on the first page of a form.
  5. All fields with a default value, auto number functionality, or read only property should be designed with a different background color.

It is common to put the name of a form into the window’s title with the Forms!FormName.Caption property. I prefer to use the Form Header Section with the label called lblFormName. This label can be larger and is therefore much more visible.

1

Figure 1: Title of the presented form is larger and more visible than the window’s title.

In my applications all mandatory fields have bold labels with the additional foreground color, named mandatoryLabelColor. All mandatory fields are surrounded with the border color, called mandatoryBorderColor.

Form showing different colors of labels, borders, and backgrounds

Picture 2: Mandatory fields are visible on the first view.

All mandatory fields are positioned on the first page. All other non-mandatory fields are positioned after first page break. With this design, the user can immediately determine which fields are required. All other fields are below them and on a second page, which is immediately visible with moving of the scrollbar. I prefer designing a form with page breaks and using the scrollbar for easy moving between pages. From my experience, this works better than a Tab control on forms with many fields.

All fields with default values, fields with the auto number functionality or read-only fields should have a different background color. For example: I use a yellow background color, called specialBackgroundColor. Additionally, all those fields have the AutoTab property set to No.

As mentioned above I use three colors, mandatoryLabelColor, mandatoryBorderColor, specialBackgroundColor. For all those colors I always use company colors or ask the customer to pick  specific colors.

In my examples the following colors were used:

  • color of mandatory labels: Pantone 341 C (CMYK 100, 0, 70, 30 or RGB 0, 117, 84)
  • borders of mandatory fields: Pantone 166 C (CMYK 0, 60, 100, 0 or RGB 239, 124, 0)
  • background color of special fields: Pantone 109 C (CMYK 0, 10, 95, 0 or RGB 255, 222, 0)

If all the colors are determined during the analysis phase, and they’re not likely to change later,  I always use a solution that does not require any programming. Here are Access numbers for all three colors, which I put into the appropriate properties of the labels and fields:

  • mandatoryLabelColor = LabelName.ForeColor = 5534976 (for Pantone 341 C)
  • mandatoryBorderColor = FieldName.BorderColor = 31983 (for Pantone 166 C)
  • specialBackgroundColor = FieldName.BackColor = 57087 (for Pantone 109 C)

Using global variables for more flexibility

If I think the customer might prefer different colors at a later date, I use variables instead of constants. This solution requires creation of a new module, with declarations of three public constants and three public variables. See the example modGlobalConstantsAndVariables below.

Public constants can be used when colors are defined in an early phase and are not likely to be changed. Public variables can be used when it’s possible that the colors might be changed in the future.

Module modGlobalConstantsAndVariables looks like:

'------------------------------------------------------------
' Element: Module, 2010
' Description: Definition of global constants and variables.
' - mandatoryLabeleColor
' - mandatoryBorderColor
' - specialBacgroundColor
'' Purpose : Global constants, global variables.
'' Author: Stefan Masic, january 2010
' Information: con = Constant; R=Red; G=Green; B=Blue
' See also: -
' Changes:
' (dd.mm.yyyyy, Author) Text
'------------------------------------------------------------
'--------------------------------------------------------------------

' Public constants.
Public Const conMandatoryLabelColor = 5334976 ' R=0 G=117 B=84
Public Const conMandatoryBorderColor = 31983 ' R=239 G=124 B=0
Public Const conSpecialBackgroundColor = 57087 ' R=255 G=222 B=0
'--------------------------------------------------------------------
'--------------------------------------------------------------------

' Public variables.
Public lngMandatoryLabelColor As Long
Public lngMandatoryBorderColor As Long
Public lngSpecialBacgroundColor As Long

'--------------------------------------------------------------------
' End of module
'--------------------------------------------------------------------

The simplest programming solution requires creation of a new OnOpen event on each form, with the following code for every mandatory field and every special field:

Private Sub Form_Open(Cancel As Integer)

On Error GoTo Err_Form_Open

Me.MandatoryLabelName.ForeColor = conMandatoryLabelColor
Me.MandatoryFieldName.BorderColor = conMandatoryBorderColor
Me.SpecialFiledName.BackColor = conSpecialBackgroundColor

Exit_ Form_Open

Exit Sub

Err_Form_Open

MsgBox Err.Description
Resume Exit_Form_Open

End Sub ‘ Form_Open

Storing color values in a table

When I’m creating a general purpose application where colors are likely to change, I use a special table, called tabDefaultApplicationValues. In its simplest definition, this table includes only one row and many fields. This table includes three fields MandatoryLabelColor, MandatoryBorderColor, and SpecialBackgroundColor. I also need function getDefaultApplicationValue(ValueName).

With this method, the simple procedure Form_OnOpen needs a little rearranging:

Me.MandatoryLabelName.ForeColor = lngMandatoryLabelColor
Me.MandatoryFieldName.BorderColor = lngMandatoryBorderColor
Me.SpecialFiledName.BackColor = lngSpecialBackgroundColor

Public variables can be filled during the loading process of Main form or Switchboard with the  following code:

lngMandatoryLabelColor = getDefaultApplicationValue(“MandatoryLabelColor”)
lngMandatoryBorderColor = getDefaultApplicationValue(“MandatoryBorderColor”)
lngSpecialBackgroundColor= getDefaultApplicationValue(“SpecialBackgroundColor”)

Here is a simple function you can use to retrieve the color values from the table:

'------------------------------------------------------------
' Element: Function, 2010
' Purpose: Get value from table tabDefaultApplicatonValues.
'
' Input: Default value field name (String)
' Output: Value (String)
'
' See also: tabDefaultApplicationValues
' Avtor: Stefan Masic, january 2010
' Remark:
' Changes:
'------------------------------------------------------------

Public Function getDefaultApplicationValue(strDefaultValueName As String) As String

On Error GoTo Err_getDefaultApplicationValue

Dim dbs As DAO.Database
Dim rst As DAO.Recordset
Dim strDefaultValue As String
Dim strSqlStmt As String

' Check default value name.
If IsNull(strDefaultValueName) Then
  strDefaultValue = ""
Else
  ' Define current DB.
  Set dbs = DBEngine.Workspaces(0).Databases(0)
  strSqlStmt = "SELECT " & strDefaultValueName & " FROM tabDefaultApplicationValues"
  Set rst = dbs.OpenRecordset(strSqlStmt)
  ' Get value if record exists (rst.RecordCount>0).
  If rst.RecordCount > 0 Then
    ' Check NULL.
    If Not IsNull(rst.Fields(0).Value) Then
      strDefaultValue = rst.Fields(0).Value
    Else
      strDefaultValue = ""
    End If
  Else
    strDefaultValue = ""
  End If
  ' Free rst in dbs.
  rst.Close
  dbs.Close
End If ' IsNull

getDefaultApplicationValue = strDefaultValue

Exit_getDefaultApplicationValue:
Exit Function
Err_getDefaultApplicationValue:
MsgBox Err.Description & " (Error no.: " & Err.Number & ")"
Resume Exit_getDefaultApplicationValue

End Function ' getDefaultApplicationValue

If the solution requires personalization of default application values, or if the solution will be used by many companies, then this table will have two or more rows of default values for every configuration, and the function will have two parameters. Here are two examples:

getDefaultApplicationValue(UserId, ValueName).
getDefaultApplicationValue(CompanyId, ValueName).

The first described approach does not require any programming, and as such it is easier to manage the application over time and with new versions of Access. I recommend this approach.

Send your Power Tips to Chris & Mike at accpower@microsoft.com.

Office Blogs Comments

Comments: (5) Collapse

  • Does changing this make us any more or less vulnerable to the bot attacks which have become so prevalent these days? We are specifically not using a forum because it is impossible for us, as a small company, to control.

  • Some other suggestions for dealing with this flexibly: 1. define an Enum with meaningful color names instead of using constants. This means that you can define a parameter for a sub/function that sets colors and have it be limited to the values defined in the enum. You also get Intellisense for the parameters when typing the sub/function in code. 2. if using a lookup table, don't just look up the values each time the function is called, but store them somewhere. I like using self-initializing standalone class modules or functions that use static variables internally (which are initialized on the first call and then not looked up again on further calls). Other experienced developers will use a hidden form and just store the values in controls on that hidden form. 3. rather than write the repetitive code in each OnOpen event, use a sub/function that you pass a form variable and then operate on each form the same way, utilizing the form's Controls collection. You'll need to define the groups of controls that need each particular type of formatting, and the Tag property is very useful for this. As an alternative to the Tag property, you could also check the controls to see if the field that is the ControlSource is required, an Autonumber or has a default value, and choose the color based on that, but I think it's easier to just set the Tags. In addition to setting visual formatting, you may also incorporate enable/disable and showing/hiding particular controls at runtime. However, most of the time you're doing that, you're likely to do it more often than just when the form opens (e.g., in the OnCurrent event), in which case it becomes noticeably faster if you define custom collections in the form's OnLoad and then operate on those collections (those can be passed to a common sub/function for processing, just like the suggestion above for setting colors).

  • Dear David! Thank you for your adivice. All three steps and the addition are true and will be meaningfull for me and other developers. Originaly my first tought was to show how important is design phase when we "convert" database and process characteristics into form elements. Regarding step 2. When I use data from table for local definitions (personalization of single instance of application) then this is true. If I use data for global definitions (between many users), then I must read value from table. Thank you again. With best regards, Štefan Masič.

    M 0386 41 672 487

    E stefanmasicsp@gmail.com

  • Good design - I pretty much follow your form design but I am concerned with speed and accuracy on forms and also use hotkeys so data entry people won't have to use a mouse which takes the hands off keys and slows down input. Thanks!

  • Dear DataPlus developer (mr. bhicks11). Thank you for your comment. Above forms are only part of whole design "story". When I'm designing application (and DB) for OLTP needs, I always consult my users ("data entry users"). We designed together also their process (for example: it is better to manually order atomic input elements (input documents of any kind) before entry start) and then use special steps like "Copy last document into new one" or "Next document will be like last one" and so on. In such scenario users must insert only small numbers of data fields (1 or 2). I also always strictly follow Referential and Domain integrity rules and special rules like “key data must be inserted – NOT NULL. It is key thing for users also. In OLAP phase I really do not need cleaning and transformation step and also OLAP step can be done on-line. From the user perspective it is also better because they see only latest information (for example: last information in process always close all other data). Regarding hotkeys – I do not use them – but I will – soon. I promise. Thanks for advice. My users all get Access Reference Card with all integrated keys and knowledge of QBE steps. If you wish I can send you one for Access 2000 and one for Access 2003. After that they have good knowledge for elegant and fast data entry, retrieval and data mining tasks. After couple of weeks I'm always afraid when I see "my" users how fast (really fast) they are working with Access forms. Thank you for your Comment, Štefan Masič. P.S. If you have time you can look on one of my application at http://odvetnik.wordpress.com (it is designed for layer office, it is called MY layer office).

Comments

Comments: (loading) Collapse