2

I am trying to make an MVC Java Swing program that makes use of SwingPropertyChangeSupport to notify the view whenever the model gets updated. The problem I am having is that the notifications do not seem to be happening.

I have prepared an SSCCE below. In the SSCCE, there is a Swing GUI that has a button and a text field. When you click the button, a counter in the model gets incremented, and the view is supposed to get notified so that it can update itself. However, it appears the notifications do not get sent/received (I am not sure which -- it could be both) even though I have checked to make sure that oldValue and newValue are different from each other. I would appreciate any assistance in understanding where I've gone wrong. Thanks!

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.SwingPropertyChangeSupport;


public class Main extends JFrame {

    public Main() {
        PropertyChangeView theGui = new PropertyChangeView();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(true);
        add(theGui);
        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new Main();
            }
        });
    }
}


class PropertyChangeView extends JPanel {
    private JButton button;
    private JTextField textfield;
    private GridBagConstraints gbc;
    private PropertyChangeController controller;

    public PropertyChangeView() {
        super(new GridBagLayout());
        controller = new PropertyChangeController();
        button = new JButton("Click me to increment the count");
        textfield = new JTextField(10);

        button.addActionListener(new ButtonListener());
        addPropertyChangeListener(new MyPropertyChangeListener());

        gbc = new GridBagConstraints();
        gbc.gridheight = 1;
        gbc.gridwidth = 1;
        gbc.anchor = GridBagConstraints.LINE_START;
        gbc.fill = GridBagConstraints.BOTH;

        gbc.gridx = 0;
        gbc.gridy = 0;
        add(button, gbc);

        gbc.gridx = 1;
        gbc.gridy = 0;
        add(textfield, gbc);
    }

    private class MyPropertyChangeListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            System.out.println("Event received " + evt);

            if (evt.getPropertyName().equals(PropertyChangeModel.CHANGED)) {
                textfield.setText(evt.getNewValue().toString());
            }
        }
    }

    private class ButtonListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            controller.setCounter(controller.getCounter() + 1);
            System.out.println("counter now = " + controller.getCounter());
        }
    }

}


class PropertyChangeController {
    private PropertyChangeModel model;

    public PropertyChangeController() {
        model = new PropertyChangeModel();
    }

    public int getCounter() {
        return model.getCounter();
    }

    public void setCounter(int i) {
        model.setCounter(i);
    }
}


class PropertyChangeModel {
    public static final String CHANGED = "property change model updated";

    private int counter;
    private SwingPropertyChangeSupport pcs;

    public PropertyChangeModel() {
        counter = 0;
        pcs = new SwingPropertyChangeSupport(this);
    }

    public int getCounter() {
        return counter;
    }

    public void setCounter(int i) {
        int oldValue = counter;
        int newValue = i;
        counter = newValue;
        pcs.firePropertyChange(CHANGED, oldValue, newValue);
        System.out.println("setCounter finished with oldValue=" + oldValue + ", newValue=" + newValue);
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
user1002119
  • 3,692
  • 4
  • 27
  • 30

1 Answers1

3

I've not run your program, but I see one thing out of order here:

public void setCounter(int i) {
    int oldValue = counter;
    int newValue = i;
    pcs.firePropertyChange(CHANGED, oldValue, newValue);
    counter = newValue;
    System.out.println("setCounter finished with oldValue=" + oldValue + ", newValue=" + newValue);
}

which should be:

public void setCounter(int i) {
    int oldValue = counter;
    int newValue = i;
    counter = newValue;
    pcs.firePropertyChange(CHANGED, oldValue, newValue);
    System.out.println("setCounter finished with oldValue=" + oldValue + ", newValue=" + newValue);
}

You should fire notification only after updating the model's value.

Your main problem though is that you add no PropertyChangeListener to the model.


e.g.

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.SwingPropertyChangeSupport;

public class Main extends JFrame {

   public Main() {
      PropertyChangeView theGui = new PropertyChangeView();
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      setResizable(true);
      add(theGui);
      pack();
      setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            new Main();
         }
      });
   }
}

class PropertyChangeView extends JPanel {
   private JButton button;
   private JTextField textfield;
   private GridBagConstraints gbc;
   private PropertyChangeController controller;

   public PropertyChangeView() {
      super(new GridBagLayout());
      PropertyChangeModel model = new PropertyChangeModel();
      controller = new PropertyChangeController(model);
      button = new JButton("Click me to increment the count");
      textfield = new JTextField(10);

      button.addActionListener(new ButtonListener());
      model.addPropertyChangeListener(new MyPropertyChangeListener());


      gbc = new GridBagConstraints();
      gbc.gridheight = 1;
      gbc.gridwidth = 1;
      gbc.anchor = GridBagConstraints.LINE_START;
      gbc.fill = GridBagConstraints.BOTH;

      gbc.gridx = 0;
      gbc.gridy = 0;
      add(button, gbc);

      gbc.gridx = 1;
      gbc.gridy = 0;
      add(textfield, gbc);
   }

   private class MyPropertyChangeListener implements PropertyChangeListener {
      @Override
      public void propertyChange(PropertyChangeEvent evt) {
         System.out.println("Event received " + evt);

         if (evt.getPropertyName().equals(PropertyChangeModel.CHANGED)) {
            textfield.setText(evt.getNewValue().toString());
         }
      }
   }

   private class ButtonListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         controller.setCounter(controller.getCounter() + 1);
         System.out.println("counter now = " + controller.getCounter());
      }
   }

}

class PropertyChangeController {
   private PropertyChangeModel model;

   // public PropertyChangeController() {
   // model = new PropertyChangeModel();
   // }


   public PropertyChangeController(PropertyChangeModel model) {
      this.model = model;
   }

   public int getCounter() {
      return model.getCounter();
   }


   public void setCounter(int i) {
      model.setCounter(i);
   }
}

class PropertyChangeModel {
   public static final String CHANGED = "property change model updated";

   private int counter;
   private SwingPropertyChangeSupport pcs;

   public PropertyChangeModel() {
      counter = 0;
      pcs = new SwingPropertyChangeSupport(this);
   }

   public void addPropertyChangeListener(
         PropertyChangeListener l) {
      pcs.addPropertyChangeListener(l);
   }

   public int getCounter() {
      return counter;
   }

   public void setCounter(int i) {
      int oldValue = counter;
      int newValue = i;
      counter = newValue;
      pcs.firePropertyChange(CHANGED, oldValue, newValue);
      System.out.println("setCounter finished with oldValue=" + oldValue
            + ", newValue=" + newValue);
   }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thank you, I have updated the code sample to put the `counter = newValue` before firing the notification. As for the PropertyChangeListener, I added one to the view, because that is what I wanted to get notified. Is that incorrect? (I guess it must be because my program doesn't work!) – user1002119 Jun 02 '15 at 22:29
  • @user1002119: you've got that backwards. You add the listener to the item you're listening for changes to -- the model. Your listener will then notify the view. Please see changes to code above. – Hovercraft Full Of Eels Jun 02 '15 at 22:33
  • @user1002119: Although I'd do all my creation of the single control/model and view and hooking them up within a separate class, but for speed of answering (I have to go to work) I did not make this big of a change to your code yet. – Hovercraft Full Of Eels Jun 02 '15 at 22:34
  • Thank you for taking the time to help me! So you add the listener to the same object that is firing the property change notifications? Your updated sample really helped, and I was able to get this working in my real project. Thanks again! – user1002119 Jun 02 '15 at 23:31