3

我有一个简单的 JFrame,里面有几个 jtextfields,每个 jtextfield 的 text 属性通过数据绑定与对象的一个​​字段绑定(我使用窗口构建器来设置绑定),当用户在 JTextField 上更改某些内容时,更改会自动反映对于绑定对象属性,我需要当用户按下 JButton(取消按钮)时,用户所做的所有更改都将被丢弃。

所以我希望当用户开始编辑像事务这样的字段时,将启动事务,并且根据用户操作(确定或取消按钮),事务被提交或回滚。

Swing 数据绑定框架可以吗?如何 ?

这里是初始化数据绑定的代码:

    /**
     * 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();
    }
4

1 回答 1

4

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

  • the bean is Album
  • AlbumModel is a wrapper (aka: buffer) around the bean with the same properties as the wrapped: the view is bound to the properties of this wrapper
  • internally, it uses a read-once binding to the wrappee properties
  • in addition, the wrapper has a property "buffering" which is true once any of its buffered properties is different from the wrappee. In this state, the changes can be either committed or canceled

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


}
于 2011-12-24T12:48:11.360 回答