Search code examples
javaswingjpaneljcomboboxpropertychangelistener

how to trigger an action in parent JPanel when a component in a child JPanel is updated (Java Swing)


I am trying to build an MVC application in Java Swing. I have a JPanel that contains four JComboBoxes and this JPanel is embedded into a parent JPanel. The parent JPanel has other controls in addition to the child JPanel.

The child JPanel's model gets correctly updated whenever I change the values of the JComboBoxes (it's basically a date picker with one combo box each for year, month, day of month, and hour of day). What I cannot figure out is how I can trigger the parent JPanel's model to update itself to match the value stored in the child JPanel's model whenever one of the JComboBoxes is changed.

Below is a stripped down SSCCE of the structure of what I have so far. Thank you.

import java.awt.event.*;
import javax.swing.*;

public class Example extends JFrame {
    public Example() {
        super();
        OuterView theGUI = new OuterView();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);
        add(theGUI);
        pack();
        setVisible(true);        
    }

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

class OuterView extends JPanel {
    public OuterView() {
        super();
        InnerView innerPanel = new InnerView();
        JButton button = new JButton("display OuterView's model");
        button.addActionListener(new ButtonListener());
        add(innerPanel);
        add(button);
    }

    private class ButtonListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent ae) {
            System.out.println("button was clicked");
        }
    }
}

class InnerView extends JPanel {
    public InnerView() {
        super();
        String[] items = new String[] {"item 1", "item 2", "item 3"};
        JComboBox comboBox = new JComboBox(items);
        comboBox.addActionListener(new ComboBoxListener());
        add(comboBox);
    }

    private class ComboBoxListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent ae) {
            String text = ((JComboBox) ae.getSource()).getSelectedItem().toString();
            System.out.println("store " + text + " in InnerView's model");
            System.out.println("now how do I cause OuterView's model to be updated to get the info from InnerView's model?");
        }        
    }
}

Solution

  • You could use a PropertyChangeListener, and in fact one is built into every component. e.g.:

    import java.awt.event.*;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class Example extends JFrame {
       public Example() {
          super();
          OuterView theGUI = new OuterView();
          setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          setResizable(false);
          add(theGUI);
          pack();
          setVisible(true);
       }
    
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                new Example();
             }
          });
       }
    }
    
    class OuterView extends JPanel {
       private String innerValue = "";
    
       public OuterView() {
          super();
          InnerView innerPanel = new InnerView();
          innerPanel.addPropertyChangeListener(new PropertyChangeListener() {
    
             @Override
             public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getPropertyName().equals(InnerView.COMBO_CHANGED)) {
                   innerValue = evt.getNewValue().toString();
                   System.out.println("new value from inside of OuterView: "
                         + innerValue);
                }
             }
          });
          JButton button = new JButton("display OuterView's model");
          button.addActionListener(new ButtonListener());
          add(innerPanel);
          add(button);
       }
    
       private class ButtonListener implements ActionListener {
          @Override
          public void actionPerformed(ActionEvent ae) {
             System.out.println("button was clicked. innerValue: " + innerValue);
          }
       }
    }
    
    class InnerView extends JPanel {
       public static final String COMBO_CHANGED = "Combo Changed";
       // private SwingPropertyChangeSupport pcSupport = new
       // SwingPropertyChangeSupport(this);
       String oldValue = "";
    
       public InnerView() {
          super();
          String[] items = new String[] { "item 1", "item 2", "item 3" };
          JComboBox comboBox = new JComboBox(items);
          comboBox.addActionListener(new ComboBoxListener());
          add(comboBox);
    
       }
    
       private class ComboBoxListener implements ActionListener {
          @Override
          public void actionPerformed(ActionEvent ae) {
             String text = ((JComboBox) ae.getSource()).getSelectedItem()
                   .toString();
             firePropertyChange(COMBO_CHANGED, oldValue, text);
             oldValue = text;
             System.out.println("store " + text + " in InnerView's model");
          }
       }
    }