Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
<strong>FoxTalk</strong><br />
Solutions for Microsoft® FoxPro® and Visual FoxPro® Developers<br />
Menus – A Data-<br />
Driven Approach<br />
Chad J. Lemmer<br />
Like many other Visual FoxPro developers, I long for the day<br />
when Microsoft updates the Menu Designer to be objectoriented.<br />
Some developers have gone to great lengths and<br />
have come up with excellent ways to make the current Menu<br />
Designer object-oriented. As an alternative to object-oriented<br />
menus, Chad Lemmer presents another approach—a tabledriven<br />
menu system.<br />
RECENTLY, I was working on an application where I<br />
needed a menu system that was easy to use<br />
and maintain, and that also had built-in security. If<br />
a user didn’t have access to a certain task in the system, I<br />
didn’t want the option to even show up on the menu.<br />
“Out of sight, out of mind.” Let’s face it, who likes<br />
looking at an interesting menu option, only to select it and<br />
receive a hostile message informing them that they’ve<br />
done something terribly wrong. If the user doesn’t belong<br />
there, then I don’t like giving them the option to go there.<br />
In all of my experiences using FoxPro, there’s one<br />
thing that I’ve learned—to make things table-driven<br />
wherever possible. It might take a little more work up<br />
front to get the ball rolling, but the extra time and effort is<br />
almost always gained back if changes need to be made<br />
later. My initial idea was to create a menu class that<br />
would use the DEFINE PAD, DEFINE POPUP, and<br />
DEFINE BAR commands to build a standard menu<br />
driven by a menu table and a user access table. After<br />
looking into the DEFINE commands a bit further, I<br />
concluded that the class code could get pretty hairy. In<br />
addition, the system was going to be used in a<br />
manufacturing environment with touch screens, and if<br />
you’ve ever used a touch screen, you know that selecting<br />
items from a standard Windows menu bar is a nuisance<br />
because the text is too small.<br />
After a few more brainstorming sessions, I decided<br />
to create a menu form that would be table-driven to<br />
This is an exclusive supplement for<br />
<strong>FoxTalk</strong> subscribers. For more<br />
information about <strong>FoxTalk</strong>, call us at<br />
1-800-788-1900 or visit our Web site<br />
at www.pinnaclepublishing.com/ft.<br />
Extended Article<br />
simplify the adding, modifying, and removing of<br />
menu items. The menu class excludes items by referring<br />
to a user security table that stores the menu items to<br />
which a user has access. The menu class also takes<br />
care of the touch screen problem because the menu<br />
form would use a ListBox to display the menu items<br />
with a font big enough for even Sampson’s<br />
index finger!<br />
How it works<br />
The clMenu class consists of a form with a ListBox to<br />
display menu options, along with an EditBox to display<br />
a more detailed description of the menu option. To<br />
execute a menu item, the user selects it from the ListBox<br />
and clicks on the “Run” button or presses the Enter key<br />
(see Figure 1).<br />
http://www.pinnaclepublishing.com/ft <strong>FoxTalk</strong> Extended Article: October 2000<br />
1<br />
6.0<br />
Figure 1. An example of a menu form created by the<br />
clMenu class.
The clSysMenu table<br />
The clSysMenu table is used by the clMenu class to<br />
populate the ListBox with menu items. Table 1 shows the<br />
structure of the clSysMenu.<br />
Each record in the clSysMenu table defines either<br />
another menu to display or a menu item to add to the<br />
ListBox. To instantiate another menu, you’d populate the<br />
cRunMenu field with a value from the cModuleID field that<br />
represents the menu you want to show. This setup provides<br />
an unlimited number of submenus. Each menu in the table<br />
must have a header record that specifies various menu<br />
properties such as the caption, icon, and color of the menu<br />
name. To create a menu header record, set the cMenuID<br />
field equal to the cModuleID field. When the clMenu class<br />
instantiates, it SEEKs the menu header record and sets<br />
the menu various form properties accordingly.<br />
To execute a FoxPro program, application, or form,<br />
you’d populate the cPath and cRunFile fields with the path<br />
and filename to execute, respectively. The cMenuID field<br />
stores a unique key to each menu item. The unique keys are<br />
used in conjunction with a clUser table’s mAllowAcc field<br />
that defines the menu items to which a user has access.<br />
Table 1. The clSysMenu structure.<br />
Field Description<br />
cModuldID Used to group menu items together. All items that<br />
belong to a menu have the same value in this field.<br />
cMenuID A unique menu item ID used for security and to<br />
uniquely identify each menu item.<br />
nOrder Specifies the order in which the menu items<br />
should appear.<br />
cMenuName The name of the menu or menu item to display in<br />
the ListBox.<br />
cRunMenu If specified, displays the specified submenu.<br />
cIconFile The file name for the icon to display on the menu<br />
form’s caption bar. Used in header records only.<br />
cPath Specifies the path for the file to execute in the<br />
cRunFile field or the icon specified in the<br />
cIconFile field.<br />
cRunFile If specified, the file in this field will be executed<br />
when the menu item is selected.<br />
cParms Additional parameters to pass to the file specified<br />
in the cRunFile field.<br />
mComment A longer description of the menu item to be<br />
displayed on the menu form class.<br />
nRed Passed to the RGB() function to allow different<br />
menu captions to have different colors. Used in<br />
header records only.<br />
nGreen Passed to the RGB() function to allow different<br />
menu captions to have different colors. Used in<br />
header records only.<br />
nBlue Passed to the RGB() function to allow different<br />
menu captions to have different colors. Used in<br />
header records only<br />
The clUser table<br />
The clUser table contains one record per user and has a<br />
memo field that contains a list of values from the<br />
clSysMenu.cMenuID field to which the user has access.<br />
Table 2 shows the structure for the clUser.<br />
Table 2. The clUser structure.<br />
Field Description<br />
cLoginName The name of the current user that’s logged into<br />
the application.<br />
mAllowAcc A list of values from the clSysMenu.cMenuID field<br />
that the user has access to.<br />
lAutoClose If set to .T., the previous menu will close<br />
automatically when a new menu is created,<br />
except for the main menu.<br />
When the clMenu class spins through the clSysMenu<br />
table to add items to the ListBox, it only adds the menu<br />
item if the item’s cMenuID is present in the clUser<br />
security table record. Another field, lAutoClose, tells the<br />
clMenu class to close the previous menu after running a<br />
submenu, unless they’re calling a submenu from the main<br />
menu. The main menu will always stay open.<br />
The clMenu class<br />
The clMenu class spins through the records in the<br />
clSysMenu table to populate the ListBox with menu<br />
options that the user has access to. The main menu for<br />
your application can be created by using the NewObject()<br />
function, passing it “MAIN” as the module ID and the<br />
user name of the user logged into the application. Once<br />
the main menu is instantiated, the clMenu class takes over<br />
from there. You probably want to add the line<br />
SYSMENU=OFF in your CONFIG.FPW file to remove the<br />
Visual FoxPro system menu from your application. The<br />
following is an example of how to instantiate the main<br />
menu for your application:<br />
public oMainMenu<br />
oMainMenu = NewObject("clMenu", "clMenu", "", ;<br />
"MAIN", lcLoginName)<br />
if type("oMainMenu") = "O" and ;<br />
!isnull(oMainMenu)<br />
oMainMenu.Show<br />
read events<br />
else<br />
wait window "Unable to create main menu."<br />
endif<br />
The ListBox on the clMenu form is populated by a<br />
temporary cursor called curMenu. This increases the<br />
menu’s overall performance because the ListBox only<br />
contains the values it needs—as opposed to using a<br />
filtering technique on the clSysMenu table to eliminate the<br />
menu items that you don’t want the user to see. The<br />
clMenu class accepts the following parameters as shown<br />
in Table 3.<br />
2 <strong>FoxTalk</strong> Extended Article: October 2000<br />
http://www.pinnaclepublishing.com/ft
Table 3. The clMenu class parameters.<br />
Parameter Description<br />
tcModuleID Specifies which menu items from the clSysMenu<br />
table to retrieve. Records from the clSysMenu table<br />
who’s cModuleID field value matches this<br />
parameter will be included in the ListBox, if the<br />
user has access to it.<br />
tcLoginName The name of the current user logged into<br />
the application.<br />
In order to create multiple instances of the clMenu<br />
class for each submenu that’s produced, I devised a<br />
method called GetNewMenuObject that returns the next<br />
available menu object name. This method spins through<br />
all of the objects in memory whose Name property starts<br />
with “oMenu” and appends a sequential number on the<br />
end of it until it comes up with an object name that isn’t<br />
being used. Notice the code for the GetNewMenuObject<br />
method in the following example:<br />
lparameters tnMenu<br />
local lnI, lcMenu<br />
lnI = 1<br />
lcMenu = "oMenu" + allt(str(lnI))<br />
do while .t.<br />
if type(lcMenu) "O" or isnull(&lcMenu)<br />
exit<br />
else<br />
* If the menu already exists, then show it<br />
* and don't make another copy of it<br />
if &lcMenu..cModuleID = curMenu.cRunMenu<br />
&lcMenu..show<br />
return<br />
endif<br />
lnI = lnI + 1<br />
lcMenu = "oMenu" + allt(str(lnI))<br />
endif<br />
enddo<br />
tnMenu = lnI<br />
return lcMenu<br />
In addition to finding a unique object name for each<br />
menu that’s created, this code also accepts a parameter<br />
(passed by reference) that tells the calling method what<br />
menu number was returned. The calling method uses the<br />
number to control where the new menu appears on the<br />
screen. By taking the menu number, multiplying it by a<br />
constant, and setting the new form’s Left and Top<br />
properties to that value, it gives the menus a cascading<br />
effect. Otherwise, the submenus would appear directly<br />
over the top of the previous menu (see Figure 2). Also, if<br />
the submenu selected by the user already exists as an<br />
object in memory, this method will show the existing<br />
menu instead of instantiating another one. It does this by<br />
comparing the new menu object’s cModuleID property<br />
with the value in the cRunMenu field, as you can see in<br />
the preceding code.<br />
Conclusion<br />
Because the menu class is table-driven, it’s very easy to<br />
make changes to an application’s menu. Most importantly,<br />
it eliminates the need to modify source code and install<br />
system updates when we need to change the menu. Once<br />
the menu option is added and the user is assigned access<br />
to the menu option, it appears in the menu automatically.<br />
The main disadvantage to this approach is that the menu<br />
isn’t a standard Windows menu that most other<br />
applications use. It comes down to the fact that the<br />
“standard way” might not always be the “practical way”<br />
to solve a problem.<br />
A complete working demo of the clMenu class is<br />
included in this month’s Source Code files. Questions,<br />
comments, and new ideas are always welcome! ▲<br />
10LEMMSC.ZIP at www.pinnaclepublishing.com/ft<br />
Chad J. Lemmer is a full-time I.S. programmer/analyst at Wausau Coated<br />
Products in Wausau, WI, specializing in Visual FoxPro application<br />
development. He also develops Visual FoxPro applications for the clients<br />
of his own consulting business. clemmer@dwave.net.<br />
Figure 2. The clMenu class cascades submenus.<br />
http://www.pinnaclepublishing.com/ft <strong>FoxTalk</strong> Extended Article: October 2000<br />
3