This discussion is archived
6 Replies Latest reply: Feb 26, 2013 5:52 AM by PhHein RSS

TableView auto update issue in 2.1 and 2.2

zmirc Newbie
Currently Being Moderated
Hi, guys!

Initial status: I have a TableView with some items inside. I update an item (an User object), I remove it from table and then I put it again at the same index.

Working code in 2.0.2:
// Gets selected item
User selectedUser = (User) table.getSelectionModel().getSelectedItem();

// Updates the User object to show the new data in the table
selectedUser.setFirstName(fNameTF.getText());
selectedUser.setLastName(lNameTF.getText());
selectedUser.setRank((String) (rankCB.getSelectionModel().getSelectedItem()));
selectedUser.setPassword(passTF.getText());

// Updates table data.
// First gets the user Object index in the items list,
// then removes the user object from the table list,
// then adds the updated user object at the same index it was before.
int userObjectIndexInItemsList = table.getItems().indexOf(selectedUser);
table.getItems().remove(table.getItems().indexOf(selectedUser));
table.getItems().add(userObjectIndexInItemsList, selectedUser);
Here is my problem: In version 2.1 and 2.2 (b06) this doesn't work anymore. The table doesn't show the modified data. I also tried to getTableItems in tableData (new FXCollections.observableArrayList()), then table.setItems(null), then table.setItems(tableData), but it didn't work.
So, the problem is that the FXCollections.observableArrayList() (which was used to populate the table at the beginning) doesn't report anymore to the table when a change is made.

Possible, but unwanted sollutions:
1 - Total recreation of the table.
2 - Total recreation of the tableItems (create a new observable list with new objects).

Any suggestions?
I don't want to recreate table data every time a change is made, especially because it's based on a database.
  • 1. Re: TableView auto update issue in 2.1 and 2.2
    JohnHendrikx Pro
    Currently Being Moderated
    JavaFX has a fundamentally different way of handling these kinds of updates, which may make it tricky to implement this in your current system.

    In short, the way updates work in JavaFX for the ListView, TreeView and TableView is this:

    Each type of View is made out of Cells. The amount of Cells is usually pretty close to the amount of visible rows and each Cell could be thought of to represent one Row.

    Each Cell is basically a small piece of UI, that adapts itself to whatever should be displayed at the given row at that time. The updateItem method is called on these Cells to associate them with an underlying Item from the ObservableList (your model).

    Let's say your "Items" in the Model are Person objects with a name. A cell implementation might render this as follows:
      private static final class PersonTableCell extends TableCell<Person, Person> {
    
        @Override
        protected void updateItem(final Person person, boolean empty) {
          super.updateItem(mediaNode, empty);
    
          if(!empty) {
            setText(person.getTitle());
          }
        }
      }
    The example above will also have the same update problem as your code, that is, if you change the Person object in your model the Cell will not reflect the changed name.

    In JavaFX to let the Cell know about the change, you have to add a listener to the "text" property of your Person object. This assumes that the properties of a Person object are JavaFX style properties. This is accomplished like this (using a binding which uses a listener internally):
      private static final class PersonTableCell extends TableCell<Person, Person> {
    
        @Override
        protected void updateItem(final Person person, boolean empty) {
          super.updateItem(mediaNode, empty);
    
          textProperty().unbind();
    
          if(!empty) {
            textProperty().bind(person.titleProperty());
          }
        }
      }
    The above will update correctly automatically whenever a Person's title property changes.

    However, this means that you will have to change your Person object to use JavaFX style properties. You donot always have this luxury. However, there are other options. You could for example only support a "changedProperty" in the Person class, and have the Cells listen for to this, something like this:

      private static final class PersonTableCell extends TableCell<Person, Person> {
        private Person oldPerson;
        private InvalidationListener listener;
    
        @Override
        protected void updateItem(final Person person, boolean empty) {
          super.updateItem(mediaNode, empty);
    
          if(oldPerson != null) {
            oldPerson.removeListener(listener);
            oldPerson = null;
          }
    
          if(!empty) {
            listener = new InvalidatonListener() {
              public void invalidated(Observable o) {
                 setText(person.getTitle());
              }
            };
            person.addListener(listener);
            setText(person.getTitle());
          }
        }
      }
    Now each time you want to trigger a "change", you call a method on the Person object that triggers an Invalidation event. All the cells that are listening to this Person object will be notified and update themselves.

    If you donot want to change the Person class itself, you could also do this with a wrapper. You'll have to experiment a bit.
  • 2. Re: TableView auto update issue in 2.1 and 2.2
    zmirc Newbie
    Currently Being Moderated
    Thank you for answering.
    It seems that you've worked a little bit for this reply.

    The thing is that I already have 8 tables with different type of objects/items.
    It's an university project of about 7000 lines, and I have just a little bit of time until the deadline. So, it's quite unhandy to modify now.

    The interesting part is that what I was doing worked perfectly in 2.0.3. Well, this is why I was doing like that, of course.
    Do you know why they changed the functionality in 2.1 and 2.2?
  • 3. Re: TableView auto update issue in 2.1 and 2.2
    JohnHendrikx Pro
    Currently Being Moderated
    I don't understand either why this doesn't work anymore. I donot know why it was changed (perhaps it's a bug).

    Have you considered subclassing ObservableListWrapper to make it report the change again? Or write your own implementation of ObservableList (using the one from JavaFX) as a guide?

    This is probably less work than modifying all your old code.
  • 4. Re: TableView auto update issue in 2.1 and 2.2
    zmirc Newbie
    Currently Being Moderated
    After a few hours of "playing", I've figured out that the ObserverListWrapper is smarter in >2.1.
    If you try to remove the object at pos x and then try to put it back at the same position, the list figures out that it's the same thing, so it doesn't do anything. So, I stopped considering this a bug. Now I see it as an improvement.

    There is still a possible bug, or let's just say that it's a necessary functionality: I think the ObservableListWrapper should listen not only when an object (table item wrapper) is added or removed, but also when object's fields are modified. Otherwise, it's not really a fully functional and useful ObservableList.

    But, if I send a duplicate of the object and I put it at position x (as the original one was), the list sees another object, so it fires the "NonIterableChange.SimpleAddChange".

    So, that's the easiest and most efficient way to deal with table refresh at this moment. There are some other ways (for example the option you wrote above), but they take more time and understanding to implement.

    Just by implementing method "public WRAPPER_NAME duplicate()" in my wrapper class and by passing it to the table.getItems().set(indexPosition, modifiedObject.duplicate()), the table will show correctly.

    In this way I avoid recreating the table and making DB query, and also it looks smarter. Not very much, but anyway its smarter than creating the whole thing again.
  • 5. Re: TableView auto update issue in 2.1 and 2.2
    B. Soidik Newbie
    Currently Being Moderated
    Hi!
    This thread is so old but when I folow instructions I have this exception

    java.lang.ClassCastException: java.lang.String cannot be cast to testappl.Person
  • 6. Re: TableView auto update issue in 2.1 and 2.2
    PhHein Guru Moderator
    Currently Being Moderated
    Exactly, it is old. Don't hijack old threads. Please start a new thread and include your code and links to relevant threads.

    Moderator action: I'm locking this thread.

Legend

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