6 votes

Comment lier une étiquette JavaFX à l'élément sélectionné dans une liste (ListView) ?

J'ai un ListView rempli de POJOs et je veux une étiquette dans l'interface graphique pour afficher les informations de l'élément sélectionné.

Mon POJO ressemble à quelque chose comme ça :

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

Maintenant, lorsque l'utilisateur sélectionne un client dans la liste, je veux que le nom du client sélectionné soit affiché dans une étiquette.

De toute évidence, je ne peux pas me lier à la name directement parce qu'il ne s'agit pas d'un Property . (Et je ne veux pas remplacer mes Clients String s avec StringProperty -parce que les SimpleStringProperty n'est pas serializable et j'ai besoin de la Customer pour être transféré via RMI).

J'ai essayé le BeanPathAdapter de JFXtras (qui est très joli d'ailleurs) comme ceci :

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

Mais cette solution ne fait que me renvoyer une 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

J'espérais donc qu'il y aurait une meilleure solution que d'utiliser lstCustomers.getSelectionModel().selectedItemProperty().addListener(...) et de gérer manuellement la population des étiquettes à cet endroit.

3voto

Jurgen Points 1090

Une meilleure solution, je pense, que celle que j'ai donnée précédemment, est d'utiliser le BeanPathAdapter comme vous l'avez essayé.
Cependant, la propriété suivante doit être ajoutée au BeanPathAdapter :

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

Ensuite, dans votre code, vous avez besoin de ce qui suit :

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

1voto

Jurgen Points 1090

Je ne pense pas qu'il y ait une phrase simple que tu cherches.
Vous pouvez faire ce qui suit :

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

Mais vous devrez modifier votre POJO Client comme suit :

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

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

Je ne pense pas que ce soit recommandé, car les propriétés sont censées refléter les changements dans les données sous-jacentes et la méthode ci-dessus ne reflétera que le nom tel qu'il était lorsque nameProperty a été appelé. Ainsi, si setName est appelé, la propriété ne reflétera pas le changement. Si le nom du client ne change jamais, vous pouvez vous en sortir.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X