javaswingwindowbuilderbeans-binding

Swing DataBinding Rollback changes when cancel button is pressed


I have a simple JFrame with several jtextfields inside, the text property of each jtextfield is bound with a field of an object through databinding (i used window builder to setup the binding), when the user change something on the JTextField the changes are automatically reflected to the bound object property, i have the needs that when the user press a JButton (Cancel Button) every changes done by the user will be discarded.

So i want that when the user start editing the field like a transaction will be started and depending on the user action (OK or Cancel Button) the transaction being Committed or RollBacked .

Is it possible with Swing Data Binding framework ? How ?

Here the code that initialize data bindings :

    /**
     * Data bindings initialization 
     */
    protected void initDataBindings() {
        //Title field
        BeanProperty<Script, String> scriptBeanProperty = BeanProperty.create("description");
        BeanProperty<JTextField, String> jTextFieldBeanProperty = BeanProperty.create("text");
        AutoBinding<Script, String, JTextField, String> autoBinding = Bindings.createAutoBinding(UpdateStrategy.READ_WRITE, script, scriptBeanProperty, textFieldName, jTextFieldBeanProperty, "ScriptTitleBinding");
        autoBinding.bind();
        //Id field 
        BeanProperty<Script, Long> scriptBeanProperty_1 = BeanProperty.create("id");
        BeanProperty<JLabel, String> jLabelBeanProperty = BeanProperty.create("text");
        AutoBinding<Script, Long, JLabel, String> autoBinding_1 = Bindings.createAutoBinding(UpdateStrategy.READ, script, scriptBeanProperty_1, labelScriptNo, jLabelBeanProperty, "ScriptIdBinding");
        autoBinding_1.bind();
    }

Solution

  • nothing out off the box, you have to implement the buffering logic yourself. An example is in my swinglabs incubator section, look at the AlbumModel. Basically

    Below is an excerpt of AlbumModel (nearly all minus validation) which might give you an idea. Note that BindingGroupBean is a slightly modified BindingGroup which maps internal state to a bean property "dirty" to allow binding of "buffering". You can find it in the incubator as well as a complete application BAlbumBrowser (an implementation of Fowler's classical example in terms of BeansBinding)

    /**
     * Buffered presentation model of Album. 
     * 
     */
    @SuppressWarnings("rawtypes")
    public class AlbumModel extends Album {
        @SuppressWarnings("unused")
        private static final Logger LOG = Logger.getLogger(AlbumModel.class
                .getName());
        private Album wrappee;
    
        private BindingGroupBean context;
        private boolean buffering;
    
        public AlbumModel() {
            super();
            initBinding();
        }
    
        @Action (enabledProperty = "buffering")
        public void apply() {
            if ((wrappee == null)) 
                return;
            context.saveAndNotify();
        }
    
        @Action (enabledProperty = "buffering")
        public void discard() {
            if (wrappee == null) return;
            context.unbind();
            context.bind();
        }
    
        private void initBinding() {
            initPropertyBindings();
            initBufferingControl();
        }
    
        private void initBufferingControl() {
            BindingGroup bufferingContext = new BindingGroup();
            // needs change-on-type in main binding to be effective
            bufferingContext.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ, 
                    context, BeanProperty.create("dirty"), 
                    this, BeanProperty.create("buffering")));
            bufferingContext.bind();
        }
    
        /**
         * Buffer wrappee's properties to this.
         */
        private void initPropertyBindings() {
            context = new BindingGroupBean(true);
            context.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_ONCE,
                    wrappee, BeanProperty.create("artist"), 
                    this, BeanProperty.create("artist")));
            context.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_ONCE,
                    wrappee, BeanProperty.create("title"), 
                    this, BeanProperty.create("title")));
            // binding ... hmm .. was some problem with context cleanup 
            // still a problem in revised binding? Yes - because
            // it has the side-effect of changing the composer property
            // need to bind th composer later
            context.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_ONCE,
                     wrappee, BeanProperty.create("classical"), 
                     this, BeanProperty.create("classical")));
            context.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_ONCE,
                    wrappee, BeanProperty.create("composer"), 
                    this, BeanProperty.create("composer")));
            context.bind();
        }
    
        public void setAlbum(Album wrappee) {
            Object old = getAlbum();
            boolean oldEditEnabled = isEditEnabled();
            this.wrappee = wrappee;
            context.setSourceObject(wrappee);
            firePropertyChange("album", old, getAlbum());
            firePropertyChange("editEnabled", oldEditEnabled, isEditEnabled());
        }
    
        public boolean isEditEnabled() {
            return (wrappee != null); // && (wrappee != nullWrappee);
        }
    
    
        public boolean isComposerEnabled() {
            return isClassical();
        }
    
        /**
         * Overridden to fire a composerEnabled for the sake of the view.
         */
        @Override
        public void setClassical(boolean classical) {
            boolean old = isComposerEnabled();
            super.setClassical(classical);
            firePropertyChange("composerEnabled", old, isComposerEnabled());
        }
    
        public boolean isBuffering() {
            return buffering;
        }
    
        public void setBuffering(boolean buffering) {
            boolean old = isBuffering();
            this.buffering = buffering;
            firePropertyChange("buffering", old, isBuffering());
        } 
    
        /**
         * Public as an implementation artefact - binding cannot handle
         * write-only properrties? fixed in post-0.61
         * @return
         */
        public Album getAlbum() {
            return wrappee;
        }
    
    
    }