Menu and Control Array Techniques


The ControlArray.frm sample demonstrates some basic techniques of control arrays using labels and menus (yes, menus are controls in VB). This sample loads a menu and label control array with a list of files for a selected directory. Every time the user switches directories, the control array is reset back to its default setting. shows a sample of what ControlArray.frm looks like after the cmdBuildArray button has been clicked for the selected directory.

Figure 32.1

ControlArray.frm demonstrates control array techniques using menus and labels

shows how to build a dynamic label and menu control array when the user clicks a button.

Listing 32.1 - CONTROLARRAY.FRM - The cmdBuildArray_Click event demonstrates how to load and display a control array


'*********************************************************************
' Builds the control array for the current path in the dir control.
'*********************************************************************
Private Sub cmdBuildArray_Click()
   Dim strFileName As String
   Dim intIndex As Integer
   Dim intNewHeight As Integer
   Static intOrigHeight As Integer
   '*****************************************************************
   ' Get the original height of the form -- once
   '*****************************************************************
   If intOrigHeight = 0 Then
       intOrigHeight = Height
   End If
   '*****************************************************************
   ' Get the first filename from the path in the dir control
   '*****************************************************************
   strFileName = Dir(dirPath.Path & _
       IIf(Right(dirPath.Path, 1) = "\", "", "\") & "*.*")
   '*****************************************************************
   ' Loop while dir is returning files
   '*****************************************************************
   Do While Len(strFileName)
       '*************************************************************
       ' If intIndex > 0, then create a new control under the prev
       '*************************************************************
       If intIndex > 0 Then
           Load lblControlArray(intIndex)
           With lblControlArray(intIndex)
               .Move .Left, lblControlArray(intIndex - 1).Top + _
                   lblControlArray(intIndex - 1).Height
               .Visible = True
           End With
           Load mnuArrayItems(intIndex)
           mnuArrayItems(intIndex).Visible = True
       End If
       '*************************************************************
       ' Stop at twenty and let the user know there were 20 files
       '*************************************************************
       If intIndex = 20 Then
           mnuArrayItems(intIndex).Caption = "More files..."
           With lblControlArray(intIndex)
               .Caption = "More files..."
               .Font.Bold = True
           End With
           Exit Do
       '*************************************************************
       ' Otherwise set the caption and find the next file
       '*************************************************************
       Else
           lblControlArray(intIndex) = strFileName
           mnuArrayItems(intIndex).Caption = strFileName
           strFileName = Dir
           If Len(strFileName) Then intIndex = intIndex + 1
       End If
   Loop
   '****************************************************************
   ' If the dir was empty, then notify the user in the first label
   ' and menu
   '****************************************************************
   If intIndex = 0 Then
       lblControlArray(0) = "No files found!"
       mnuArrayItems(0).Caption = "No files found!"
   '****************************************************************

   ' Otherwise, resize the form
   '****************************************************************
   Else
       '************************************************************
       ' Height of the title bar and border
       '************************************************************
       intNewHeight = (Height - ScaleHeight)
       intNewHeight = intNewHeight + lblControlArray(intIndex).Top
       intNewHeight = intNewHeight + lblControlArray(intIndex).Height
   End If
   '****************************************************************
   ' If the new calculated height > original height, then resize
   '****************************************************************
   If intNewHeight > intOrigHeight Then
       Height = intNewHeight
   '****************************************************************
   ' Otherwise resize to the default
   '****************************************************************
   Else
       Height = intOrigHeight
   End If
   '****************************************************************
   ' Disable the cmdButton until the user changes directories
   '****************************************************************
   cmdBuildArray.Enabled = False
End Sub

When using control arrays there are several important concepts to remember. They are:

The code in demonstrates step 2 (listed previously) about how to shrink the control array without accidentally trying unload the first element.

Listing 32.2 - CONTROLARRAY.FRM - dirPath_Change demonstrates how to unload a control array


'*********************************************************************
' Unload the control array every time the user changes the directory
'*********************************************************************
Private Sub dirPath_Change()
   Dim ctl As Control
   '****************************************************************
   ' Iterate through the controls collection unloading the labels
   ' and menus
   '****************************************************************
   For Each ctl In Controls
       If ctl.Name = "lblControlArray" Or ctl.Name = _
           "mnuArrayItems" Then
           '********************************************************
           ' If the index > 0 then unload it
           '********************************************************
           If ctl.Index Then Unload ctl
       End If
   Next ctl
   '****************************************************************
   ' Clear the first menu & label and re-enable the command button
   '****************************************************************
   mnuArrayItems(0).Caption = ""
   lblControlArray(0) = ""
   cmdBuildArray.Enabled = True
End Sub

The code in the mnuArrayItems_Clickevent below is a simplistic example of how using control arrays for menus can save you from having to write a lot of duplicate code. If you didn't have an array of menu items, the following code would have to appear in twenty separate click events, and you would have to display and hide each menu item based on the selection of dirPath.


'*********************************************************************
' Display the menu caption when a menu item is clicked
'*********************************************************************
Private Sub mnuArrayItems_Click(Index As Integer)
   MsgBox mnuArrayItems(Index).Caption, vbInformation
End Sub

Can you think of some real world examples of where menu control arrays might be a real code saver? How about a Most Recently Used (MRU) menu? Perhaps the most common application is in the Window menu of a MDI application. The following code was taken from MDIChild.frmin the next chapter:


'*********************************************************************
' If you set your indexes of your Window menu properly, you can save
' yourself some code. I was careful to make sure my Window menu items
' indices were equivalent to the possible values for the Arrange
' method.
'*********************************************************************
Private Sub mnuWindowItems_Click(Index As Integer)
   MDIParent.Arrange Index
End Sub

It is my humble opinion that you should always use control arrays for menus, since it is much more readable to have all of the code for a series of similar menu items (such as the Edit menu items) together.

Another more practical use for control arrays are with option buttons. I'm always amazed to see the complicated maze of code that people create to handle something as simple as option buttons. However, the average VB programmer is either unfamiliar (or uncomfortable) with control arrays, so they don't honestly see a better way to use option buttons. The code in demonstrates how easy it is to use control arrays with option buttons.

Listing 32.3 - OPTDEMO.FRM - Option Buttons were made for Control Arrays


'*********************************************************************
' OptDemo.frm - Demonstrates how to use a option button control array
'*********************************************************************
Option Explicit
'*********************************************************************
' Val(<parent frame>).Tag returns the index of the currently selected
' option button. We'll use this information to display the caption of
' the currently selected option button.
'*********************************************************************
Private Sub cmdCurButton_Click()
   Caption = optButtons(Val(fraParent.Tag)).Caption & " is selected!"
End Sub
'*********************************************************************
' Set the value of the initial option button (which fires the click
' event for that index)
'*********************************************************************
Private Sub Form_Load()
   optButtons(0).Value = True
End Sub
'*********************************************************************
' Store the index (or some other useful data) of the currently
' selected option button in the frame containing the option buttons
'*********************************************************************
Private Sub optButtons_Click(Index As Integer)
   optButtons(Index).Container.Tag = CStr(Index)
End Sub