Ask the MultiValued Visual
Basic Expert - #13A
(as published in Spectrum
magazine Jan/Feb 1999)
To email your questions to "Ask the MultiValued VB
Expert", click here.
Copyright © 1996-98 Caduceus Consulting. All rights reserved.

Loading Custom Menus
I want to create a
menu at run-time in a VB application. The Load statement can
create one level of menu, but how can I create a multi-level menu
(i.e. menu entries with sub-menus)? -
Ajay Singh
Load Statement
A challenging question!
Let me first back up a bit to bring the less intrepid readers up
to speed. If you have spent much time with Visual Basic, you
probably know that the Load statement is usually used to load
forms (that you have created at design time) into memory, without
showing them right away (the Show method performs a Load
automatically, if required). You can then refer to controls on
that form or call Public procedures on that form from other VB
code, but the form will not become visible until you invoke
CustBrowse.Show or set CustBrowse.Visible = True. Have a look at
the following example:
Load CustBrowse
' Put in search
criteria...
CustBrowse.txtSearch.Text = "Caduceus"
' Call Public function
to search file...
NumFound% = CustBrowse.SearchDB("SUPPLIER")
If NumFound% > 1 Then
....' Make it visible and wait for a
selection...
....CustBrowse.Show vbModal
End If
CustKey$ = CustBrowse.txtItemId.Text
After loading
CustBrowse, two controls on the form (txtSearch and txtItemId)
can be referred to from my current module, and I can call
SearchDB, which is a Public function in the General section of
CustBrowse. The browse form will only be shown if more than one
entry is found.
Control Arrays
A more advanced use of
the Load statement relates to creating new elements of a control
array. A control array is simply an array of controls that are
all of the same type. They all have the same Name property, and
they are distinguished by an integral Index property (usually but
not necessarily starting at 0). For example, I might have three
phone number text boxes on a form that are referred to as
txtPhone(0), txtPhone(1) and txtPhone(2). If you look at an event
procedure for an element of a control array, you will notice that
Visual Basic passes in an extra argument (Index) to each
subroutine, so that you can tell which specific control the event
was triggered for. You do not need to have more than one control
in order to create a control array. By simply setting a control's
Index property to any integer value, you have created a control
array.
Loading New Controls
at Run-Time
We can now put the
preceding two concepts together. Imagine that I wish to create a
fourth phone number text box on my form at run-time. The
following code will do the trick:
Load txtPhone(3)
txtPhone(3).Top = 2100
Note that the new text
box will have the same properties (including Top and Left values)
as the first element, so I have to move it out from under
txtPhone(0) to a visible position.
The Menu Problem
Ajay's problem is that
he has created an array of menu controls in order to have a
customizable menu at run-time. Unfortunately, menus and sub-menus
have a nested parent-child relationship that is not controlled by
properties. In other words, all menu controls that are loaded at
run-time will be at the same level as the original element of the
menu control array. Because all members of a single menu control
array must be contiguous and at the same level (i.e. exist under
the same parent menu entry), it is only possible to add new menu
controls where originals already existed at design time. It is
possible to overcome this obstacle, within limits that you impose
when you first create the menu. Here's how:
Imagine that you want a
menu called Clients, which lists several clients. Under each
client, you want a sub-menu of cities where the clients have
major offices. All of this information will be read from your
MultiValue database at run-time. [I had to get the MultiValue
database in there somewhere!] It is possible to do this, but only
if you set a maximum number of clients that can appear. For
simplicity, let us assume that the maximum is three. In the Menu
editor at design time, create a tree of menu controls, named as
follows, where the number in parentheses is the Index property:
mnuClients
mnuClient(1)
mnuC1City(1)
mnuClient(2)
mnuC2City(1)
mnuClient(3)
mnuC3City(1)
Be sure to set the
Visible property of all of the mnuClient and mnuCnCity
controls to False. We can now use the following VB code (along
with my handy MVB library) to create a customized menu at run-time:
For i% = 1 To NumClients%
....mnuClient(i%).Caption =
mvbExtract(ClientListItem$, i%)
....mnuClient(i%).Visible = True
....CityList$ = mvbExtract(CityListItem$, i%)
....Select Case i%
....Case 1
........mnuC1City(1).Caption =
mvbExtract(CityList$, 1, 1)
........mnuC1City(1).Visible = True
........For j% = 2 to mvbDCount(CityList$, VM)
............Load mnuC1City(j%)
............mnuC1City(j%).Caption =
mvbExtract(CityList$, 1, j%)
............mnuC1City(j%).Visible = True
........Next j%
....Case 2
........mnuC2City(1).Caption =
mvbExtract(CityList$, 1, 1)
........mnuC2City(1).Visible = True
........For j% = 2 to mvbDCount(CityList$, VM)
............Load mnuC2City(j%)
............mnuC2City(j%).Caption =
mvbExtract(CityList$, 1, j%)
............mnuC2City(j%).Visible = True
........Next j%
....Case 3
........mnuC3City(1).Caption =
mvbExtract(CityList$, 1, 1)
........mnuC3City(1).Visible = True
........For j% = 2 to mvbDCount(CityList$, VM)
............Load mnuC3City(j%)
............mnuC3City(j%).Caption =
mvbExtract(CityList$, 1, j%)
............mnuC3City(j%).Visible = True
........Next j%
....End Select
Next i%
It's not the prettiest
VB code ever written, but if you have to use menu controls
(instead of something more flexible like combo boxes), the above
solution is one way to make it work.

To email your questions to "Ask the MultiValued VB
Expert", click here.
Copyright © 1996-98 Caduceus Consulting. All rights reserved.
Revised: November 10, 1998.
Return to Caduceus
Consulting Home Page