Is it possible to bind the non-empty state of an ObservableList inside an ObjectProperty with the Bindings API?


Question

I have a situation where I want to bind a BooleanProperty to the non-empty state of an ObservableList wrapped inside an ObjectProperty.

Here's a basic synopsis of the behavior I'm looking for:

    ObjectProperty<ObservableList<String>> obp = new SimpleObjectProperty<ObservableList<String>>();

    BooleanProperty hasStuff = new SimpleBooleanProperty();

    hasStuff.bind(/* What goes here?? */);

    // ObservableProperty has null value 
    assertFalse(hasStuff.getValue());

    obp.set(FXCollections.<String>observableArrayList());

    // ObservableProperty is no longer null, but the list has not contents.
    assertFalse(hasStuff.getValue());

    obp.get().add("Thing");

    // List now has something in it, so hasStuff should be true
    assertTrue(hasStuff.getValue());

    obp.get().clear();

    // List is now empty.
    assertFalse(hasStuff.getValue());

I'd like to use the builders in the Bindings class rather than implementing a chain of custom bindings.

The Bindings.select(...) method theoretically does what I want, except that there's no Bindings.selectObservableCollection(...) and casting the return value from the generic select(...) and passing it to Bindings.isEmpty(...) doesn't work. That is, the result of this:

    hasStuff.bind(Bindings.isEmpty((ObservableList<String>) Bindings.select(obp, "value")));

causes a ClassCastException:

java.lang.ClassCastException: com.sun.javafx.binding.SelectBinding$AsObject cannot be cast to javafx.collections.ObservableList

Is this use case possible using just the Bindings API?


Solution

Based on answer from @fabian, here's the solution that worked:

    ObjectProperty<ObservableList<String>> obp = new SimpleObjectProperty<ObservableList<String>>();

    ListProperty<String> lstProp = new SimpleListProperty<>();
    lstProp.bind(obp);

    BooleanProperty hasStuff = new SimpleBooleanProperty();
    hasStuff.bind(not(lstProp.emptyProperty()));

    assertFalse(hasStuff.getValue());

    obp.set(FXCollections.<String>observableArrayList());

    assertFalse(hasStuff.getValue());

    obp.get().add("Thing");

    assertTrue(hasStuff.getValue());

    obp.get().clear();

    assertFalse(hasStuff.getValue());
1
9
2/7/2014 3:03:26 PM

Accepted Answer

I don't see a way to do this using Bindings API only. ObservableList doesn't have a property empty, so you can't use

Bindings.select(obp, "empty").isEqualTo(true)

and

ObjectBinding<ObservableList<String>> lstBinding = Bindings.select(obp);
hasStuff.bind(lstBinding.isNotNull().and(lstBinding.isNotEqualTo(Collections.EMPTY_LIST)));

doesn't work since it only updates when the list changes, but not when it's contents change (i.e. the third assertion fails).

But the custom chain of bindings you have to create is very simple:

SimpleListProperty lstProp = new SimpleListProperty();
lstProp.bind(obp);
hasStuff.bind(lstProp.emptyProperty());
6
2/7/2014 1:26:58 PM

It could be done with fewer variables:

SimpleListProperty<String> listProperty = new SimpleListProperty<>(myObservableList);

BooleanProperty hasStuff = new SimpleBooleanProperty();
hasStuff.bind(not(listProperty.emptyProperty()));

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon