Ultrashock Tutorials > Flash MX 2004 > Flash MX 2004 UI Components  
 
by Aral Balkan,  BitsAndPixels.co.uk 
Download Source Files (5MB!)  
 
Flash MX 2004 UI Components
 

 01. v1 Components, We Hardly Knew Ye 
 02. Button Component 
 03. Alert Component 
 04. Checkbox Component 
 05. Radio Buttons 
 06. The List Box family of Components 
 07. Date Chooser Component 
 08. Date Field Component 
 09. Label Component 

 10. Loader / Progress Bar Components 
 11. Numeric Stepper Component 
 12. Text Area/Input Components 
 13. Menu / Menu Bar Components 
 14. Scroll Pane Component 
 15. Window Component 
 16. Tree Component 
 17. Accordion Component 
 18. Conclusion 

Aral Balkan is co-author of
Flash MX Components Most Wanted.
Click to check it out at Amazon.com!
- discuss this tutorial -

13. Menu and Menu Bar Components

Using the Menu and Menu Bar components, you can easily create a main menu that could be mistaken for one in a desktop application. Your menu items can contain icons and you can set up listeners to react to user selections. In addition, your menu items can perform like check boxes (to toggle a state) and like radio buttons (to select one of a number of options.) You can use the Menu and the Menu Bar independently of each other. In other words, you can have a Menu Bar without menus (also known as a Toolbar) and you can have a Menu without a Menu Bar (for example, a menu that shows when the user clicks a button.)

Menu Example 1: A Simple Menu Bar

The simplest way to get up and running with the Menu Bar is to populate it through ActionScript. The following example shows you how.

  1. Create a new FLA. Use the screenshot as a guide when laying out your movie in the following steps.



  2. Create a new Layer and call it Actions. Name the lower layer Components.

  3. Drag a MenuBar component from your Components panel on to the Stage, into Frame 1 of the Components layer.

  4. Using the Property Inspector, give the MenuBar instance the instance name myMenuBar, as show below. Notice how the component only has a single parameter, labels. For all intents and purposes, you can ignore this parameter. You will need to use this component through code.



  5. In this excercise, you will be creating a simple Help menu on the Menu Bar with two menu items, About and Ultrashock. In a real-world application, the first might pop up an info box and the second a browser window with the Ultrashock home page. These are easy to achieve using the Alert Component and getURL. For the purposes of this tutorial, however, they will just be tracing information into the Output window.

    In Frame 1 of the Actions layer, enter the following code to create the Help menu:
    ////////////////////////////////////////////////////////////////
    // Help Menu
    ////////////////////////////////////////////////////////////////
               
    var helpMenu = myMenuBar.addMenu("Help");
    helpMenu.addMenuItem({label:"About", instanceName:"aboutMenuItem"});
    helpMenu.addMenuItem({label:"Ultrashock", instanceName:"aboutUltrashock"}); 
    
  6. Test the movie (Control -> Test Movie; Ctrl-Enter). At this point you should see the Menu Bar with the Help menu. Clicking on Help will open up the menu, with the two menu items you just created. Clicking on the items themselves will close the menu. To make the menu do something when an item is selected, you have to set up a listener.

  7. Add the following code to the script in Frame 1 of the Actions layer:
    ////////////////////////////////////////////////////////////////
    // Help Menu Listener
    ////////////////////////////////////////////////////////////////
               
    var helpMenuListener = new Object();
               
    helpMenuListener.change = function(eventObject){
        var theMenu = eventObject.menu;
        var theMenuItem = eventObject.menuItem;
        switch (theMenuItem)
        { 
            case theMenu.aboutMenuItem:
                // about selected
                trace ("Help Menu: About selected");
            break;
               
            case theMenu.aboutUltrashock:
                // ultrashock selected
                trace ("Help Menu: Ultrashock selected");
            break;
               
            default:
                // error: unknown menu item
                trace ("Error: Unknown menu item selected");
            break;
        }
    }
               
    helpMenu.addEventListener("change",helpMenuListener); 
  8. Test the movie (Control -> Test Movie; Ctrl-Enter) and select and option from the menu to see a message confirming your selection in the Output window. Notice how you check to see which menu was selected: The eventObject contains a property called .menu that points to the Menu object that the event occured on (this is similar to the .target property in some of the other components.) It also has a property called .menuItem that points at the specific menu item that fired the change event.

    In the switch statement, you compare the menu item that fired the event (theMenuItem) with each of the items in the menu (these are actual references to the menu item instances themselves) and trace out the respective message. The default clause is in there because it is good practice to cover your posterior when programming. In this case, it should never get called but it may save you some time in debugging if you add a menu item to the menu in the future and forget to add a check for it in the
    switch statement. It's nice to have your application break in predictable ways as you develop, whether through unit tests or well thought-out error checking or a combination of both.

    If you've never seen a switch statement before, it is nothing more than a different way of stating an if..else if..else if..else statement. You could have written the same statement as:
    if(theMenuItem == theMenu.aboutMenuItem)
    {
        // about selected 
        trace ("Help Menu: About selected"); 
    } 
    else if ( theMenuItem == theMenu.aboutUltrashock )
    {
        // ultrashock selected
        trace ("Help Menu: Ultrashock selected");
    } 
    else 
    {
        // error: unknown menu item
        trace ("Error: Unknown menu item selected");
    } 
    Note that my indentation of the switch statement is non-standard. I add an additional level of indentation between the case statement and the break, essentially making the case and the break take the place of curly braces. There are many who do not like the switch statement and argue that it is a Bad Thing (tm). Personally, I believe that if it is used right, it is better (more legible, at least) to use a switch statement in place of a very long string of if..else..if statements. I am sure that there are others who will argue the opposite.

