如何使用 Bindings.when 将按钮 disableProperty 与 TableView Selecteditem 绑定 属性

How to use Bindings.when to bind button disableProperty with a TableView Selecteditem property

我有一个包含模型 class 的 TableView,它有一个布尔值属性,如下所示

@FXML
TableView<Model> tableView;

型号Class:

class Model{

BooleanProperty valid;

public Model()
{
valid = new SimpleBooleanProperty();
}
... getters and setters
}

我想要实现的是将按钮禁用 属性 与模型 class 中的选定项目有效 属性 从 table 视图绑定,我知道我可以通过监听器实现这一点,但是使用监听器需要首先正确设置初始值,因为在发生一些变化之前它们不会被触发,例如在这种情况下,如果 [=42 中没有选定的项目=] 并且按钮从一开始就设置为不禁用,它仍然是那样,直到监听器被触发,这就是为什么我更喜欢使用绑定,因为它不关心初始值。有什么办法也可以用 Bindings 做到这一点吗?

我试过的:

我试过这个:

transferButton.disableProperty().bind(Bindings.when(tableView.getSelectionModel().selectedItemProperty().isNotNull()).then(
               tableView.getSelectionModel().getSelectedItem().valideProperty()
        ).otherwise(false));

但问题是我收到以下错误:

return value of "javafx.scene.control.TableView$TableViewSelectionModel.getSelectedItem()" is null

即使我为绑定设置了条件:Bindings.when(tableView.getSelectionModel().selectedItemProperty().isNotNull()

您可以使用实现侦听器的自定义绑定:例如:

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

Bindings API 中还有一个 selection API 可以使用,尽管它不可​​靠并且会在选择为空时生成虚假警告:

transferButton.disableProperty().bind(Bindings.selectBoolean(
    tableView.getSelectionModel().selectedItemProperty(),
    "valid"
)).not());

这是自定义 select 绑定的方法,它使用函数提供嵌套属性(类似于核心 SelectBinding,只是通过提供嵌套属性的函数替换对嵌套属性的反射访问)

基本思路

  • 从绑定到根开始
  • 在依赖项中保留绑定链
  • 在验证时更新绑定链(只要绑定无效就无需执行任何操作)
  • 实施状态清理

代码示例(这里只有一个功能,可以扩展为更长的链,但是,通过添加更多功能并遍历供应商)

/**
 * 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;
    }

}

在 OP 的上下文中使用:

// XSelectBinding
ObjectBinding<Boolean> xSelectBinding = new XSelectBinding<Model, Boolean>(
            table.getSelectionModel().selectedItemProperty(),
            item -> item.validProperty());
transferButton.disableProperty().bind(BooleanExpression.booleanExpression(xSelectBinding).not());