Reputation: 426
I have a TableView that holds a Model class, which has a BooleanProperty as follow
@FXML
TableView<Model> tableView;
Model Class :
class Model{
BooleanProperty valid;
public Model()
{
valid = new SimpleBooleanProperty();
}
... getters and setters
}
What i want to acheive is to bind a button disable property with selected item valid Property in the Model class fom the tableView, i know that i can acheive that with listeners, but using a listener needs to set first the initial value properly, since they are not getting fired until there is some change, as an exemple in this case, if there is no selected item from the table and the button is set to be not disable from the start, it will still be like that, until the listener fired, this is why i prefer to use Bindings, since it doesn't care about the initial value. is there any way to do so with Bindings also ?
what i tried :
i tried this :
transferButton.disableProperty().bind(Bindings.when(tableView.getSelectionModel().selectedItemProperty().isNotNull()).then(
tableView.getSelectionModel().getSelectedItem().valideProperty()
).otherwise(false));
but the problem is that i'm getting the following error :
return value of "javafx.scene.control.TableView$TableViewSelectionModel.getSelectedItem()" is null
Even tho i put a condition to the binding : Bindings.when(tableView.getSelectionModel().selectedItemProperty().isNotNull()
Upvotes: 0
Views: 528
Reputation: 51535
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
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());
Upvotes: 1
Reputation: 209684
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());
Upvotes: 2