The above example showed you the simplest way to populate a Menu Bar and barely scratched the surface of what you can do with the MenuBar and Menu components. In the next example, you will add a new menu to the Menu Bar and, in the process, learn about two new types of menu items, the check box and separator.

Menu Example 2: The Check Box and Separator Menu Items

  1. Continue with the FLA from the previous example. If you don't have it, open up menu_bar.fla from your downloads and follow along.

  2. Add the following code to the top of the code in Frame 1 of the Actions layer:
    ////////////////////////////////////////////////////////////////
    // Options Menu
    //////////////////////////////////////////////////////////////// 
    
    var optionsMenu = myMenuBar.addMenu ( "Options" ); optionsMenu.addMenuItem({label: "Enable Item 3", type: "check",
    instanceName: "enableItem3", selected: false}); optionsMenu.addMenuItem({type: "separator" } ); optionsMenu.addMenuItem({label: "Item 3", instanceName: "item3",
    enabled: false});
    Notice that the first menu item has a type property that is set to check. You are telling the Menu to treat this item as a check box. Visually, it will display a check mark when toggled on. The selected property decides whether or not it appears checked by default. Here, you are setting it to false, so it will appear in the menu without a check mark. In the previous example, you did not have to specify a type property since a menu item without a specified type defaults to normal. Both of the menu items in the Help menu are normal menu items.

    The second menu item definition is also interesting. Here, you are creating a separator. The default appearance for a separator is as a horizontal rule within a menu. A separator does not need any other properties to describe it.

    Finally, you create the third menu item and disable it by setting the enabled property to false.

  3. Enter the following code underneith the batch you just typed in:
    ////////////////////////////////////////////////////////////////
    // Options Menu Listener
    ////////////////////////////////////////////////////////////////
               
    var optionsMenuListener = new Object();
               
    optionsMenuListener.change = function(eventObject){
               
        var theMenu = eventObject.menu;
        var theMenuItem = eventObject.menuItem;
               
        switch(theMenuItem)
        { 
            case theMenu.enableItem3:
                // check type menu item toggled
                var checked = theMenuItem.attributes.selected;
               
                // if item is checked, enable item 3, if not disable it
                theMenu.setMenuItemEnabled ( theMenu.item3, checked );
            break;
               
            case theMenu.item3:
                // ultrashock selected
                trace ("Options Menu: Item 3 selected.");
            break;
               
            default:
                // error: unknown menu item
                trace ("Options Menu Error: Unknown menu item selected");
            break;
        }
    }
          
    optionsMenu.addEventListener("change",optionsMenuListener);
    Here, you are setting up the familiar change listener. The difference is that if the user clicks on the Enable Item 3 menu item, you now have to check to see if it is selected (checked) or not and enable or disable Item 3 in the Menu accordingly. You do this by checking the selected attribute of the menu item. Menu items are nothing more than standard XML objects and thus have their attributes stored in their attributes objects. To enable or disable Item 3, you use the setMenuItemEnabled() method. Since the checked variable will contain either a true or false (boolean) value, you can pass it directly as the second argument (which requires a true for enable and false for disable.) There isn't anything new in the rest of the code.

  4. Test the movie. Try to click on Item 3 in the Options menu. Notice that you can't and that it is disabled. Now click the Enable Item 3 menu item in the Options menu. Return to the Options menu and click Item 3. Notice that it is now enabled and that the change event handler on your optionsMenuListener gets called when you click the menu item.

