如何双向绑定到对象集的 属性 到 table 视图的选定项 属性?
How to bidirectionally bind to a property of an object set to the selected item property of a table view?
我的用户界面如下所示:
我想要的是,当您单击 table 中的 1 时,文本框显示 2,而 table 中的 3 显示 4 并且当您在文本框中键入内容时, 所选行绑定到的对象上的 属性 得到更新。
所以我有一个具有两个属性的对象集合(值 1 - 在 table 中,值 2 在文本框中)。
现在复杂一点:我想用双向绑定而不是事件侦听器来做到这一点。
基本上文本框的绑定可以这样描述:
Bind the text property to the value2 property of the selected item property of the table view bi-directionally
这有可能吗?
我一直在摆弄,我得到了这个,但它不是双向的,当然(我希望可能必须提供第二次回调才能朝相反的方向发展):
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>
没有标准的 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);
}
}
我的用户界面如下所示:
我想要的是,当您单击 table 中的 1 时,文本框显示 2,而 table 中的 3 显示 4 并且当您在文本框中键入内容时, 所选行绑定到的对象上的 属性 得到更新。
所以我有一个具有两个属性的对象集合(值 1 - 在 table 中,值 2 在文本框中)。
现在复杂一点:我想用双向绑定而不是事件侦听器来做到这一点。
基本上文本框的绑定可以这样描述:
Bind the text property to the value2 property of the selected item property of the table view bi-directionally
这有可能吗?
我一直在摆弄,我得到了这个,但它不是双向的,当然(我希望可能必须提供第二次回调才能朝相反的方向发展):
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>
没有标准的 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);
}
}