javaswingpropertychangelistenerpropertychangesupport

Java: SwingPropertyChangeSupport


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);
    }
}

Solution

  • 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);
       }
    }