How can I develop a VCE-extendable GUI framework in VisualAge for Java?

Scott Stanchfield

The problem with designing GUI frameworks in VisualAge is that once you add components to a container, you cannot add components to subclasses of that container. For example, suppose you extend Frame, creating class MyFramework, adding a status bar and menus to it. If you extend MyFramework, the VCE will notice that you have already added components to it and not allow you to add any more! (The reason for this is that the superclass should remain independent. The owner of the superclass could add a new component to it, a toolbar perhaps. If your subclass adds a component in the position that the toolbar would go, the subclass effectively hides the toolbar.)

We can "trick" the VCE into allowing us to create a framework, using  the Beans.isDesignTime() method provided by the JavaBean Specification. We make the VCE think that there are no components in the superclass by adding those components only at runtime. At design time (working in the VCE) we don't add the extra components (status bar, menu items, etc). The VCE user can then add components to the framework's subclass, and we effectively move those components into a sub-panel of the framework at runtime.

The following code demonstrates how this is possible. The basic trick is to provide different behavior for design time vs. run time in your framework.

The framework does nothing special at design time. As far as the developer of the subclass is concerned, he is adding components directly to the Frame. At run time, however, things get interesting. The framework places all VCE-added components into a special container that we'll call the body. The body contains the user-constructed GUI, and we place this body into whatever layout we want for our framework. In this example, we define a BorderLayout for our framework, adding the body in the center of that layout and a status bar in the south of that layout. We also add a menu bar to the layout. (If we were using Swing's JFrame as the base of the framework, we could also have added a tool bar to the framework.)

import java.awt.*;
import java.awt.event.*;
import java.beans.Beans;
import java.awt.event.*;

/** A Framework Frame
 *  This Frame can be subclassed and composed in the VCE
 *  This class is meant as an example only and is lacking
 *    any real functionality
 *  The trick (thought up by Gil Peeters) is to add components to
 *    a "body panel" at runtime, and add them to the Frame at
 *    design time.
 *  At design time, you add panels directly to the subclass of
 *    this Framework. All you see in the VCE are your newly added
 *    components.
 *  At runtime, the VCE-added components are added to the body panel
 *    which is added to the Frame. This gives us a chance to add
 *    other components, like a status bar and menus to the Frame
 *  If you set your own menubar or status bar in the subclass, 
 *    we do not create one 
 */
public class Framework extends Frame {
    // did we setup the framework yet?
    private transient boolean setup;

    // the panel where contents are added at runtime
    private transient Panel body;

    // the status bar
    private Component statusBar;

    /** This constructor exists to set null layout in the superclass Frame */
    public Framework() {
        super(null);
    }

    /** All components that are added come through here. 
     *    (see java.awt.Container's add() methods)
     *  * NOTE * This should not be called directly by the user.    
     *  Use the add() methods to add items to the framework
     *  Note the "trick" being employed:
     *    If at runtime, we add the VCE-added components to the _body_
     *    panel rather than directly to the frame. At design time, we
     *    add directly to the Frame
     *  @param comp the component to be added.
     *  @param constraints an object expressing layout constraints for this component.
     *  @param index the position in the container's list at which to insert the component,
     *           where -1 means insert at the end.
     */
    protected void addImpl(Component comp, Object constraints, int index) {
        // If runtime, add to the body   
        if (!Beans.isDesignTime())
            
            // If the component is the body itself or the status bar,
            //   add them directly to the Frame
            if (comp == getFrameworkBody() || comp == getStatusBar())
                super.addImpl(comp, constraints, index);
            else
                // Add any other components to the Body
                // (these would have been added via the VCE)
                getFrameworkBody().add(comp, constraints);

        else
            // At design time, just add directly to the Frame
            super.addImpl(comp, constraints, index);
    }

    /** Create the Framework's menu bar
     *  For real use this must be made more flexible, perhaps by
     *    exposing the items and menus via protected get methods?
     *  This example merely demonstrates that we can setup menus in
     *    the superclass
     *  Note that if the user explicitly sets the menu bar for
     *    the subclass, this method is not called
     */
    protected MenuBar createMenuBar() {
        MenuBar bar = new MenuBar();
        Menu fileMenu = new Menu("File");
        Menu editMenu = new Menu("Edit");
        Menu helpMenu = new Menu("Help");
        bar.add(fileMenu);
        bar.add(editMenu);
        bar.add(helpMenu);

        MenuItem newItem = new MenuItem("New");
        MenuItem openItem = new MenuItem("Open");
        MenuItem closeItem = new MenuItem("Close");
        MenuItem exitItem = new MenuItem("Exit");
        exitItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }});
        fileMenu.add(newItem);
        fileMenu.add(openItem);
        fileMenu.add(closeItem);
        fileMenu.add(exitItem);
        
        MenuItem undoItem = new MenuItem("Undo");
        MenuItem redoItem = new MenuItem("Redo");
        MenuItem cutItem = new MenuItem("Cut");
        MenuItem copyItem = new MenuItem("Copy");
        MenuItem pasteItem = new MenuItem("Paste");
        editMenu.add(undoItem);
        editMenu.add(redoItem);
        editMenu.add(new MenuItem("-"));
        editMenu.add(cutItem);
        editMenu.add(copyItem);
        editMenu.add(pasteItem);

        MenuItem aboutItem = new MenuItem("About");
        helpMenu.add(aboutItem);
        
        return bar;
    }

    /** Returns the body panel -- it is created only when
     *    needed via lazy instantiation
     */
    protected Panel getFrameworkBody() {
        if (body == null)
            body = new Panel(null);
        return body;
    }

    /** Return the status bar component. By default it's
     *    just a label, though the subclass could set it
     */
    public Component getStatusBar() {
        if (statusBar == null) {
            statusBar = new Label("");
        }
        return statusBar;
    }

    /** Override paint to set up layout the first time it's going to be displayed...  
     *  (Ideally we should determine a better way to do this...)
     */
    public void paint(Graphics g) {
        if (!Beans.isDesignTime() && !setup) {
            setup = true;
            super.setLayout(new BorderLayout());
            add(getFrameworkBody(), BorderLayout.CENTER);
            if (getMenuBar() == null)
                setMenuBar(createMenuBar());
            add(getStatusBar(), BorderLayout.SOUTH);
            validate();
        }
        super.paint(g);
    }

    /** Sets the layout manager for this container.
     *  Note how we delegate to the body panel at runtime...
     *  @param mgr the specified layout manager  
     */
    public void setLayout(LayoutManager mgr) {
        if (Beans.isDesignTime())
            super.setLayout(mgr);
        else
            getFrameworkBody().setLayout(mgr);
    }

    /** Allows the user to set the component used as the status bar */
    public void setStatusBar(Component statusBar) {
        this.statusBar = statusBar;
    }
}

See also

Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

About | Sitemap | Contact