简体   繁体   中英

How to bind a JavaFX Label to the selected item from a ListView

I have a ListView full of POJOs and want a label in the GUI to display informations from the selected item.

My POJO looks something like that:

class Customer {
  private String name;
  ...
  public String getName() {
  return name; 
  }

Now when the user selects a customer from the list I want the name of the selected customer displayed in a label.

Obviously I can't bind to the name directly because it is not a Property . (And I don't want to replace my Customers String s with StringProperty -objects because the SimpleStringProperty is not serializable and I need the Customer to be transfered via RMI.)

I've tried the BeanPathAdapter from JFXtras (which looks really nice by the way) like this:

    BeanPathAdapter<MultipleSelectionModel> customerBeanPathAdapter;
    customerBeanPathAdapter = new BeanPathAdapter<>(lstCustomers.getSelectionModel());
    customerBeanPathAdapter.bindBidirectional("selectedItem.name", lblCustomerName.textProperty());

But this solution only throws me an Exception:

...
Caused by: java.lang.IllegalArgumentException: Unable to resolve accessor getSelectedItem
at jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle.buildAccessor(BeanPathAdapter.java:3062)
at jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle.buildAccessorWithLikelyPrefixes(BeanPathAdapter.java:3022)
at jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle.updateMethodHandles(BeanPathAdapter.java:2986)
at jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle.<init>(BeanPathAdapter.java:2977)
at jfxtras.labs.scene.control.BeanPathAdapter$FieldBean.performOperation(BeanPathAdapter.java:1348)
at jfxtras.labs.scene.control.BeanPathAdapter$FieldBean.performOperation(BeanPathAdapter.java:1186)
at jfxtras.labs.scene.control.BeanPathAdapter.bindBidirectional(BeanPathAdapter.java:567)
at jfxtras.labs.scene.control.BeanPathAdapter.bindBidirectional(BeanPathAdapter.java:369)
at at.gs1.sync.qm.client.gui.MainWindowController.initialize(MainWindowController.java:61)
... 22 more
Caused by: java.lang.IllegalAccessException: symbolic reference class is not public: class javafx.scene.control.ListView$ListViewBitSetSelectionModel, from jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle
at java.lang.invoke.MemberName.makeAccessException(MemberName.java:512)
at java.lang.invoke.MethodHandles$Lookup.checkSymbolicClass(MethodHandles.java:1113)
at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:1094)
at java.lang.invoke.MethodHandles$Lookup.findVirtual(MethodHandles.java:626)
at jfxtras.labs.scene.control.BeanPathAdapter$FieldHandle.buildAccessor(BeanPathAdapter.java:3049)
... 30 more

So I hoped there would be a better solution than to use lstCustomers.getSelectionModel().selectedItemProperty().addListener(...) and handle the population of the labels there manually.

A better solution I think, to the one I gave before, is to use the BeanPathAdapter as you tried.
However the BeanPathAdapter needs to have the following property added to it:

private final ObjectProperty<B>  beanProp = new SimpleObjectProperty<>();
{
    beanProp.addListener( new ChangeListener<B>()
    {
        @Override
        public void changed( ObservableValue<? extends B> ob, B oldVal, B newVal )
        {
            setBean( newVal );
        }
    } );
}

public ObjectProperty<B> beanProperty()
{
    return beanProp; 
}

Then in your code you need the following:

BeanPathAdapter<Customer>  custBean;
custBean = new BeanPathAdapter<>( new Customer() );   // empty or any customer
custBean.bindBidirectional( "name", label.textProperty() );
custBean.beanProperty().bind( listview.getSelectionModel().selectedItemProperty() );

I don't think that there's a simple one liner that you are looking for.
You could do the following:

label.textProperty().bind( Bindings.selectString( listview.getSelectionModel().selectedItemProperty(), "name" ) );

But you will need to modify your Customer POJO like so:

class Customer 
{
    private String name;
    ...
    public String getName() { return name;  }

    public ReadOnlyStringProperty nameProperty()
    {
        return new SimpleStringProperty( name );
    }
}

I don't think this is recommended though because properties are expected to reflect changes in the underlying data and the above will only reflect the name as it was when nameProperty is called. So if setName is called the property won't reflect the change. If the Customer name never changes then you could get away with this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM