【问题标题】:How to bidirectionally bind to a property of an object set to the selected item property of a table view?如何双向绑定到设置为表视图的选定项属性的对象的属性?
【发布时间】:2015-09-02 09:37:53
【问题描述】:

我的用户界面如下所示:

我想要的是,当您单击表格中的 1 时,文本框在表格上显示 2 和 3,它显示 4,并且当您在文本框中键入时,所选行的对象上的属性一定会更新。

所以我有一个具有两个属性的对象集合(value1 - 在表中,value2 在文本框中)。

现在是复杂性:我想通过双向绑定而不是事件侦听器来做到这一点

基本上文本框的绑定可以这样描述:

将 text 属性双向绑定到 table view 的 selected item 属性的 value2 属性

这有可能吗?

我一直在摆弄,我得到了这个,但它当然不是双向的(我希望可能必须提供第二个回调才能朝相反的方向前进):

this.textField.textProperty().bind(         
        Bindings.createStringBinding(() -> {

            if (this.tableView.getSelectionModel().getSelectedItem() == null) {
                return "";
            }

            return String.valueOf(this.tableView.getSelectionModel().getSelectedItem().value2Property().get());
        }, 
        this.tableView.getSelectionModel().selectedItemProperty()));

这是我正在谈论的完整工作示例,如果它更清楚的话:

import java.net.URL;
import java.util.ResourceBundle;

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

public final class Program extends Application {

    private static final class SomeSortOfController implements Initializable {

        @FXML private TableView<SomeSortOfObject> tableView;
        @FXML private TableColumn<SomeSortOfObject, Number> value1TableColumn;
        @FXML private TextField textField;

        @Override
        public void initialize(final URL url, final ResourceBundle resourceBundle) {

            this.tableView.setItems(FXCollections.observableArrayList(
                    new SomeSortOfObject(1,2),
                    new SomeSortOfObject(3,4)));

            this.value1TableColumn.setCellValueFactory(c -> c.getValue().value1Property());
            this.value1TableColumn.setEditable(false);

            // Here I need to come up with some sort of binding from the selected item
            // in the table view to the value2 property in the SomeSortOfObject (and
            // vice versa)
        }
    }

    private static final class SomeSortOfObject {

        private final IntegerProperty value1;
        private final IntegerProperty value2;

        public SomeSortOfObject(int value1, int value2) {
            this.value1 = new SimpleIntegerProperty(value1);
            this.value2 = new SimpleIntegerProperty(value2);
        }

        public IntegerProperty value1Property() {
            return this.value1;
        }

        public IntegerProperty value2Property() {
            return this.value2;
        }
    }

    public static void main(final String[] args) {
        launch(args);
    }

    @Override
    public void start(final Stage stage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/main.fxml"));
        loader.setController(new SomeSortOfController());   
        stage.setScene(new Scene(loader.load(), 200, 100));     
        stage.show();
    }
}

为了完整,这是 main.fxml 文件的内容:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<HBox xmlns="http://javafx.com/javafx/8.0.45" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <TableView fx:id="tableView">
        <columns>
          <TableColumn text="value1" fx:id="value1TableColumn" />
        </columns>
      </TableView>
      <TextField fx:id="textField" />
   </children>
</HBox>

【问题讨论】:

  • 你不能 - selectedItem/Index 是只读的
  • @kleopatra,谢谢,但我不想在表格视图上设置 selectedItem/Index - 我想在我的对象上设置 value2Property 和文本框的 text 属性
  • 我觉得我的标题很差
  • hmm .. 但是,问题出在哪里? You can replace the binding whenever the selection changes.我可能仍然误解你的问题:-)
  • 您可以执行一次,然后在需要时重复使用。有几种实现,我非常简单的一个叫做PathAdapter :-)

标签: java binding javafx javafx-8


【解决方案1】:

没有标准的 API 方法可以做到这一点。有一些第三方库提供此功能。最著名的可能是EasyBind,而且这个库可能会在版本 9 中并入 JavaFX。

这是一个使用 EasyBind 的 SSCCE:

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.monadic.PropertyBinding;

public class BidirectionalBindingToNestedProperty extends Application {

    private PropertyBinding<String> selectedValue2;

    @Override
    public void start(Stage primaryStage) {
        TableView<Item> table = new TableView<>();
        TableColumn<Item, String> col1 = new TableColumn<>("Value 1");
        col1.setCellValueFactory(cellData -> cellData.getValue().value1Property());

        table.getColumns().add(col1);

        TextField textField = new TextField();
        selectedValue2 = EasyBind
                .monadic(table.getSelectionModel().selectedItemProperty())
                .selectProperty(Item::value2Property);
        textField.textProperty().bindBidirectional(selectedValue2);

        for (int i = 1 ; i <= 40; i+=2) {
            Item item = new Item(String.valueOf(i), String.valueOf(i + 1));
            item.value2Property().addListener((obs, oldValue, newValue) -> 
                System.out.println("Item with value1 = "+item.getValue1() + " changed value2 from "+oldValue+" to "+newValue));
            table.getItems().add(item);
        }

        BorderPane root = new BorderPane(table, null, null, null, textField);

        Scene scene = new Scene(root, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static class Item {
        private final StringProperty value1 = new SimpleStringProperty();
        private final StringProperty value2 = new SimpleStringProperty();

        public Item(String value1, String value2) {
            setValue1(value1);
            setValue2(value2);
        }

        public final StringProperty value1Property() {
            return this.value1;
        }

        public final java.lang.String getValue1() {
            return this.value1Property().get();
        }

        public final void setValue1(final java.lang.String value1) {
            this.value1Property().set(value1);
        }

        public final StringProperty value2Property() {
            return this.value2;
        }

        public final java.lang.String getValue2() {
            return this.value2Property().get();
        }

        public final void setValue2(final java.lang.String value2) {
            this.value2Property().set(value2);
        }


    }

    public static void main(String[] args) {
        launch(args);
    }
}

【讨论】:

  • 感谢您的回答。但是,您的示例不起作用-我完全按照引用easybind 1.0.3的方式使用它,并且单击表格不会更新文本框,反之亦然-我将深入研究easybind,看看我是否能弄清楚发生了什么
  • 我有 EasyBind 1.0.4-SNAPSHOT。我不认为 EasyBind 遭受标准 JavaFX 绑定所遭受的“过早垃圾收集”问题,但我更新了问题中的代码以强制它持有对“嵌套属性”的引用;看看这是否有所作为。我将试用 EasyBind 1.0.3。
  • 出于兴趣,是否将 PropertyBinding 用作字段(而不是局部变量)来修复它?
  • 是的,绝对是,将其移动到本地会破坏它。
  • EasyBind的作者还有一个叫ReactFX的框架,主要目的是实现“事件流”。该框架的 2.0 版有一个 Val 类表示一个可观察值,其工作方式类似于此处示例中的 EasyBind 属性。该实现不使用WeakListeners,因此不会遭受相同的垃圾收集问题(因此您可以将变量设为本地)。见tomasmikula.github.io/blog/2015/02/10/…tomasmikula.github.io/blog/2015/02/10/…
猜你喜欢
  • 1970-01-01
  • 2013-02-14
  • 1970-01-01
  • 2017-05-17
  • 2015-03-22
  • 2013-04-28
  • 2013-03-14
相关资源
最近更新 更多