This discussion is archived
1 Reply Latest reply: Nov 5, 2012 8:23 AM by James_D RSS

bidirectional bind

959924 Newbie
Currently Being Moderated
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 Guru
    Currently Being Moderated
    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);
      }
    
    }

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points