In this exercise you saw how to create a check box menu item and a separator menu item. In the next exercise, you will see the final menu item type, radio button.

Menu Example 3: The Radio Box Menu Items

  1. Continue with the FLA from the previous example. If you don't have it, open up menu_bar2.fla from your downloads and follow along.

  2. Modify the top of the script in Frame 1 of the Actions layer to add the code shown in boldface, below. You are adding a separator and two radio button menu items to the Options menu.
    var optionsMenu = myMenuBar.addMenu("Options");
    optionsMenu.addMenuItem({label: "Enable Item 3", type: "check", 
    instanceName: "enableItem3", selected: false}); optionsMenu.addMenuItem({type: "separator"}); optionsMenu.addMenuItem({label: "Item 3", instanceName: "item3",
    enabled: false}); optionsMenu.addMenuItem({type: "separator"}); optionsMenu.addMenuItem({label: "Either choose me", type: "radio",
    groupName: "myGroup", instanceName: "item5", selected: "true"}); optionsMenu.addMenuItem({label: "Or me (but not both)", type: "radio",
    groupName: "myGroup", instanceName: "item6"});
    Notice how the radio button menu item has an additional property, groupName. Just like regular Radio Button components, you need to group radio button menu items. Only one of the items in a given radio button group can be selected at a time. It is expected behavior that one of the radio button menu items in a group is selected by default. In this case, you are setting the Either choose me item as selected.

  3. Modify the switch statement in the optionsMenu change() event handler method (in Frame 1 of the Actions layer) by adding the following two case clauses:
    case theMenu.item5:
        // first radio button
        trace ("Options Menu: First radio button selected.");
    break; 
    case theMenu.item6:
        // second radio button
        trace ("Options Menu: Second radio button selected.");
    break; 
  4. Test the movie. Click on the Either choose me menu item and the Or me (but not both) menu item and see how the selection (the bullet) moves between them.

You have now created and used all of the various menu item types that come with the Menu component. In the next example, you will learn how to create a menu using an external XML file. Although this is normally a longwinded process involving either the use of multiple menu files (one for each menu in a MenuBar) or that of an incorrectly formed XML file. I will be providing you with two small helper classes to make things much easier.

Menu Example 4: The Radio Box Menu Items

Up to this point, you have been creating the menus for the Menu Bar using ActionScript. Although this is possible, and arguably easy, it is messy. This messiness manifests itself all the more when you have long, complicated menus. Being a tree-structure itself, XML lends itself greatly to the display of hierarchical menus. In fact, as you saw earlier, the menu items are nothing more than XML objects. This is also true for the Menus themselves. What you were doing in the previous examples was essentially creating XML nodes using ActionScript. Wouldn't it be nicer if you could have your whole MenuBar defined in a neat external XML file?

Given the standard Menu and MenuBar components that come out of the box, this is possible but slightly messy. For one thing, you cannot use a single, correctly-formed XML file that contains all your various menus (e.g., Options, Help) and your Menu Bar structure (e.g., Options first, then Help). Instead, you need have one XML file per Menu object that you want to create. After having loaded these in, you have to go and add them to the MenuBar and then, to add insult to injury, you need to add your listeners to each of the Menus... I don't know about you but my head spins just thinking about all that. Of course, it also means more code that really has nothing to do with our business logic (what our application is actually trying to achieve). So, to help you out, I've created a MenuBarDecorator class that handles all of this for you automatically and in a very elegant manner.

