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:

  • You must create the first element of your control array at design time. This can be done quite easily by setting the Index property of your control to 0 (zero).
  • Although you can add and remove control arrays at runtime, you can't unload the first element of your control array at runtime. This means you should always check for the first index in the control in loops.
  • When new items in the control array are created (using Load) they take on the same properties (Caption, Top, Left, etc...) as their parent, with one exception. Their Visible property equals False. This gives you the opportunity to change the new control's properties and move it to a new location before displaying it.
  • Items can not be added to a menu control array from a parent menu's Click event. For example, in ControlArray.frm, you can't load new menu items from the mnuArray_Clickevent.
  • Each control in the control array is separate window as far as Windows is concerned, so don't go overboard. Too many controls on a form can have a significant impact on the performance of your application, so use good judgment. For example, ControlArray.frmis the perfect example of what not to do in a "real world" application. A list box would have been more efficient than a control array. However, the purpose of this example is simply to demonstrate control array techniques on an unknown set of variables.

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
Top Home