MVC/Designer Example
— print (last updated: Sep 9, 2009) print

Select font size:
The goal of this document is twofold: The application we're creating is very simple. It should look something like this:
Clicking the Next button displays in the textfield a random sequence of numbers between 0 and 99. The Reset button clears the textfield and resets the random number generator.

MVC Construction in NetBeans Designer

Although NetBeans' Designer gives the option of creating a so-called Desktop Application, we do not want to use this since it takes us too far afield from the basic Swing applications. Instead, we'll always work from Java Applications and add the the basic Swing containers (JFrame, JPanel, etc) as NetBeans' visual forms.

The NetBeans Designer forms are, in effect, a combination of an XML file and a Java class. The XML file is used by the designer to provide a visual interface for constructing the component. The Java class is the actual code in which parts are "automatically generated" according to the XML file, and not directly editable.

View/Controller event handling

A Java event generation consists of an object which generates the event (like a Button) and an object which performs an action when the generator generates the event. The event generator is part of the View, belonging directly to one of the visual components. NetBeans will easily lead you to making the visual component be the handler, but this is exactly what we want to avoid, in order to maintain the separation of the View and Controller.

In our scheme, the Main program will be the controller and the handler of all the events (with some exceptions) generated by objects in the visual components. Permitting the Main program to defined an event handler for a visual component means that the visual component must define a public interface function which associates a listener defined outside the component directly to the object which generates the event.

For example, suppose we have defined a MyVisualComponent class which contains a button; this can all be done in the Designer. Then, however, we want to break out of the design mode into source mode and define an interface function, addButtonActionListener, making the class look like this:
public class MyVisualComponent extends ... {

  public void addButtonActionListener(ActionListener listener) {
    button.addActionListener(listener);
  }

  public MyVisualComponent() {
    // ...
  }

  private JButton button = new JButton("My Button");
}
The Main class, acting as controller, becomes the button's event handler by virtue of defining:
public class Main ... {

  private MyVisualComponent mvc = new MyVisualComponent():

  public Main() {
    mvc.addButtonActionListener(
      new ActionListener() {
        public void actionPerformed(ActionEvent evt) { ... }
      }
    );
  }
}

Handler objects

The handler is an object defined by an anonymous inner class specified by the code:
new ActionListener() {
   public void actionPerformed(ActionEvent evt) { ... }
}
What happens is that Java translates this into code something like this:
class MyClass implements ActionListener() {
   public void actionPerformed(ActionEvent evt) { ... }
}
ActionListener myobj = new MyClass();
mvc.addButtonActionListener(myobj);
In particular, the class MyClass is an inner class in that it is defined within the larger class within a function. Inner classes can also be defined at the data level, but this is scoped differently. The class MyClass is never given a name per se; Java references these anonymous classes by number. The instantiation is also anonymous. In certain cases it is advantageous to name the object, myobj, so that it can be reusable, e.g.,
ActionListener myobj = new ActionListener() {
    public void actionPerformed(ActionEvent evt) { ...  }
  };
mvc.addButtonActionListener(myobj);

Retrieving/Setting content in a visual component

If, say, the visual component has a textfield the clicking of a button could signify that the controller should read the content and do something with it. In order to maintain the View/Controller separation, we again define interface function(s) to handle the needs of the application. For example, consider MyVisualComponent class holding a JTextField component myinfo. We may have a setup like this:
public class MyVisualComponent extends ... {
  public String getMyinfoText() {
    return myinfo.getText();
  }

  public MyVisualComponent() {
    // ...
  }

  private JTextField myinfo = new JTextField();
}
In the controller the code which reads the text of the textfield when the button is clicked would look something like this:
public class Main ... {

  private MyVisualComponent mvc = new MyVisualComponent():

  public Main() {
    mvc.addButtonActionListener(
      new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
           String infoText = mvc.getMyinfoText();
        }
      }
    );
  }
}

Interface functions

Observe the nascent systematic creation of the interface function names:
addButtonActionListener()      calls       button.addActionListener()
getMyinfoText()                calls       myinfo.getText()
There is no official rule or other religious dogma we're following, it's just a convention that we make up. Depending on the needs of the application, or how reusable we wish to make our visual component, other interface functions for myinfo may apply:
public void setMyinfoText(String text) { myinfo.setText(text); }
public void setMyinfoEnabled(boolean b) { myinfo.setEnabled(b); }