The following exercise demonstrates how to load in a single XML file to create a MenuBar and all of its associated menus, and set up listeners for each of them.

  1. Continue with the FLA from the previous example. If you don't have it, open up menu_bar3.fla from your downloads and follow along.
  2. Remove all of the code in the Options Menu and Help Menu sections in Frame 1 of the Actions layer, but leave the Options Menu Listener and Help Menu Listener code as they are. You will not be altering the listeners, just the way in which in you create the menu bar and its menus.

  3. Add the following code at the top of the code in Frame 1 of the Actions layer:
    ////////////////////////////////////////////////////////////////
    // Create Menu Bar Decorator
    //////////////////////////////////////////////////////////////// 
    import MenuBarDecorator;
               
    // create a new menu bar decorator instance for myMenuBar
    myMenuBarDecorator = new MenuBarDecorator(myMenuBar);
               
    // load the menu structure into our decorated menu bar
    myMenuBarDecorator.loadMenuStructure("menubar.xml", this);
    Yes, that's it! We've just replaced some 45 or so lines of code with the few you see above. The magic is in the MenuBarDecorator class that you are importing with the first statement but you don't have to worry about how that works at all (you can look in the source for MenuBarDecorator.as, in your downloads if you're of the curious type!).

    In the second line you create a new MenuBarDecorator instance. It's called a Decorator because it adds functionality to an existing component without altering the component itself in any way. If you're coming from an Object-Oriented Programming background, a Decorator pattern implements a Has A relationship instead of an Is A relationship. I have adapted the Decorator pattern here to Flash to allow it to function with an existing component instance on Stage. Although this alters the base pattern slightly in implementation, it does not in spirit and in fact adapts it quite nicely to a semi-visual workflow that is well suited to the semi-visual nature of Flash.

    In the third statement, you tell the MenuBarDecorator instance to load an external menu structure held in a file called menubar.xml. The second argument specifies where the MenuBarDecorator can find your listeners. The rest of the magic happens in the menubar.xml file.

  4. Take a look at the menubar.xml file that is part of your downloaded material:
    <?xml version="1.0" encoding="iso-8859-1"?>
    <menuBar>
      <menu name="Options" instanceName="options" 
    listener="optionsMenuListener"> <menuItem label="Enable Item 3" type="check" instanceName="enableItem3"
    selected="false" /> <menuItem type ="separator" /> <menuItem label="Item 3" instanceName="item3" enabled="false" /> <menuItem type="separator" /> <menuItem label="Either choose me" type="radio" groupName="myGroup"
    instanceName="item5" selected="true"/> <menuItem label="Or me (but not both)" type="radio" groupName="myGroup"
    instanceName="item6" /> </menu> <menu name="Help" instanceName="help" listener="helpMenuListener"> <menuItem label="About" instanceName="aboutMenuItem" /> <menuItem label="Ultrashock" instanceName="aboutUltrashock" /> </menu> </menuBar>
    The complete structure of the MenuBar is contained within this XML file. You specify each of the menus in your MenuBar using the menu tag. So far, this is the same as the built-in functionality. What I have done with the MenuBarDecorator is allowed for three new attributes to the menu tag: name, instanceName and listener.

    The name attribute is where you specify the name of the menu. This is what will show up visually on the MenuBar component as the label of the menu.

    The instanceName attribute specifies the instance name that the MenuBarDecorator will give the current Menu object. Although we don't use it in the example above, the loadMenuStructure() method of the MenuBarDecorator returns and object with references to each of the created Menu objects, in case you need to affect them in some way.

    The final attribute, listener, is where you specify the name of your listener object. When the MenuBarDecorator creates each of the menus, it automatically adds your specified listener, which it searches for in the timeline (or object) reference that you passed to it as the second argument of the loadMenuStructure() method.

  5. Test the movie. Note that it creates exactly the same MenuBar as in the previous example but the code is simpler, neater and more legible and thus easier to maintain and scale. If you get any errors when compiling, make sure the the two required class files, MenuBarDecorator.as and MenuStructure.as are either in your working directory or available in your class path.

- discuss this tutorial -
 
©2003 Ultrashock.com - All rights reserved