Alternative Buttons

<< Click to Display Table of Contents >>

Navigation:  Access Controls >

Alternative Buttons

Paul Millennas          

With Access, the rule for buttons seems to be "You can have any color button you want, as long as it's Battleship Grey." But you don't have to be that limited: You can expand your buttons' range in color, texture, and functionality, provided that you're prepared to bend the rules a little. Paul Millennas shows you how.

You won't get much credit for providing an elegant technical solution if the aesthetics of your application aren't just as elegant. An application with a boilerplate switchboard and great code underneath won't make as strong an impression on your users (or the people paying your bills) as one with a pretty interface and less code. Users become more demanding every year and fewer will accept a GUI that looks like it was built with a Microsoft Access wizard. Part of the problem is, of course, the Web–the most common user experience. Your users expect your application to show the same kind of responsiveness that they've seen on their favorite Web sites.

I can't cover everything that you need to create an interface that will make your users say, "Wow." But I can give you some tools to spice up one of the most common components of your user interface: your buttons. And this isn't all just "glitz and glamour." As you'll see, these techniques also enable you to provide your users with more guidance and feedback as they use your application–and that's always a good thing.

Rollover/hover buttons

Almost every Web site, it seems, has rollover buttons (also called hover buttons). The difference between a rollover button and a regular Access command button is how the button changes when you hover your mouse over it. After that, the buttons have more differences than shared features (for instance, some hover buttons can have focus and some can't; some change appearance when you click them and others don't). As you'll see, I'm not really going to be using buttons at all. While my "buttons" will fire Click events, they can't gain focus (that is, users can't tab to these buttons). I'll discuss the implications of this later in the article.

As my hover effect in the following samples, I'm going to be changing the color of my buttons. However, all of my screenshots are greyscale, so you'll have to use your imagination a little. You can see the full result in the sample database in the accompanying download.

Figure 1 shows what appears to be a simple pair of hover buttons: Continue and Exit Application (these appear in the splash screen popup). These are, in fact, not buttons but labels. In the picture the Continue "button" has the mouse over it, so it has a red caption and a black border. Exit Application doesn't have the mouse over it, so it's not activated and has the default settings of a black caption and grey border.

200507_pm1
Figure 1

To make this magic happen, I've used the MouseOver event. The MouseOver event should be used sparingly, because, unlike most events, this event fires continuously, at least while the mouse is over the label. This could make your application loop or cause the screen to flicker.

The MouseOver event of the Continue label calls my function SetHighlight passing three parameters:

• The name of the current form

• The name of the label

• The name of one of the display modes that I've defined for the labels

To ensure that this function isn't constantly executing (as the MouseOver event repeatedly fires), I put a handshake token in the label's Tag property (in this case, the word "Highlight") the first time that I run the routine. On subsequent executions, I check that Tag field and exit the routine if the Tag property is already set to "Highlight." This means, of course, that you can't use the Tag field on these labels for any other programming trick in your toolbox.

In the routine, I check the display mode passed as the pstr_Mode parameter and set the label's properties accordingly. For instance, when "RedBlack" is passed as the display mode parameter, I normally set the caption to Red and the border to Black. However, I check for a couple of conditions first:

