Question

I am having trouble setting a bean which is a member of another bean and having it update correctly. More concretely, I have:

public class MyForm extends javax.swing.JFrame {
    MyBean aBean;
    JTextField aTextField;
    ....
    public MyBean getABean() { return aBean; }
    public void setABean(MyBean newBean) { 
        MyBean oldBean = aBean;
        aBean = newBean;
        propertyChangeSupport.firePropertyChange(PROP_MYBEAN, oldBean, aBean);
    }
    ...
    binding = Bindings.createAutoBinding(
        AutoBinding.UpdateStrategy.READ_WRITE,
        aBean, 
        ELProperty.create("${file.name}"),
        aTextField, 
        BeanProperty.create("text")
    );
    bindingGroup.addBinding(binding);
    ...
}

where MyBean has a File member with appropriate getters ad setters. What I am trying to do is have it so that I can change aBean using the setter and have anything bound to it update to reflect the value of the new aBean.

I can see the issue; the original aBean was bound to the TextField, and when a new aBean is assigned, there is no "swapping". However, seeing the problem doesn't mean I have the solution!

My current work-around involves a middle-man class. I have MyForm has MyMiddleMan and MyMiddleMan has MyBean. Whenever I want to change MyBean, the setter in MyMiddleMan is called and fires an appropriate event which is seen by MyForm. The member of MyForm never changes and so it "works".

My work-around is bad on many levels. Is there a better, best-practice way? I admit I'm not a Beans expert and may be missing something obvious. I am using NetBeans IDE 8.0 if that is relevant, and want to produce my GUI using the builder. Obviously, my real code is much larger than this example, so if I have to start adding extra code every time, it might get cumbersome.

I have a working example below that demonstrates my issue. As the Beans need to be public, there are three files; MyBean.java, MyMiddleMan.java and MyForm.java. It should produce four bound text fields. The first demonstrates I can change the contents of a member without problem. The second shows my problem (I know why it doesn't work, just not how to fix it), the third is irrelevant, taken from the tutorial as a bit of a "sanity check", and the fourth is using my "MyMiddleMan" work-around.

MyBean.java

package testpackage;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;

public class MyBean {

    private File file;

    public static final String PROP_FILE = "file";

    public MyBean() {
    }

    public MyBean(File file) {
        this.file = file;
    }

    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        File oldFile = this.file;
        this.file = file;
        propertyChangeSupport.firePropertyChange(PROP_FILE, oldFile, file);
    }

    private transient final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.removePropertyChangeListener(listener);
    }

    @Override
    public String toString() {
        return "MyBean{" + "file=" + file.getName() + '}';
    }

}

MyMiddleMan.java

package testpackage;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public class MyMiddleMan {

    private MyBean myBean;

    public static final String PROP_MYBEAN = "myBean";

    /**
     * Get the value of myBean
     *
     * @return the value of myBean
     */
    public MyBean getMyBean() {
        return myBean;
    }

    /**
     * Set the value of myBean
     *
     * @param myBean new value of myBean
     */
    public void setMyBean(MyBean myBean) {
        MyBean oldMyBean = this.myBean;
        this.myBean = myBean;
        propertyChangeSupport.firePropertyChange(PROP_MYBEAN, oldMyBean, myBean);
    }

    private transient final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

    /**
     * Add PropertyChangeListener.
     *
     * @param listener
     */
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.addPropertyChangeListener(listener);
    }

    /**
     * Remove PropertyChangeListener.
     *
     * @param listener
     */
    public void removePropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.removePropertyChangeListener(listener);
    }

}

MyForm.java Much of this was automatically generated by NetBeans.

package testpackage;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;

public class MyForm extends javax.swing.JFrame {

    private static final long serialVersionUID = -9151817428068627139L;

