1 Reply Latest reply: Nov 5, 2012 10:23 AM by James_D RSS

    bidirectional bind

    959924
      Hello,

      how can I bind a detail pane with a selected row in a tableview in both ways?
      Means when I select a row in the tableview the detail fields will updated, also when I Change the value in a detail field the value in the table have to Change.

      thx

      Olaf
        • 1. Re: bidirectional bind
          James_D
          I think you have to use a listener on your selected item property to achieve this (though others may see a more elegant way to do it).

          When I use "editor panels" I like to use separate fxml files and controllers for them, and you can still do this; define a property for your details pane and bind it to the selected item property in the table's selection model. Then register a change listener with the property, and update the bindings in the detail editor's controls to bind bidirectionally to the properties in the selected item.

          Example: first the standard Person class, which all table examples are required to use.

          Person.java
          import javafx.beans.property.SimpleIntegerProperty;
          import javafx.beans.property.SimpleStringProperty;
          import javafx.beans.property.StringProperty;
          import javafx.beans.value.ChangeListener;
          import javafx.beans.value.ObservableValue;
          
          public class Person {
          
              private final SimpleIntegerProperty num;
              private final SimpleStringProperty firstName;
              private final SimpleStringProperty lastName;
              
          
              public Person(int id, String fName, String lName) {
                this.firstName = new SimpleStringProperty(fName);
                this.lastName = new SimpleStringProperty(lName);
                this.num = new SimpleIntegerProperty(id);
          
          
              public StringProperty firstNameProperty() {
                return firstName ;
              }
              
              public String getFirstName() {
                return firstName.get();
              }
          
              public void setFirstName(String fName) {
                firstName.set(fName);
              }
              
              public StringProperty lastNameProperty() {
                return lastName ;
              }
          
              public String getLastName() {
                return lastName.get();
              }
          
              public void setLastName(String fName) {
                lastName.set(fName);
              }
          
              public int getId() {
                return num.get();
              }
          
              public void setId(int id) {
                num.set(id);
              }
          
            }
          FXML file for the details pane, which has text fields for first and last name:

          DetailsPane.fxml
          <?xml version="1.0" encoding="UTF-8"?>
          
          <?import javafx.scene.layout.GridPane?>
          <?import javafx.scene.control.TextField?>
          <?import javafx.scene.control.Label?>
          
          <GridPane xmlns:fx="http://javafx.com/fxml" fx:controller="DetailsPaneController">
               <Label text="First Name">
               <GridPane.rowIndex>0</GridPane.rowIndex>
               <GridPane.columnIndex>0</GridPane.columnIndex>
               </Label>
               <Label text="Last Name">
               <GridPane.rowIndex>1</GridPane.rowIndex>
               <GridPane.columnIndex>0</GridPane.columnIndex>
               </Label>
               <TextField fx:id="firstNameTextField">
               <GridPane.rowIndex>0</GridPane.rowIndex>
               <GridPane.columnIndex>1</GridPane.columnIndex>
               </TextField>
               <TextField fx:id="lastNameTextField">
               <GridPane.rowIndex>1</GridPane.rowIndex>
               <GridPane.columnIndex>1</GridPane.columnIndex>
               </TextField>
          </GridPane>
          Controller for the Details pane: this defines the property for the person being edited, registers a change listener with it, and binds the text fields to the appropriate properties in the Person.

          DetailsPaneController.java
          import javafx.beans.property.ObjectProperty;
          import javafx.beans.property.SimpleObjectProperty;
          import javafx.beans.value.ChangeListener;
          import javafx.beans.value.ObservableValue;
          import javafx.fxml.FXML;
          import javafx.scene.control.TextField;
          
          
          public class DetailsPaneController {
            private @FXML TextField firstNameTextField ;
            private @FXML TextField lastNameTextField ;
            private ObjectProperty<Person> personProperty ;
            
            public void initialize() {
              personProperty = new SimpleObjectProperty<Person>(null) ;
              
              personProperty.addListener(new ChangeListener<Person>() {
          
                @Override
                public void changed(ObservableValue<? extends Person> observable,
                    Person oldValue, Person newValue) {
          
                  if (oldValue != null) {
                    firstNameTextField.textProperty().unbindBidirectional(oldValue.firstNameProperty());
                    lastNameTextField.textProperty().unbindBidirectional(oldValue.lastNameProperty());
                  }
                  if (newValue != null) {
                    firstNameTextField.textProperty().bindBidirectional(newValue.firstNameProperty());
                    lastNameTextField.textProperty().bindBidirectional(newValue.lastNameProperty());
                  }
                }
                
              });
            }
            
            public ObjectProperty<Person> personProperty() {
              return personProperty ;
            }
            
            public Person getPerson() {
              return personProperty.get();
            }
            
            public void setPerson(Person p) {
              personProperty.set(p);
            }
          }
          Main FXML file. Just defines a table view and references the FXML file above.

          TableWithDetails.fxml
          <?xml version="1.0" encoding="UTF-8"?>
          
          <?import javafx.scene.layout.HBox?>
          <?import javafx.scene.control.TableView?>
          
          <HBox xmlns:fx="http://javafx.com/fxml" fx:controller="TableWithDetailsController">
               <children>
                    <TableView fx:id="tableView"/>
                    <fx:include source="DetailsPane.fxml" fx:id="detailsPane" />
               </children>
          </HBox>
          And the controller for the main fxml file. The initialize method initializes the table, and binds the selectedItem property to the person property defined in the details pane's controller:

          TableWithDetailsController.java
          import javafx.collections.FXCollections;
          import javafx.collections.ObservableList;
          import javafx.fxml.FXML;
          import javafx.scene.control.TableColumn;
          import javafx.scene.control.TableView;
          import javafx.scene.control.cell.PropertyValueFactory;
          
          
          public class TableWithDetailsController {
            private @FXML TableView<Person> tableView ;
            private @FXML DetailsPaneController detailsPaneController ;
            
            public void initialize() {
              final ObservableList<Person> data = FXCollections.observableArrayList(
                  new Person(1, "Joe", "Pesci"),
                  new Person(2, "Audrey", "Hepburn"),
                  new Person(3, "Gregory", "Peck"),
                  new Person(4, "Cary", "Grant"),
                  new Person(5, "Robert", "De Niro")
                  );
              tableView.setItems(data);
              
              TableColumn<Person, Integer> idColumn = new TableColumn<Person, Integer>();
              idColumn.setCellValueFactory(new PropertyValueFactory<Person, Integer>("id"));
              TableColumn<Person, String> firstNameColumn = new TableColumn<Person, String>();
              firstNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
              TableColumn<Person, String> lastNameColumn = new TableColumn<Person, String>();
              lastNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
              tableView.getColumns().addAll(idColumn, firstNameColumn, lastNameColumn);
              
              detailsPaneController.personProperty().bind( tableView.getSelectionModel().selectedItemProperty() );
            }
          }
          Finally, for completeness, the application startup code:

          TableWithDetails.java
          import javafx.application.Application;
          import javafx.fxml.FXMLLoader;
          import javafx.scene.Parent;
          import javafx.scene.Scene;
          import javafx.stage.Stage;
          
          public class TableWithDetails extends Application {
          
            @Override
            public void start(Stage primaryStage) throws Exception {
              Parent root = FXMLLoader.load(getClass().getResource("TableWithDetails.fxml"));
              Scene scene = new Scene(root);
              primaryStage.setScene(scene);
              primaryStage.sizeToScene();
              primaryStage.show();
            }
          
            public static void main(String[] args) {
              launch(args);
            }
          
          }