5 Replies Latest reply: Dec 17, 2012 8:01 PM by James_D RSS

    ComboBox is refreshed inside a TableView

    936633
      I am having a problem that while scrolling the TableView the values of the ComboBox are refreshed. How can i avoid this behaviour.

      import javafx.application.Application;
       
      import javafx.scene.Group;
       
      import javafx.scene.Scene;
       
      import javafx.stage.Stage;
       
      import javafx.beans.property.BooleanProperty;
       
      import javafx.beans.property.SimpleBooleanProperty;
       
      import javafx.beans.property.StringProperty;
       
      import javafx.beans.property.SimpleStringProperty;
       
      import javafx.beans.value.ChangeListener;
       
      import javafx.beans.value.ObservableValue;
       
      import javafx.collections.FXCollections;
       
      import javafx.collections.ObservableList;
       
      import javafx.event.EventHandler;
       
      import javafx.geometry.Pos;
       
      import javafx.scene.control.CheckBox;
      import javafx.scene.control.ComboBox;
       
      import javafx.scene.control.TableCell;
       
      import javafx.scene.control.TableColumn;
       
      import javafx.scene.control.TableColumn.CellEditEvent;
       
      import javafx.scene.control.TableView;
       
      import javafx.scene.control.TextField;
       
      import javafx.scene.control.cell.PropertyValueFactory;
       
      import javafx.scene.input.KeyCode;
       
      import javafx.scene.input.KeyEvent;
      import javafx.scene.layout.VBox;
       
      import javafx.util.Callback;
       
       
       
      /**
      
       * A simple table that uses cell factories to add a control to a table
      
       * column and to enable editing of first/last name and email.
      
       *
      
       * @see javafx.scene.control.TableCell
      
       * @see javafx.scene.control.TableColumn
      
       * @see javafx.scene.control.TablePosition
      
       * @see javafx.scene.control.TableRow
      
       * @see javafx.scene.control.TableView
      
       */
       
      public class Experiment extends Application {
           ObservableList<Person> data = null ;
           static CheckBox checkBox;
           static ComboBox comboBox;
       
       
          @SuppressWarnings({ "unchecked", "rawtypes" })
           private void init(Stage primaryStage) {
       
              Group root = new Group();
       
              primaryStage.setScene(new Scene(root));
              
             checkBox = new CheckBox();
             comboBox = new ComboBox();
       
              data = FXCollections.observableArrayList(
                      new Person(true, "Jacob", "Smith", "jacob.smith@example.com"),
                      new Person(false, "Isabella", "Johnson", "isabella.johnson@example.com"),
                      new Person(true, "Ethan", "Williams", "ethan.williams@example.com"),
                      new Person(true, "Emma", "Jones", "emma.jones@example.com"),
                      new Person(false, "Isabella", "Johnson", "isabella.johnson@example.com"),
                      new Person(true, "Ethan", "Williams", "ethan.williams@example.com"),
                      new Person(true, "Emma", "Jones", "emma.jones@example.com"),
                      new Person(false, "Isabella", "Johnson", "isabella.johnson@example.com"),
                      new Person(true, "Ethan", "Williams", "ethan.williams@example.com"),
                      new Person(true, "Emma", "Jones", "emma.jones@example.com"),
                      new Person(false, "Isabella", "Johnson", "isabella.johnson@example.com"),
                      new Person(true, "Ethan", "Williams", "ethan.williams@example.com"),
                      new Person(true, "Emma", "Jones", "emma.jones@example.com"),
                      new Person(false, "Isabella", "Johnson", "isabella.johnson@example.com"),
                      new Person(true, "Ethan", "Williams", "ethan.williams@example.com"),
                      new Person(true, "Emma", "Jones", "emma.jones@example.com"),
                      new Person(false, "Isabella", "Johnson", "isabella.johnson@example.com"),
                      new Person(true, "Ethan", "Williams", "ethan.williams@example.com"),
                      new Person(true, "Emma", "Jones", "emma.jones@example.com"),
                      new Person(false, "Michael", "Brown", "michael.brown@example.com"));
       
              //"Invited" column
       
              TableColumn invitedCol = new TableColumn<Person, Boolean>();
              invitedCol.setText("Invited");
              invitedCol.setMinWidth(50);
              invitedCol.setCellValueFactory(new PropertyValueFactory("invited"));
              invitedCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
       
       
       
                  public TableCell<Person, Boolean> call(TableColumn<Person, Boolean> p) {
                      return new CheckBoxTableCell<Person, Boolean>();
                  }
       
              });
              
            
       
              //"First Name" column
       
              @SuppressWarnings("rawtypes")
                TableColumn firstNameCol = new TableColumn();
       
              firstNameCol.setText("First");
       
              firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
       
              //"Last Name" column
       
              TableColumn lastNameCol = new TableColumn();
       
              lastNameCol.setText("Last");
       
              lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
       
              //"Email" column
       
              TableColumn emailCol = new TableColumn();
       
              emailCol.setText("Email");
       
              emailCol.setMinWidth(200);
       
              emailCol.setCellValueFactory(new PropertyValueFactory("email"));
       
       
       
              //Set cell factory for cells that allow editing
       
              Callback<TableColumn, TableCell> cellFactory =
       
                      new Callback<TableColumn, TableCell>() {
       
       
       
                          public TableCell call(TableColumn p) {
       
                              return new EditingCell();
       
                          }
       
                      };
       
              emailCol.setCellFactory(cellFactory);
       
              firstNameCol.setCellFactory(cellFactory);
       
              lastNameCol.setCellFactory(cellFactory);
       
       
       
              //Set handler to update ObservableList properties. Applicable if cell is edited
       
              updateObservableListProperties(emailCol, firstNameCol, lastNameCol);
       
       
       
              TableView tableView = new TableView();
       
              tableView.setItems(data);
       
              //Enabling editing
              
              VBox vb = new VBox();
       
              tableView.setEditable(true);
       
              tableView.getColumns().addAll(invitedCol, firstNameCol, lastNameCol, emailCol);
              
              
          CheckBox cb =  new CheckBox("Select all");
          cb.selectedProperty().addListener(new ChangeListener<Boolean>() {
              public void changed(ObservableValue<? extends Boolean> ov,
                  Boolean old_val, Boolean new_val) {
                  if (new_val) {
                    for (  Person p : data ) {
                       p.invited.set(true);
                    } 
                  
                  }
          
                      
              }
          });
       
              
              vb.getChildren().addAll(cb, tableView);      
              root.getChildren().addAll(vb); 
       
          }
       
       
       
        @SuppressWarnings("unchecked")
      private void updateObservableListProperties(TableColumn emailCol, TableColumn firstNameCol,
       
                  TableColumn lastNameCol) {
       
              //Modifying the email property in the ObservableList
       
              emailCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {           
       
                  @Override public void handle(CellEditEvent<Person, String> t) {
       
                      ((Person) t.getTableView().getItems().get(
       
                              t.getTablePosition().getRow())).setEmail(t.getNewValue());
       
                  }
       
              });
       
              //Modifying the firstName property in the ObservableList
       
              firstNameCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {          
       
                  @Override public void handle(CellEditEvent<Person, String> t) {
       
                      ((Person) t.getTableView().getItems().get(
       
                              t.getTablePosition().getRow())).setFirstName(t.getNewValue());
       
                  }
       
              });
       
              //Modifying the lastName property in the ObservableList
       
              lastNameCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, String>>() {           
       
                  @Override public void handle(CellEditEvent<Person, String> t) {
       
                      ((Person) t.getTableView().getItems().get(
       
                              t.getTablePosition().getRow())).setLastName(t.getNewValue());
       
                  }
       
              });
       
          }    
       
           
       
          //Person object
       
          public static class Person {
       
              private BooleanProperty invited;
       
              private StringProperty firstName;
       
              private StringProperty lastName;
       
              private StringProperty email;
       
               
       
              private Person(boolean invited, String fName, String lName, String email) {
       
                  this.invited = new SimpleBooleanProperty(invited);
       
                  this.firstName = new SimpleStringProperty(fName);
       
                  this.lastName = new SimpleStringProperty(lName);
       
                  this.email = new SimpleStringProperty(email);
       
                  this.invited = new SimpleBooleanProperty(invited);
       
                   
       
                  this.invited.addListener(new ChangeListener<Boolean>() {
       
                      public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
       
                          System.out.println(firstNameProperty().get() + " invited: " + t1);
       
                      }
       
                  });            
       
              }
       
       
       
              public BooleanProperty invitedProperty() { return invited; }
       
        
       
              public StringProperty firstNameProperty() { return firstName; }
       
       
       
              public StringProperty lastNameProperty() { return lastName; }
       
        
       
              public StringProperty emailProperty() { return email; }
       
       
       
              public void setLastName(String lastName) { this.lastName.set(lastName); }
       
        
       
              public void setFirstName(String firstName) { this.firstName.set(firstName); }
       
         
       
              public void setEmail(String email) { this.email.set(email); }
       
          }
       
       
       
          //CheckBoxTableCell for creating a CheckBox in a table cell
       
          public static class CheckBoxTableCell<S, T> extends TableCell<S, T> {
       
             private final ComboBox comboBox;
             private final  ObservableList<String> data4 =  FXCollections.observableArrayList("Fun", "Ball", "Bat", "Car");
             
       
              private ObservableValue<T> ov;
       
       
       
              public CheckBoxTableCell() {
       
                  this.comboBox = new ComboBox();
      
                  comboBox.setItems(data4);
       
       
       
                  setAlignment(Pos.CENTER);
       
                  setGraphic(comboBox);
              } 
       
               
       
              @Override public void updateItem(T item, boolean empty) {
       
                  super.updateItem(item, empty);
                  
                  
       
                  if (empty) {
       
                      setText(null);
       
                      setGraphic(null);
       
                  } else {
       
                               setGraphic(comboBox);
                            
        
                      if (ov instanceof BooleanProperty) {
       
                          checkBox.selectedProperty().unbindBidirectional((BooleanProperty) ov);
       
                      }
       
                      
                      ov = getTableColumn().getCellObservableValue(getIndex());
                     
                      if (ov instanceof BooleanProperty) {
       
                          checkBox.selectedProperty().bindBidirectional((BooleanProperty) ov);
       
                      }
       
                  }
       
              }
       
          }
       
       
       
          // EditingCell - for editing capability in a TableCell
       
          public static class EditingCell extends TableCell<Person, String> {
       
              private TextField textField;
       
       
       
              public EditingCell() {
       
              }
       
              
       
              @Override public void startEdit() {
       
                  super.startEdit();
       
       
       
                  if (textField == null) {
       
                      createTextField();
       
                  }
       
                  setText(null);
       
                  setGraphic(textField);
       
                  textField.selectAll();
       
              }
       
              
       
              @Override public void cancelEdit() {
       
                  super.cancelEdit();
       
                  setText((String) getItem());
       
                  setGraphic(null);
       
              }
       
              
       
              @Override public void updateItem(String item, boolean empty) {
       
                  super.updateItem(item, empty);
       
                  if (empty) {
       
                      setText(null);
       
                      setGraphic(null);
       
                  } else {
       
                      if (isEditing()) {
       
                          if (textField != null) {
       
                              textField.setText(getString());
       
                          }
       
                          setText(null);
       
                          setGraphic(textField);
       
                      } else {
       
                          setText(getString());
       
                          setGraphic(null);
       
                      }
       
                  }
       
              }
       
       
       
              private void createTextField() {
       
                  textField = new TextField(getString());
       
                  textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
       
                  textField.setOnKeyReleased(new EventHandler<KeyEvent>() {                
       
                      @Override public void handle(KeyEvent t) {
       
                          if (t.getCode() == KeyCode.ENTER) {
       
                              commitEdit(textField.getText());
       
                          } else if (t.getCode() == KeyCode.ESCAPE) {
       
                              cancelEdit();
       
                          }
       
                      }
       
                  });
       
              }
       
       
       
              private String getString() {
       
                  return getItem() == null ? "" : getItem().toString();
       
              }
       
          } 
       
       
       
          @Override public void start(Stage primaryStage) throws Exception {
       
              init(primaryStage);
       
              primaryStage.show();
       
          }
       
          public static void main(String[] args) { launch(args); }
       
      }
       
        • 1. Re: ComboBox is refreshed inside a TableView
          dscarminiabielefeld
          Hey,
          i've played a bit around with your tableview, but i can't see the described effect.
          I can scroll the TableView without anything changing.
          Can you try to explain what exactly happens?
          • 2. Re: ComboBox is refreshed inside a TableView
            936633
            The scenario is simple. First select any value from the top ComboBox (e.g. car). Now just scroll down till end. Now scroll up again, you will see either the value in the first ComboBox is refreshed or moved down.

            On a separate note, please explain how can I fetch the value of the ComboBox from this tableview?
            • 3. Re: ComboBox is refreshed inside a TableView
              James_D
              The way that Cells work is (typically) to use only a single instance which is "rubber stamped" into each of the (potentially large number of) places in which it's used in the control.

              Consequently, in your code, there's only a single comboBox which is reused whenever it's needed. When you select something in the combo box, it changes the selected value for the one and only combo box. Any cell which is then redrawn is drawn with the new selected item. Of course, you and I have no idea how the redrawing is implemented and when and for which table cells this will be invoked during scrolling.

              Before "painting" the cell to the table, the updateItem(...) method is called, passing in the item representing the current row in the table which is being rendered. So what you need to do in the updateItem(...) method is set the selected item in the combo box appropriate for some data from the current row. So you need something like
              public static class CheckBoxTableCell<S, T> extends TableCell<S, T> {
                public void updateItem(T item, boolean empty) {
                  super.updateItem(item, empty);
                  if (empty) { ... } else {
                    setGraphic(comboBox);
              
                    // update combo box's selectedItem depending on values in item
              
                 }
                }
              }
              but it's not really clear how your combo box relates to the data you're displaying. It looks like you copied this from an example using check boxes (you still have the class name CheckBoxTableCell, and you still have the code that sets the checkbox state from the boolean property); you want to do something similar but set the combobox selected item from the value in the column.
              • 4. Re: ComboBox is refreshed inside a TableView
                936633
                Hi, James.

                Thank you very much for your reply. Yes you are right, I have used an existing example to keep things simple. What I am trying to do is making an Answer Sheet editor. The concept is that the combo box will contain Multiple choice Answers but the number of questions may vary in each Answer Sheet. So in this case I have to maintain the state or the Answer which is selected by the student. The complete Answer Sheet once saved can be used later to edit the answers.

                The problem which I am facing is that the number of combo boxes to be constructed is variable and every time I scroll down or up it is reconstructing or refreshing the combo box.

                Hope you can give better suggestion after the elaboration. Thanking you in anticipation.
                • 5. Re: ComboBox is refreshed inside a TableView
                  James_D
                  I think you need to start by defining your model; simply copying the Person class here is obviously not going to work. It's hard to incorporate the ComobBox until you have some data from which to set it's selected item. Once you've defined your data model, i.e. a class whose instances represent each row in the table, then it should be a lot easier to figure out how to write the updateItem(...) method.