• Is this a subform? (I'll return to this later in the article.)

• Is the label disabled? (I check the forecolor to determine the label's state.)

Here's the code for the routine:

Public Function SetHighlight( _

 pstr_FormName As String, _

 pstrControlName As String, _

 pstr_Mode As String) As Boolean

Dim frm As Form

Dim ctl As Control

Set frm = Screen.ActiveForm

'Check if subform

If frm.FormName <> pstr_FormName Then

  Set frm = frm(pstr_FormName).Form

End If

Set ctl = frm(pstrControlName)

With ctl

  If .Tag <> "Highlight" Then

    Select Case pstr_Mode

      Case "RedBlack"

        'Check if disabled

        If .ForeColor <> 12632256 Then

          .ForeColor = 255

          .BorderColor = 0

        End If

      'other display modes go here

    End Select

    .Tag = "Highlight"

  End If

End With

End Function

When the mouse moves out of the label's area, I need to restore the label's properties. I do this by catching the MouseOver event of the Detail section of the form (after all, if the mouse is over the detail section, the mouse can't be over the label). In the Detail section's MouseOver event, I call my ResetHighlight function passing the name of the form and the display mode used in the previous call. Since I may have many hover buttons on a form, this routine sweeps through all the controls looking for any label or image with its Tag set to my handshake token of "Highlight" (I'll be discussing images in the next section of the article). When I find a highlighted label or image, I restore it to the default settings:

Public Function ResetHighlight( _

 pstr_FormName As String, pstr_Mode As String)

Dim frm As Form

Dim ctl As Control

Dim int_i As Integer

Set frm = Screen.ActiveForm

If frm.FormName <> pstr_FormName Then

  Set frm = frm(pstr_FormName).Form

End If

For int_i = 0 To frm.Controls.Count - 1

  Set ctl = frm.Controls(int_i)

  With ctl

    If (.ControlType = acLabel Or _

        .ControlType = acImage) And _

       .Tag = "Highlight" Then

      Select Case pstr_Mode

         Case "RedBlack"

            If .ForeColor <> 12632256 Then

              .ForeColor = 0

              .BorderColor = 8421504

            End If

      'Other display modes go here

      End Select

      .Tag = ""

    End If

End With

Next int_i

End Function

With these simple changes, users get immediate feedback on where their mouse is and what button will be activated when they click on the screen. If you've ever searched the screen for your mouse (or wiggled it back and forth to find it), you know how valuable this feature can be.

Enhancing with images

Going beyond simple text buttons, you can add graphics to your buttons to provide your users with a visual clue as to what your button does. However, to make this work effectively, you need to have your images use the same styles as your hover buttons. If you don't, users won't see the buttons and the images as a single unit.

After my application's splash screen displays, I bring up my main menu. Figure 2 shows a section from the main menu with images and hover buttons underneath. The SetHighlight function handles this also by doing the following:

• Making the image border width thick

• Setting the caption of the label below the image to the color of the image border

• Displaying the label's Control Tip Text in another label at the bottom left of the form

200507_pm2
Figure 2

Here's the additional code for the SetHighlight function:

Case "Image"

  .BorderWidth = 6

   frm("lbl" & Right(pstrControlName, _

        Len(pstrControlName) - 3))_

      .ForeColor = .BorderColor

  frm!lblHelp.Caption = .ControlTipText

Once the mouse has moved off the label, the function ResetHighlight() is called to put everything back the way it was. Here's the additional code required for ResetHighlight to restore the images:

Case "Image"

  .BorderWidth = 2

  frm("lbl" & Right(.Name, _

    Len(ctl.Name) - 3)).ForeColor = 16777215

  frm!lblHelp.Caption = ""

"Just-In-Time" Help

More than simple visual feedback, you can provide your users with specific Help about the functions on their forms. The technique that I'll show you here provides Just-In-Time (JIT) Help. The goal of JIT Help is to provide the specific Help the users need, just when they need it: The Help for the button that their mouse is over (which shows that they're interested in the button) is delivered before they click the button (and do something they don't want to do).

By clicking on any image, the next form is called. Figure 3, for instance, shows the Administrator Menu for my application. This form has 10 green buttons with black captions. I've "greyed out" one button to demonstrate how a disabled button looks.

200507_pm3
Figure 3

On this form, when the mouse is over a button, the label becomes black with a white caption, and Help information about the button is displayed in a box to the right. My GetCaption routine retrieves the required Help text for the relevant control from a globally declared recordset, by finding the record for the current form name and hover button name:

Public Sub GetCaption(pfrmName As String, _

                              pstr_Code As String)

  Dim frm As Form

  Set frm = Forms(pfrmName)

  With gsnp_Captions

    frm!lblHelp.Caption = ""

    If .RecordCount > 0 Then

      .MoveFirst

      .Find "STR_CAP_CODE='" & pstr_Code & "'"

      If Not .EOF Then

         frm!lblHelp.Caption = !STR_CAP_TEXT

      End If

    End If

  End With

End Sub

Not surprisingly, the code to implement this "Just-In-Time" Help information goes in my SetHighlight function. Here's the additional code, which provides the required effect:

Case "GreenBlackCaption"

 If .ForeColor <> 12632256 Then

  .BackColor = 0

  .ForeColor = 16777215

  GetCaption(pstr_FormName, pstrControlName)

 End If

This effect is reversed, as usual, by the ResetHighlight function. Here's the additional code to handle this new feature:

Case "GreenBlackCaption"

   If .ForeColor <> 12632256 Then

     .ForeColor = 0

     .BackColor = 65280

     GetCaption(frm.FormName, "Default")

   End If

The "Default" keyword displays Help text for the form (rather than for any particular button). I use this when a button isn't highlighted.

For the sake of completeness, here's the code that I use to load all my Help text into the gsnp_Captions recordset during application initialization. As you can see, I've used ADO here because, once I've retrieved the data, I break the connection with the database since I don't need to stay connected anymore. This feature isn't available in DAO (though you get the same effect by using a snapshot query). But, other than that, there's no reason this code couldn't be rewritten to use DAO:

Public Function LoadCaptions()

Dim str_SQL As String

str_SQL = "Select STR_CAP_CODE,STR_CAP_TEXT " & _

          " From TBL_CAP_CAPTION"

With gsnp_Captions

.Open str_SQL, _

   Application.CurrentProject.AccessConnection, _

   adOpenForwardOnly,adLockReadOnly

  .ActiveConnection = Nothing

End With

End Function

I also include a form in my application that allows users to edit the table that the recordset is loaded from. This lets users customize the Help system to meet their needs.

Beyond the hover basics

There's more that could be done here. It might be useful, for instance, to make the button "tri-state" and introduce another effect in the label's OnClick event (for example, reverse video), which would persist through the process started when the user clicks the button. This would give users feedback that the process is in progress during a slow query and prevent them from clicking your button over and over again while they wait for their results. I've implemented this in the Administration Menu of the sample application by making the button background red.

I've also only touched a few of the label's properties. You can implement your own effects by simple manipulation of the label properties. I've even tried rotating the label and switching the text to vertical orientation as part of the feedback process.

As I said at the start of this article, my hover buttons aren't actually buttons, but labels and images that can't gain focus. This has both advantages and disadvantages.

An advantage is that my buttons' functions can't be activated by an accidental press of the Enter key, or by an auto-generated control linefeed (from an in-line barcode reader, for instance). My buttons need deliberate mouse-driven activation. However, if a user has entered text prior to my "button" being selected, focus won't be moved from the textbox to the button. As a result, the textbox's buffer won't have been read by Access when processing starts. To handle this, you need to move focus as part of your "button" code.

Also, you can't place my hover buttons too close together: They must have adequate space between them, to ensure that the reset function is activated before the mouse reaches another hover button.

Bitmap buttons

Hover buttons are just one tool that you can use to create a richer user experience. Another tool is bitmaps to create image buttons. A simple solution in Microsoft Access is to place a bitmap behind a transparent button. However, unlike the "up-or-depressed" appearance of a regular button, a bitmap image doesn't move, so it gives the users no visual feedback that they've successfully clicked the button.

There's a solution: a set of matched bitmaps that duplicate that "up-or-depressed" effect of a standard button. You could create your own matching images, but you can find many pairs of matching "up-and-depressed" button images from the many "Free Button" Web sites (once you've closed the many advertising popups). One of my favorites, for instance, has a wooden trim. Figure 4 shows a typical button.

200507_pm4
Figure 4

The solution requires stacking four controls on top of each other. From the bottom, they are:

• The depressed button picture

• The up button picture

• A label with a caption for the button

• A transparent command button

I put code in the button's MouseUp and MouseDown events to swap between the two button images. When the user selects the button, the "up" graphic is made invisible and, as a result, the "depressed" graphic at the bottom of the stack becomes visible. To further the effect, I move the label about 60 twips southeast. When the button is released, I reverse the process: I make the up button image visible, covering the depressed image, and move the picture back to where it started.

The MouseDown and MouseUp events of the command button call the same function. The routine is passed two parameters:

• The first parameter is set to True to display the up image (and False for the down image)

• The subform name if the button is placed in a subform

Function ButtonFlick(pbln As Boolean, _

              pstr_SubForm As String)

  Dim frm As Form

  Set frm = Screen.ActiveForm

  If Len(pstr_SubForm) > 0 Then

     Set frm = frm(pstr_SubForm).Form

  End Sub

  frm!imgButtonDown.Visible = pbln

  frm!imgButtonUp.Visible = Not pbln

  With frm!lblButton

      If pbln Then

        .Left = .Left + 50: .Top = .Top + 50

      Else

        .Left = .Left - 50: .Top = .Top - 50

      End If

  End With

End Function

The two images imgButtonDown and imgButtonUp are switched and the label is moved diagonally. Slick and simple.

Handling subforms

As you've noticed, all of my code takes special care of subforms. Subforms are the poor man's add-in component and an important tool for simplifying and standardizing the user interface. It's not surprising that they turn up in so many Access applications.

One use of subforms is to hold a bitmap that you intend to use in more than one place in your application. I use this for application identifiers to use in most of my forms and reports, standardizing the look and feel of my application without bloating the application with multiple instances of the same bitmap. This is the danger of my bitmap button: If I use bitmap buttons on multiple forms, I'll end up with an enormous .mdb file, just to hold all of the bitmaps. In addition, in many cases I want to use the same bitmap for several different buttons.

My Exit bitmap button (shown in Figure 4) is so useful that I'd like to use it on most of my forms. But I'd also like it to have some intelligence. On most forms I want the button to display the caption "Close" and perform a Close Form action, returning control to the calling form. But on my Main Menu, I want this button to bear the caption "Exit" and perform a validated Quit. The solution is to create a subform to hold my bitmap/label/button stack. I can then incorporate some intelligence into the button by adding code to the subform. The subform can interact with the form that it's been added to through the subform's Parent property.

For instance, I change the caption for my button by putting this code in the subform's Form Open event:

Private Sub Form_Open(Cancel As Integer)

  If Me.Parent.Name = "frm_MainMenu" Then

    Me!lblButton.Caption = "Exit"

  Else

    Me!lblButton.Caption = "Close"

  End If

End Sub

The change in functionality is provided by the following code called from the transparent button:

If Me.Parent.Name = "frm_MainMenu" Then

  Dim var_Rtn As Variant

var_Rtn = MsgBox("Are you sure that you wish " & _

   " to exit this application?", _

   vbInformation + vbYesNo, "Confirm Exit")

  If var_Rtn = vbYes Then Application.Quit

Else

  DoCmd.Close acForm, Me.Parent.FormName

End If

If you do use a subform to hold your bitmap button, there's nothing to stop you from adding other, more typical "Access-like" functions to the subform. Figure 5 shows one of my "subforms with a button" in action. This is the subform I use in the Header section of forms in my application. Just by adding this subform to those forms, I've accomplished the following:

• Standardized the look-and-feel of my application.

• Added two bitmaps and a label that make up my application logo to every form, but ensured that I only store the bitmap once.

• Standardized the display of my form's caption of the parent form.

• Added a hover button to perform the Close/Exit functions described earlier.

• Displayed the current time using the Now() function and requerying the textbox every minute through the subform's OnTimer event.

200507_pm5
Figure 5

As you can imagine, this gives my application a very professional appearance that meets the expectations of my Web-savvy users.

Your download file is called 507MILLENNAS.ZIP in the file SA2005-07down.zip

This can be purchased with all the other downloads on this page