The construction

The construction asks you to add code in a number of places. You can either key it in (recommended), or for expediency, copy and paste it in. If you do copy and paste, you should reformat the source code by simply right-clicking the editor window and selecting Format.
  1. Select File New Project
  2. In the New Project window, select the Java category, and choose Java Application. Click Next.
  3. Choose the project name "MVCExample". Keep Create Main Class and Set as Main Project checkboxes checked. Click Finish.

we will let the created package mvexample serve as the repository for our Controller classes.
  1. Right-click on the project and select New JFrame Form. Enter this information (the views package will be automatically created):
    Class Name: Frame
    Package: views
NetBeans present you with the JFrame in Design mode in which one can use the accompanying Palette and other tools to create many GUI code features automatically. The Source mode is available for entering code at appropriate places; it also contains generated code which is not editable.
  1. Modify Frame in Design mode. Right-click on the form and select Set Layout FlowLayout
  2. Right-click on the form and select Properties. Set the title property to MVC Example.
  3. Make the following additions to the form using the Swing Controls from the Palette.
  4. Again right-click on jTextField, select Properties, and
  5. Edit mvcexample.Main. Modify the class code to be this:
    private final views.Frame frame = new views.Frame();
    
    public Main() {
      frame.setVisible(true);
    }
        
    public static void main(String[] args) {
      new Main();
    }
    
  6. Save and test run the program. The visual appearance of the application should be right although nothing happens yet.
Frame class which can be used by the Main class (the controller) to determine the semantic behavior of the visual object. Towards this end, we need to manually edit Frame (in source mode) and add these desired capabilities.
  1. Edit Frame in Source mode. Add this import line (just underneath the package statement):
    import java.awt.event.*;
    
    Add these member functions (just inside the class Frame declaration):
    public void setOutputText(String text) { 
      output.setText(text); 
    }
    
    public void addNextActionListener(ActionListener a) {
      next.addActionListener(a);
    }
    
    public void addResetActionListener(ActionListener a) {
      reset.addActionListener(a);
    }
    
To complete the application we create the Model and join the Model to the View inside the Controller, i.e. the mvcexample.Main class.
  1. Create the Java Class Data in the models package.
  2. Add these into the models.Data class:
    private java.util.Random rnd = new java.util.Random();
    
    public Data() { 
      reset();
    }
    
    public int next() {
      return rnd.nextInt(100);
    }
    
    public void reset() { 
      rnd.setSeed(0);
    }
    
  3. Edit mvcexample.Main. Add the import line:
    import java.awt.event.*;
    
    Add this data declaration after class Main (same idea as the frame variable):
    private final models.Data data = new models.Data();
    
    Add this code inside the Main constructor after the "frame.setVisible(true);" statement:
    frame.addNextActionListener(
      new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
          frame.setOutputText("" + data.next());
        }
      }
    );
    
    frame.addResetActionListener(
      new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
          frame.setOutputText("");
          data.reset();
        }
      }
    );
    
  4. Finally, run the program and test it.

Create a standalone application

Select Build Build Main Project. This creates a JAR file with all the class files. It is held in the dist folder of the project directory MVCExample. As suggested in the output, to run it, change directory to this folder and do:
java -jar MVCExample.jar
In Linux using the GNOME Desktop, you can create a Launcher which basically has the above statement, something like:
java -jar PATH-TO-THE-JAR-FILE/MVCExample.jar 
In Windows if you want to distribute this application to Desktop users, have them download and store MVCExample.jar in some dedicated folder, which, for simplicity we'll say is the C:\ folder. Then:
  1. Right-click on the desktop and select New Shortcut. This brings up wizard which asks for a location. You should be able to simply type javaw.exe (not java.exe). These Java executables are most likely already a user's Windows system in an executable-accessible folder (like \windows\system32). Click Next, then Finish.
  2. Rename the shortcut whatever you like and then right-click on it to obtain the properties.
  3. Change Start in to "C:\"
  4. In the Target entry append this:
    -jar MVCExample.jar
  5. Click OK and then try out the application shortcut by double-clicking it.


© Robert M. Kline