    public MyForm() {
        initComponents();
        myBean1.setFile(new File("test.txt"));
        setMyBean2(new MyBean(new File("test.txt")));
        myMiddleMan1.setMyBean(new MyBean(new File("test.txt")));

        System.out.println("MyBean1: " + myBean1);
        System.out.println("MyBean2: " + myBean2);
        System.out.println("MyMiddleMan.myBean: " + myMiddleMan1.getMyBean());
    }

    public static final String PROP_MYBEAN2 = "myBean2";

    /**
     * Get the value of myBean2
     *
     * @return the value of myBean2
     */
    public MyBean getMyBean2() {
        return myBean2;
    }

    /**
     * Set the value of myBean2
     *
     * @param myBean2 new value of myBean2
     */
    public void setMyBean2(MyBean myBean2) {
        MyBean oldMyBean2 = this.myBean2;
        this.myBean2 = myBean2;
        propertyChangeSupport.firePropertyChange(PROP_MYBEAN2, oldMyBean2, myBean2);
    }

    private transient final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

    /**
     * Add PropertyChangeListener.
     *
     * @param listener
     */
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.addPropertyChangeListener(listener);
    }

    /**
     * Remove PropertyChangeListener.
     *
     * @param listener
     */
    public void removePropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.removePropertyChangeListener(listener);
    }

    /**
     * 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() {
        bindingGroup = new org.jdesktop.beansbinding.BindingGroup();

        myBean1 = new testpackage.MyBean();
        myBean2 = new testpackage.MyBean();
        myMiddleMan1 = new testpackage.MyMiddleMan();
        jTextField1 = new javax.swing.JTextField();
        jTextField2 = new javax.swing.JTextField();
        jSlider1 = new javax.swing.JSlider();
        jTextField3 = new javax.swing.JTextField();
        jTextField4 = new javax.swing.JTextField();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, myBean1, org.jdesktop.beansbinding.ELProperty.create("${file.name}"), jTextField1, org.jdesktop.beansbinding.BeanProperty.create("text"));
        bindingGroup.addBinding(binding);

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ, jSlider1, org.jdesktop.beansbinding.ELProperty.create("${value}"), jTextField2, org.jdesktop.beansbinding.BeanProperty.create("text"));
        bindingGroup.addBinding(binding);

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, myBean2, org.jdesktop.beansbinding.ELProperty.create("${file.name}"), jTextField3, org.jdesktop.beansbinding.BeanProperty.create("text"));
        bindingGroup.addBinding(binding);

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, myMiddleMan1, org.jdesktop.beansbinding.ELProperty.create("${myBean.file.name}"), jTextField4, org.jdesktop.beansbinding.BeanProperty.create("text"));
        bindingGroup.addBinding(binding);

        jLabel1.setText("Altering aBean");

        jLabel2.setText("Setting new aBean");

        jLabel3.setText("Slider (irrelevant)");

        jLabel4.setText("Using MyMiddleMan");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel2)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jTextField3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel4)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jTextField4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel3)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap(79, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel2)
                    .addComponent(jTextField3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(jLabel3)
                        .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(jSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel4)
                    .addComponent(jTextField4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(190, Short.MAX_VALUE))
        );

        bindingGroup.bind();

        pack();
    }// </editor-fold>                        

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* 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(MyForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(MyForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(MyForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(MyForm.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 MyForm().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JSlider jSlider1;
    private javax.swing.JTextField jTextField1;
    private javax.swing.JTextField jTextField2;
    private javax.swing.JTextField jTextField3;
    private javax.swing.JTextField jTextField4;
    private testpackage.MyBean myBean1;
    private testpackage.MyBean myBean2;
    private testpackage.MyMiddleMan myMiddleMan1;
    private org.jdesktop.beansbinding.BindingGroup bindingGroup;
    // End of variables declaration                   
}
Was it helpful?

Solution

I worked it out.

I was selecting myBean as the source and binding to ${file.name}.

What worked is selecting Form as the source and binding to ${myBean.file.name}.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top