1

I have to create a program with graphical user interfaces which buttons perform certain things including the login and registration. I wanted to use the MVC pattern to create this project in such a way that the code remains "ordered" and clean.

So I created 3 packages: Model, View and Controller.

In Model there are several classes that define the objects (for example: User, Message, etc.). In View there are classes with the code created by NetBeans for GUI. I know that using the NetBeans GUI Builder you can create listeners to associate the various buttons. But this code listener is created in the same file as the View while I would like to separate files in the view from that of the controller.

So I would have something like:

  • Model: files that define the objects
  • View: files that define the GUI
  • Controller: file "manage" the GUI taking user input and process them

But now in the files of the view there are both the GUI and the Controllers (listener).

For example, this is the LoginView file:

public class Login extends javax.swing.JFrame {


   /** Creates new form Login */
   public Login() {
      initComponents();
   }

   /** This method is called from within the constructor to
    * initialize the form.
    * WARNING: Do NOT modify this code. The content of this method is
    * always regenerated by the Form Editor.
    */
   @SuppressWarnings("unchecked")
   // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
   private void initComponents() {

      jPanel1 = new javax.swing.JPanel();
      usernameLabel = new javax.swing.JLabel();
      passwordLabel = new javax.swing.JLabel();
      usernameField = new javax.swing.JTextField();
      regButton = new javax.swing.JButton();
      logButton = new javax.swing.JButton();
      titleLabel = new javax.swing.JLabel();
      passwordField = new javax.swing.JPasswordField();

      javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
      jPanel1.setLayout(jPanel1Layout);
      jPanel1Layout.setHorizontalGroup(
         jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGap(0, 0, Short.MAX_VALUE)
      );
      jPanel1Layout.setVerticalGroup(
         jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGap(0, 0, Short.MAX_VALUE)
      );

      setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
      setTitle("Login");
      setLocationByPlatform(true);
      setName("login"); // NOI18N
      setResizable(false);

      usernameLabel.setText("Username:");

      passwordLabel.setText("Password:");
      passwordLabel.setOpaque(true);

      usernameField.setToolTipText("");

      regButton.setText("Registrati");
      regButton.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            regButtonActionPerformed(evt);
         }
      });

      logButton.setText("Accedi");
      logButton.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            logButtonActionPerformed(evt);
         }
      });

      titleLabel.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
      titleLabel.setText("Inserisci i tuoi dati:");

      javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
      getContentPane().setLayout(layout);
      layout.setHorizontalGroup(
         layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(layout.createSequentialGroup()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
               .addGroup(layout.createSequentialGroup()
                  .addGap(26, 26, 26)
                  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                     .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addComponent(usernameLabel)
                        .addComponent(passwordLabel, javax.swing.GroupLayout.Alignment.TRAILING))
                     .addGroup(layout.createSequentialGroup()
                        .addComponent(regButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 24, Short.MAX_VALUE)
                        .addComponent(logButton))
                     .addComponent(passwordField)
                     .addComponent(usernameField)))
               .addGroup(layout.createSequentialGroup()
                  .addGap(54, 54, 54)
                  .addComponent(titleLabel)))
            .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
      );
      layout.setVerticalGroup(
         layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(layout.createSequentialGroup()
            .addContainerGap()
            .addComponent(titleLabel)
            .addGap(18, 18, 18)
            .addComponent(usernameLabel)
            .addGap(3, 3, 3)
            .addComponent(usernameField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(passwordLabel)
            .addGap(3, 3, 3)
            .addComponent(passwordField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(18, 18, 18)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
               .addComponent(logButton)
               .addComponent(regButton))
            .addContainerGap(22, Short.MAX_VALUE))
      );

      usernameLabel.getAccessibleContext().setAccessibleName("usernameLabel");
      usernameLabel.getAccessibleContext().setAccessibleDescription("");
      passwordLabel.getAccessibleContext().setAccessibleName("passwordLabel");

      setSize(new java.awt.Dimension(218, 227));
      setLocationRelativeTo(null);
   }// </editor-fold>                        

   private void regButtonActionPerformed(java.awt.event.ActionEvent evt) {                                          
      // TODO add your handling code here:
      //**Code for signup (create a new record, insert in database, etc.)**
   }                                         

    private void logButtonActionPerformed(java.awt.event.ActionEvent evt) {                                          
        // TODO add your handling code here:
        //**Code for login (look in the database, etc.)**
    }                                         


   /**
    * @param args the command line arguments
    */
   public static void start() {
      /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
       * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
       */
      try {
         for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
               javax.swing.UIManager.setLookAndFeel(info.getClassName());
               break;
            }
         }
      } catch (ClassNotFoundException ex) {
         java.util.logging.Logger.getLogger(Login.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
      } catch (InstantiationException ex) {
         java.util.logging.Logger.getLogger(Login.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
      } catch (IllegalAccessException ex) {
         java.util.logging.Logger.getLogger(Login.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
      } catch (javax.swing.UnsupportedLookAndFeelException ex) {
         java.util.logging.Logger.getLogger(Login.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
      }
        //</editor-fold>

      /* Create and display the form */
      java.awt.EventQueue.invokeLater(new Runnable() {
         public void run() {
            new Login().setVisible(true);
         }
      });
   }

   // Variables declaration - do not modify                     
   private javax.swing.JPanel jPanel1;
   private javax.swing.JButton logButton;
   private javax.swing.JPasswordField passwordField;
   private javax.swing.JLabel passwordLabel;
   private javax.swing.JButton regButton;
   private javax.swing.JLabel titleLabel;
   private javax.swing.JTextField usernameField;
   private javax.swing.JLabel usernameLabel;
   // End of variables declaration                   
}

How do I separate the View from the Controller? Could you show me an example? I searched a lot on the Internet but I could not find an example that is right for me.

My main problem is that the components that create the NetBeans GUI Builder (button, JLabel, JList, etc.) are private and included in the View class. I must be able to manage these objects from an external class (in the controller).

For example, the LoginView class has only the GUI code and the class LoginController manages buttons and JTextField of LoginView class...

How can I do? Thank you very much!

  • 1
    possible duplicate of [The MVC pattern and SWING](http://stackoverflow.com/questions/5217611/the-mvc-pattern-and-swing) – Aubin Jun 26 '14 at 18:08
  • 1
    See another pattern, more close to swing approach: http://martinfowler.com/eaaDev/PresentationModel.html – Aubin Jun 26 '14 at 18:48

1 Answers1

0

You'll want to create another class to act as your action listener. You can do this by having a class implement the ActionListener interface. For an example, here's a class you can use for your regButton

package Controller; //I assume you'll want this class in the Controller package, right?

/*I'd recommend importing the classes you need at the beginning of the program
* that way, you don't have to type so much out every time */
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent; 

public class RegButtonListener implements ActionListener
{
    actionPerformed(ActionEvent evt)
    {
        Model.Login.regButtonActionPerformed(evt);
        //I'm assuming that the Login class you gave is in the Model package you made.
    }

    /*Because you're implementing an interface, you'll probably have to add in some
    *other abstract methods. Just add them in and leave them empty*/
}

And now in your Login class, just have the button point to this new class when you add the action listener.

  regButton.setText("Registrati");
  regButton.addActionListener(Controller.RegButtonListener);

  public void regButtonActionPerformed(java.awt.event.ActionEvent evt)
  {
      //Do whatever it is the button does
  }

Using this method, you'll have to create a whole class for every component. And that can get really messy really fast. One alternative would be to have your new action listening class work for all your components and call the appropriate method. Like this example:

package Controller;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent; 

public class GeneralActionListener implements ActionListener
{
    actionPerformed(ActionEvent evt)
    {
        Object source = evt.getSource();
        //This might not work if NetBeans makes everything private
        if (source == Model.Login.regButton)
        {
            Model.Login.regButtonActionPerformed(evt);
        }
        else if (source == Model.Login.logButton)
        {
            Model.Login.logButtonActionPerformed(evt);
        }
        //Repeat for each component
    }
}

Hope that helps. Sorry, I've never used the NetBeans GUI builder before, but this is generally how I would do things. Another common trick (which doesn't keep the classes separate but is still handy) would be to have the Login class itself implement the ActionListener interface and put the ActionPerformed method in there.

Cameron Tauxe
  • 312
  • 1
  • 5
  • Hello you have been really nice but I have a doubt. Following your method, however I have the code for the management of the data entered in the GUI in the View file and not in the files of the Controller. Am I wrong? –  Jun 26 '14 at 19:34
  • The "package Controller;" line at the top of the class should put the class into the Controller package, so it would be in a separate file than the other class. – Cameron Tauxe Jun 26 '14 at 19:39
  • Ok but the code that creates the GUI (for example: `JButton regButton = new JButton()`, etc.) I have to insert it into the LoginView file that is in the file that contains the second block of code written by you (the one that begins with `regButton.setText ("Registrati");`). The problem is that the file LoginView is already very complicated due to the creation of the GUI so I will not complicate it further by including the code of the listener. Maybe I'm saying something wrong but I think your code does not separate these two things...I apologize if I'm wrong.. –  Jun 26 '14 at 19:50
  • Oh. I think I see what you're getting at. Are you saying that you don't want the code that performs the action for the button to be in the same class as the one that creates the GUI? If that's the case, then you can replace the 'Model.Login.regButtonActionPerformed(evt)' with the code to perform the action directly. So then the code would be in the class for handling all the action listening instead of the class that creates the GUI. – Cameron Tauxe Jun 26 '14 at 20:01
  • Yeah, I really wanted this. Now I modified the code of the Login class (in the packege View) by inserting `regButton.addActionListener(Controller.RegButtonListener)`. Then I created a new class called `RegButtonListener` in which I included the method `actionPerformed(ActionEvent evt)` that executes the code for the button click. But there are 2 problems: **1)** the method `actionPerformed(ActionEvent evt)` I need to close the current frame (the login) so I have to use the `dispose()` method. –  Jun 26 '14 at 20:31
  • When I had all the code (GUI code and code of the listener) in the same file, I wrote simply `this.dispose()`. But now what I write instead of this? **2)** In Login class (in the packege View) I have included the line `regButton.addActionListener(Controller.RegButtonListener)`. However, Netbeans tells me an error "package regButton does not exist. expected". Why does he consider `regButton` a package? How can I fix these two problems? –  Jun 26 '14 at 20:32