How to use Bindings.when to bind button disableProperty with a TableView Selecteditem property
You can use a custom binding which implements a listener: for example:
transferButton.disableProperty().bind(new BooleanBinding() {
{
tableView.getSelectionModel().selectedItemProperty().addListener(obs, oldSelection, newSelection) -> {
if (oldSelection != null) unbind(oldSelection.validProperty());
if (newSelection != null) bind(newSelection.validProperty());
invalidate();
});
bind(tableView.getSelectionModel().selectedItemProperty());
}
@Override
protected boolean computeValue() {
Model selection = tableView.getSelectionModel().getSelectedItem();
if (selection == null) return true ;
return ! selection.isValid();
}
});
There is also a selection
API in the Bindings
API which will work, though it is not robust and will generate spurious warnings when the selection is null:
transferButton.disableProperty().bind(Bindings.selectBoolean(
tableView.getSelectionModel().selectedItemProperty(),
"valid"
)).not());
Here's an approach of a custom select binding which uses functions to provide nested properties (similar to core SelectBinding, just replacing the reflective access to the nested properties by functions providing them)
The basic idea
- start with binding to the root
- keep the binding chain in the dependencies
- update the binding chain on validating (no need to do anything as long as the binding is not valid)
- implement state cleanup
Code example (here with a single function only, can be extended for a longer chain, though, by adding more functions and walk the providers)
/**
* Custom binding to a nested property using a Function to provide the nested.
*/
public class XSelectBinding<R, T> extends ObjectBinding<T> {
private ObservableList<ObservableValue<?>> dependencies;
private Function<R, ObservableValue<T>> provider;
public XSelectBinding(ObservableValue<R> root, Function<R, ObservableValue<T>> provider) {
if (root == null) {
throw new NullPointerException("root must not be null");
}
if (provider == null) {
throw new NullPointerException("provider must not be null");
}
dependencies = FXCollections.observableArrayList(root);
this.provider = provider;
bind(root);
}
/**
* Implemented to update dependencies and return the value of the nested property if
* available
*/
@Override
protected T computeValue() {
onValidating();
ObservableValue<?> child = dependencies.size() > 1 ? dependencies.get(1) : null;
return child != null ? (T) child.getValue() : null;
}
/**
* Updates dependencies and bindings on validating.
*/
protected void onValidating() {
// grab the root
ObservableValue<R> root = (ObservableValue<R>) dependencies.get(0);
// cleanup bindings and dependencies
unbindAll();
// rebind starting from root
dependencies.add(root);
ObservableValue<T> nestedProperty = root.getValue() != null ?
provider.apply(root.getValue()) : null;
if (nestedProperty != null) {
dependencies.add(nestedProperty);
}
bind(dependencies.toArray(new ObservableValue<?>[dependencies.size()]));
}
/**
* Unbinds and clears dependencies.
*/
private void unbindAll() {
unbind(dependencies.toArray(new ObservableValue<?>[dependencies.size()]));
dependencies.clear();
}
@Override
public ObservableList<?> getDependencies() {
return FXCollections.unmodifiableObservableList(dependencies);
}
/**
* Implemented to unbind all dependencies and clear references to path providers.
*/
@Override
public void dispose() {
unbindAll();
provider = null;
}
}
To use in the OP's context:
// XSelectBinding
ObjectBinding<Boolean> xSelectBinding = new XSelectBinding<Model, Boolean>(
table.getSelectionModel().selectedItemProperty(),
item -> item.validProperty());
transferButton.disableProperty().bind(BooleanExpression.booleanExpression(xSelectBinding).not());