Search code examples
javajavafx-2bindobservablelist

How to bind the position in a List to an attribute of that element?


I have an ObservableList<MyElement> list = FXCollections.observableArrayList();

public class MyElement
{
  private IntegerProperty position;//with getter
  //[...]

  //somewhere in the constructor taking the list as argument
  position.bind(list.indexOf(this));
}

Now I'd like to bind MyElement.position to the actual position in the list i.e. if the position changes in the list (for instance drag and drop in the GUI or anything else) I want the position property to be updated automatically.

Is this possible? Can I make a bidirectional binding between these values?


Solution

  • I don't know if I correctly understood your question, but I will try to answer it :-).

    The thing is, once a ObservableList (javafx.collections) object does not store some kind of "selected" index state, why do we should bind another integer to it?

    I think, in this case, your code should be responsible for store the "selected" index state and expose it to a client code. If this is what you are looking for, I suggest you have three attributes to deal with it:

    public class ListSelection<T> {
        private ObservableList<T> items = FXCollections.observableArrayList(new ArrayList<T>());
        private ObjectProperty<T> selectedItem = new SimpleObjectProperty<>();
        private IntegerProperty selectedIndex = new SimpleIntegerProperty(0);
    }
    

    The selected element can be controlled using the selectedIndex attribute.

    Then, create a bind to the selectedItem, to "automatically" update it when selectedIndex change:

    selectedItem.bind(
        when(isEmpty(items))
            .then(new SimpleObjectProperty<T>())
            .otherwise(valueAt(items, selectedIndex))
    );
    

    Bindings should have been imported statically:

    import static javafx.beans.binding.Bindings.*;
    

    Notice the use of method Bindings.valueAt(ObservableList<E> list, ObservableIntegerValue index). It creates a bind to the list.get(index.getValue()) element.

    Finally, you can use it like this:

    ListSelection<String> selection = new ListSelection<>();
    Label label = new Label();
    List<String> weekDays = Arrays.asList("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday");
    
    selection.getItems().addAll(weekDays);
    label.textProperty().bind(selection.selectedItemProperty());
    

    I also suggest you to take a look to javafx.scene.control.SelectionModel class and its subclasses (eg. javafx.scene.control.SingleSelectionModel). Maybe, it could be easier to extend some of them.