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());