title: JavaFX 2 TableView how to update cell when object is changed
body: <p>I'm creating a TableView to show information regarding a list of custom objects (EntityEvents).</p>
  
  <p>The table view must have 2 columns.
  First column to show the corresponding EntityEvent's name.
  The second column would display a button. The button text deppends on a property of the EntityEvent. If the property is ZERO, it would be "Create", otherwise "Edit".</p>
  
  <p>I managed to do it all just fine, except that I can't find a way to update the TableView line when the corresponding EntityEvent object is changed.</p>
  
  <p>Very Important: I can't change the EntityEvent class to use JavaFX properties, since they are not under my control. This class uses PropertyChangeSupport to notify listeners when the monitored property is changed.</p>
  
  <p>Note:
  I realize that adding new elements to the List would PROBABLY cause the TableView to repaint itself, but that is not what I need. I say PROBABLY because I've read about some bugs that affect this behavior.
  I tried using this approach to force the repaint, by I couldn't make it work.</p>
  
  <p>Does anyone knows how to do it?</p>
  
  <p>Thanks very much.</p>
  
  <p>Here is a reduced code example that illustrates the scenario:</p>
  
  <pre><code>import java.beans.PropertyChangeEvent;
  import java.beans.PropertyChangeListener;
  import java.beans.PropertyChangeSupport;
  
  import javafx.application.Application;
  import javafx.beans.property.ReadOnlyStringWrapper;
  import javafx.beans.value.ObservableValue;
  import javafx.collections.FXCollections;
  import javafx.collections.ObservableList;
  import javafx.event.ActionEvent;
  import javafx.event.EventHandler;
  import javafx.geometry.Insets;
  import javafx.scene.Scene;
  import javafx.scene.control.Button;
  import javafx.scene.control.ContentDisplay;
  import javafx.scene.control.TableCell;
  import javafx.scene.control.TableColumn;
  import javafx.scene.control.TableColumn.CellDataFeatures;
  import javafx.scene.control.TableView;
  import javafx.scene.layout.StackPane;
  import javafx.scene.layout.VBox;
  import javafx.scene.paint.Color;
  import javafx.stage.Stage;
  import javafx.util.Callback;
  
  public class Main extends Application {
  
      //=============================================================================================
      public class EntityEvent {
          private String m_Name;
          private PropertyChangeSupport m_NamePCS = new PropertyChangeSupport(this);
          private int m_ActionCounter;
          private PropertyChangeSupport m_ActionCounterPCS = new PropertyChangeSupport(this);
  
          public EntityEvent(String name, int actionCounter) {
              m_Name = name;
              m_ActionCounter = actionCounter;
          }
  
          public String getName() {
              return m_Name;
          }
  
          public void setName(String name) {
              String lastName = m_Name;
              m_Name = name;
              System.out.println("Name changed: " + lastName + " -&gt; " + m_Name);
              m_NamePCS.firePropertyChange("Name", lastName, m_Name);
          }
  
          public void addNameChangeListener(PropertyChangeListener listener) {
              m_NamePCS.addPropertyChangeListener(listener);
          }   
  
          public int getActionCounter() {
              return m_ActionCounter;
          }
  
          public void setActionCounter(int actionCounter) {
              int lastActionCounter = m_ActionCounter;
              m_ActionCounter = actionCounter;
              System.out.println(m_Name + ": ActionCounter changed: " + lastActionCounter + " -&gt; " + m_ActionCounter);
              m_ActionCounterPCS.firePropertyChange("ActionCounter", lastActionCounter, m_ActionCounter);
          }
  
          public void addActionCounterChangeListener(PropertyChangeListener listener) {
              m_ActionCounterPCS.addPropertyChangeListener(listener);
          }   
      }
  
      //=============================================================================================
      private class AddPersonCell extends TableCell&lt;EntityEvent, String&gt; {
          Button m_Button = new Button("Undefined");
          StackPane m_Padded = new StackPane();
  
          AddPersonCell(final TableView&lt;EntityEvent&gt; table) {
              m_Padded.setPadding(new Insets(3));
              m_Padded.getChildren().add(m_Button);
  
              m_Button.setOnAction(new EventHandler&lt;ActionEvent&gt;() {
                  @Override public void handle(ActionEvent actionEvent) {
                      // Do something
                  }
              });
          }
  
          @Override protected void updateItem(String item, boolean empty) {
              super.updateItem(item, empty);
              if (!empty) {
                  setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                  setGraphic(m_Padded);
                  m_Button.setText(item);
              }
          }
      }
  
      //=============================================================================================
      private ObservableList&lt;EntityEvent&gt; m_EventList;
  
      //=============================================================================================
      @SuppressWarnings("unchecked")
      @Override
      public void start(Stage primaryStage) {
          primaryStage.setTitle("Table View test.");
  
          VBox container = new VBox();
  
          m_EventList = FXCollections.observableArrayList(
                  new EntityEvent("Event 1", -1),
                  new EntityEvent("Event 2", 0),
                  new EntityEvent("Event 3", 1)
                  );
          final TableView&lt;EntityEvent&gt; table = new TableView&lt;EntityEvent&gt;();
          table.setItems(m_EventList);            
  
          TableColumn&lt;EntityEvent, String&gt; eventsColumn = new TableColumn&lt;&gt;("Events");
          TableColumn&lt;EntityEvent, String&gt; actionCol = new TableColumn&lt;&gt;("Actions");
          actionCol.setSortable(false);
  
          eventsColumn.setCellValueFactory(new Callback&lt;CellDataFeatures&lt;EntityEvent, String&gt;, ObservableValue&lt;String&gt;&gt;() {
              public ObservableValue&lt;String&gt; call(CellDataFeatures&lt;EntityEvent, String&gt; p) {
                  EntityEvent event = p.getValue();
                  event.addActionCounterChangeListener(new PropertyChangeListener() {
                      @Override
                      public void propertyChange(PropertyChangeEvent event) {
                          // TODO: I'd like to update the table cell information.
                      }
                  });
                  return new ReadOnlyStringWrapper(event.getName());
              }
          });
  
          actionCol.setCellValueFactory(new Callback&lt;TableColumn.CellDataFeatures&lt;EntityEvent, String&gt;, ObservableValue&lt;String&gt;&gt;() {
              @Override
              public ObservableValue&lt;String&gt; call(TableColumn.CellDataFeatures&lt;EntityEvent, String&gt; ev) {
                  String text = "NONE";
                  if(ev.getValue() != null) {
                      text = (ev.getValue().getActionCounter() != 0) ? "Edit" : "Create";
                  }
                  return new ReadOnlyStringWrapper(text);
              }
          });
  
          // create a cell value factory with an add button for each row in the table.
          actionCol.setCellFactory(new Callback&lt;TableColumn&lt;EntityEvent, String&gt;, TableCell&lt;EntityEvent, String&gt;&gt;() {
              @Override
              public TableCell&lt;EntityEvent, String&gt; call(TableColumn&lt;EntityEvent, String&gt; personBooleanTableColumn) {
                  return new AddPersonCell(table);
              }
          });
  
          table.getColumns().setAll(eventsColumn, actionCol);
          table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
  
          // Add Resources Button
          Button btnInc = new Button("+");
          btnInc.setOnAction(new EventHandler&lt;ActionEvent&gt;() {
              @Override
              public void handle(ActionEvent ev) {
                  System.out.println("+ clicked.");
                  EntityEvent entityEvent = table.getSelectionModel().getSelectedItem();
                  if (entityEvent == null) {
                      System.out.println("No Event selected.");
                      return;
                  }
                  entityEvent.setActionCounter(entityEvent.getActionCounter() + 1);
                  // TODO: I expected the TableView to be updated since I modified the object.
              }
          });
  
          // Add Resources Button
          Button btnDec = new Button("-");
          btnDec.setOnAction(new EventHandler&lt;ActionEvent&gt;() {
              @Override
              public void handle(ActionEvent ev) {
                  System.out.println("- clicked.");
                  EntityEvent entityEvent = table.getSelectionModel().getSelectedItem();
                  if (entityEvent == null) {
                      System.out.println("No Event selected.");
                      return;
                  }
                  entityEvent.setActionCounter(entityEvent.getActionCounter() - 1);
                  // TODO: I expected the TableView to be updated since I modified the object.
              }
          });
  
          container.getChildren().add(table);
          container.getChildren().add(btnInc);
          container.getChildren().add(btnDec);
  
          Scene scene = new Scene(container, 300, 600, Color.WHITE);        
  
          primaryStage.setScene(scene);
  
          primaryStage.show();
      }
  
      //=============================================================================================
      public Main() {
      }
  
      //=============================================================================================
      public static void main(String[] args) {
          launch(Main.class, args);
      }
  
  }
  </code></pre>