2

I've been messing around with javafx for a bit. and I came across something I cannot realy explain. I am trying to create an editable Tableview with string values. And everything worked, till I started to do biger tests. Before I tested with small lists, so it didn't exceed the list's visible limit. Though with the big test, the list was far more bigger than it could show in its window.

The problem is that I give a certain amount of cells at the begin of the Tableview another colour, which works perfectly, till you start scrolling down. it seems like the re-used cells aren't updated fast enough, since some have been given the wrong colour. When scrolling down, and thus updating more cells, the patern of 'wrong coloured cells' changes as well. The interesting part is, that these 'fails' are consistent!

(btw, Im checking the cells index by using this.getIndex() after which I call this.setStyle() with the proper colour syntax.)

The second thing I noticed, is when I start editing a cell, though dont stop editing and scroll down. There are multiple cells that are being edited at the same time. (also in a consistent patern) And its not a visual glitch!, I have debugged it, and it seems like the editing property isn't updated well enough either.

One last side note, I update the TableCells' graphics in the Cell#updateItem() method. (and for the editing part in the Cell#startEdit() and Cell#cancelEdit() as well.)

My question is, is this a bug? And if so, is there a workaround? Please leave a comment if you have any idea's/suggestions/solutions.

Thanks in advance!


EDIT 01:

First I need to appoligize, since the problem is ocuring with the tableView, not the listView. That being said, I made an example class which displays exactually the problem I tried to describe. If there are any suggestions/solutions, please let me know!

package testpacket;

import java.util.Arrays;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;


public class TableViewTest extends Application
{
    private static ObservableList<String> exampleList = FXCollections.observableArrayList();
    private static String[] testArray = new String[50];

    public static void main(String[] args)
    {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception
    {
        // basic ui setup
        AnchorPane parent = new AnchorPane();
        Scene scene = new Scene(parent);
        primaryStage.setScene(scene);

        //fill backinglist with data
        Arrays.fill(testArray, 0, testArray.length, "Test String");
        exampleList.setAll(Arrays.asList(testArray));

        //create a basic tableView
        TableView<String> listView = new TableView<String>();
        TableColumn<String, String> column = new TableColumn<String, String>();
        column.setCellFactory(E -> new TableCellTest<String, String>());
        column.setCellValueFactory(E -> new SimpleStringProperty(E.getValue()));

        // set listViews' backing list
        listView.getItems().addAll(exampleList);
        // listView.setItems(exampleList); it doesnt rely on the backing list, either way it is showing this bug.


        listView.getColumns().clear();
        listView.getColumns().add(column);
        parent.getChildren().add(listView);

        primaryStage.show();
    }

    public static class TableCellTest<S, T> extends TableCell<S, T>
    {

        @Override
        protected void updateItem(T item, boolean empty)
        {
            super.updateItem(item, empty);

            // dipslays cells' value
            this.setText((String)this.getItem());

            // checks cells index and set its color.
            if(this.getIndex() < 12)
                this.setStyle("-fx-background-color: rgba(253, 255, 150, 0.4);");
            else this.setStyle("");
        }
    }
}

EDIT 02:

@James_D

many thanks for your answer, indeed the duplicated items are the problem. Somehow javafx is checking whether the content has changed or not. unfortunatly the solution:
testArray[i] = new String("Test String");
does not work, which is quit unfortunate because my system depends heavily on duplicated entries. Trying to make them unique would be almost impossible to handle perfectly.

So I really hope there is a possibility to overwrite the 'is equal check' somehow. I have been diging a bit, and it seems like all those methods/fields are private. So changing that is kinda impossible to do, unless I recreate a bunch of javafx classes.

So please if there are still suggestion/solutions for this one, feel free to post them!

Onto the next problem though, the editing property isnt updated well enough either. In the onUpdateItem() method I check if the current cell is editing and keep updating that cell so it stays in editing mode, even if you scroll down (and thus scrolling the editing index out of the window). I made this example so you can see for yourself.

Note that when you remove the onUpdateItem() part of the editing system, the cell will exit edit mode after it has been scrolled outside the viewWindow.

package testpacket;

import java.util.Arrays;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;


public class TableViewTest extends Application
{
    private static ObservableList<String> exampleList = FXCollections.observableArrayList();
    private static String[] testArray = new String[50];

    public static void main(String[] args)
    {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception
    {
        // basic ui setup
        AnchorPane parent = new AnchorPane();
        Scene scene = new Scene(parent);
        primaryStage.setScene(scene);

        //fill backinglist with data
        for(int i = 0 ; i < testArray.length; i++)
            testArray[i] = "Test String" + i;

//      Arrays.fill(testArray, 0, testArray.length, new String("Test String"));
        exampleList.setAll(Arrays.asList(testArray));

        //create a basic tableView
        TableView<String> listView = new TableView<String>();
        TableColumn<String, String> column = new TableColumn<String, String>();
        column.setCellFactory(E -> new TableCellTest<String, String>());
        column.setCellValueFactory(E -> new SimpleStringProperty(E.getValue()));
        column.setEditable(true);

        // set listViews' backing list
        listView.getItems().addAll(exampleList);
        listView.setEditable(true);
//      listView.setItems(exampleList); it doesnt rely on the backing list, either way it is showing this bug.


        listView.getColumns().clear();
        listView.getColumns().add(column);
        parent.getChildren().add(listView);

        primaryStage.show();
    }

    public static class TableCellTest<S, T> extends TableCell<S, T>
    {

        protected TextField textField;

        @Override
        public void startEdit()
        {
            super.startEdit();

            if (!isEditable()
                    || (this.getTableView() != null && !this.getTableView().isEditable())
                    || (this.getTableColumn() != null && !this.getTableColumn().isEditable()))
                return;

            if(this.textField == null)
                this.createTextField();

            this.textField.setText((String)this.getItem());
            this.setGraphic(this.textField);
            this.textField.selectAll();
            this.setText(null);
        }

        @Override
        public void cancelEdit()
        {
            super.cancelEdit();
            if (!isEditing())
                return;

            this.setText((String)this.getItem());
            this.setGraphic(null);
        }

        @Override
        protected void updateItem(T item, boolean empty)
        {
            super.updateItem(item, empty);

            if(empty || item == null)
            {
                this.setText(null);
                this.setGraphic(null);
            }
            else
            {
                // I tried this option to check whether a cell represents the correct item, though it doesnt work
                if(this.isEditing())// && this.getTableView().getEditingCell() != null && this.getTableView().getEditingCell().equals(this))
                {
                    if(this.textField != null)
                        this.textField.setText((String)this.getItem());
                    this.setText(null);
                    this.setGraphic(this.textField);
                }
                else
                {
                    this.setText((String)this.getItem());
                    this.setGraphic(null);
                }
            }
        }

        @SuppressWarnings("unchecked")
        public void createTextField()
        {
            this.textField = new TextField();
            this.textField.setOnKeyReleased((EventHandler<? super KeyEvent>)E -> {
                if(E.getCode() == KeyCode.ENTER)
                {
                    this.setItem((T)this.textField.getText());
                    this.commitEdit(this.getItem());
                }
                else if(E.getCode() == KeyCode.ESCAPE)
                    this.cancelEdit();
            });
        }
    }
}

Edit 03:

Unlike It mentioned in the class, there is a workaround for this one. Note that this works only for 'single cell editing mode'! (as it is the only mode that is supported by the TableView) The solution is as follows:

if(this.isEditing() && this.getTableView().getEditingCell() != null
     && this.getTableView().getEditingCell().getTableColumn().getCellData(this.getTableView().getEditingCell().getRow()).equals(this.getItem()))
{
    ... do stuff
}

Though unfortunatly it doesn't solve the index problem. This thread is still open for suggestions/solutions.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
n247s
  • 1,898
  • 1
  • 12
  • 30
  • You mention a code change in `ListCell`. Do these phenomena happen with the default implementation? If not, if may be impossible to answer unless you post your code. – Itai Jan 27 '16 at 20:36
  • I have overwritten 3 methods, `startEdit`, `cancelEdit` and `updateItem`. All of them are calling its superclass' method. after that I just do some handling for visuals and some updating to the backing system. Though these bugs appear in a passive state . e.g. after initializing scrolling down, and opening a edit field (e.g. calling startEdit()). I have searched for a bit, and the way I implement editing is simular on tutorials I found about _'how to create an editable ListView'_. Is this enough 'default' behavior? or do I need to try something else? – n247s Jan 27 '16 at 21:37
  • you can't have _identical_ objects in the list - that's bound to blow at some place. To my knowledge, there's no way around, it's simply not supported. Requiring them sounds a bit pathological - care to elaborate to the why? – kleopatra Jan 28 '16 at 13:33
  • Anyway, what exactly is the open part of the question and what exactly is the part that's solved (and how)? Got lost a bit in your edits :-) You might consider separating out the different parts into different questions and self-answer the one that's solved. – kleopatra Jan 28 '16 at 13:35
  • also, with your setup you can't edit the data (you can start an edit always, but it's never saved to the backing array) - you would need a commitHandler to apply the edit to the array – kleopatra Jan 28 '16 at 13:45
  • I don't really understand what you're trying to do with cell editing. You said you're trying to keep the cell in editing mode, even if it is scrolled out of view: that just won't do anything sensible, even if you achieve it. When the cell is scrolled out of view, it becomes eligible for reuse, so it has the potential to be reused to display a different item. If you keep that cell in editing mode, you basically move the editing to an arbitrary item when the cell is reused, which is presumably not what you want. – James_D Jan 28 '16 at 14:24
  • It might be unclear the way I explained it indeed. what I tried to do is keep an Item in editing mode. not a cell in particular. meaning when the cell is used for another item, it needs to be set to a non editing state with the right display value. and once its scrolling back to the editing item, it needs to revert back to editing mode. I found a solution luckly, I hope that example will explain much more than I can with word :D. Thanks for the help, it pointed me in the right direction! – n247s Jan 28 '16 at 14:33
  • @James_D keeping a cell editing when it's scrolled out is a reasonable requirement, IMO: that's what Swing does. Not being supported might be regarded a technicality/missing feature in fx :-) – kleopatra Jan 28 '16 at 14:34
  • Persisting the editing state of an *item* (not a cell) during scrolling is a reasonable (and probably desirable) requirement. I'm just playing around with the default `TextFieldTableCell` and it seems it actually persists the editing state of the *cell* during scrolling, which results in the buggy behavior I described above (the cell stays in editing mode even when reused, which is horrible). That's a bug... – James_D Jan 28 '16 at 14:49
  • Though it is easilly solved whith updating the specific cell in the `onUpdateItem()` method. (described in the example I posted). Appart from that, I think you are mixing a different type of TableCell with the normal cells (e.g. TextFieldTableCell extends TableCell, and will always display a textField, no matter what). So maybe it is intendend behavior, but not the kind we are looking for – n247s Jan 28 '16 at 15:11
  • `TextFieldTableCell` does not always display a text field: it only displays a text field if the cell is being edited. Otherwise it looks like a label. I made fairly drastic updates to my answer to address both the highlighting on identical items (don't use identical items) and the editing. I think that approach is a lot cleaner that the one you have. – James_D Jan 28 '16 at 15:21
  • @James_D nitpicking: the item has no editing state *grins. Introducing some temporary storage ItemInEditingState might be solution, as might some locking marker on cell to not re-use until the edit is really terminated might be another. All technicalities, that app coders shouldn't be bothered with. If you found a bug, you might consider filing it. Unrelated: please note re-vitalized discussion in https://bugs.openjdk.java.net/browse/JDK-8089514 (biased me drumming up interest :) – kleopatra Jan 28 '16 at 15:48
  • OK, rephrase: the aim is to persist which item is being edited as state (of something, should be the tableview, but I don't want to subclass it...), as opposed to which cell is being edited. Agree this should all be baked-in and not the responsibility of the developer. – James_D Jan 28 '16 at 16:08

2 Answers2

1

The highlighting problem arises because you have identical elements in your table: i.e. you have table.getItems().get(0)==table.getItems().get(1) etc. If you change your table initialization so the items are not identical (but still equal):

// Arrays.fill(testArray, 0, testArray.length, "Test String");
for (int i = 0 ; i < testArray.length; i++) 
    testArray[i] = new String("Test String");

then the highlighting works as expected.

To persist the editing state during scrolling (including the case where the editing cell is scrolled out of view), you need to be careful to distinguish between cells and items. You want to persist the item that is being edited: since the cell may be reused during scrolling this means you need some way of keeping track of the item being edited during scrolling; then in the updateItem(...) method you need to check this and "go into editing mode" if necessary. Probably the easiest way to track the item being edited is using the row index.

The following example shows a way to do this:

import java.util.Arrays;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import javafx.util.Callback;


public class TableViewTest extends Application
{

    public static void main(String[] args)
    {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception
    {
        // basic ui setup
        AnchorPane parent = new AnchorPane();
        Scene scene = new Scene(parent);
        primaryStage.setScene(scene);



        //create a basic tableView
        TableView<String> table = new TableView<String>();
        table.setEditable(true);
        TableColumn<String, String> column = new TableColumn<String, String>();
        column.setCellFactory(new CellFactory());
        column.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue()));

        column.setOnEditCommit(e -> {
            int row = e.getTablePosition().getRow() ;
            table.getItems().set(row, e.getNewValue());
        });

        for (int i = 0 ; i < 50; i++) {
            table.getItems().add(new String("Test"));
        }

        table.getColumns().add(column);
        parent.getChildren().add(table);

        primaryStage.show();
    }

    public static class CellFactory implements Callback<TableColumn<String, String>, TableCell<String, String>> {

        private int editingIndex = -1 ;

        @Override
        public TableCell<String, String> call(TableColumn<String, String> param) {
            return new TableCell<String, String>() {

                private TextField textField = new TextField() ;

                {
                    textField.setOnAction(e -> commitEdit(textField.getText()));
                    textField.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
                        if (e.getCode() == KeyCode.ESCAPE) {
                            cancelEdit();
                        }
                    });
                    setGraphic(textField);
                    setContentDisplay(ContentDisplay.TEXT_ONLY);
                }

                @Override
                protected void updateItem(String item, boolean empty)
                {
                    super.updateItem(item, empty);

                    if (getIndex() != -1 && getIndex() == editingIndex) {
                        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                    } else {
                        setText(empty ? null : item);
                        setContentDisplay(ContentDisplay.TEXT_ONLY);
                    }

                    // checks cells index and set its color.
                    if(this.getIndex() >= 0 && getIndex() < 12)
                        this.setStyle("-fx-background-color: rgba(253, 255, 150, 0.4);");
                    else this.setStyle("");
                }

                @Override
                public void startEdit() {
                    editingIndex = getIndex();
                    super.startEdit();
                    textField.setText(getItem());
                    setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                }

                @Override
                public void commitEdit(String newValue) {
                    editingIndex = -1 ;
                    super.commitEdit(newValue);
                    setContentDisplay(ContentDisplay.TEXT_ONLY);
                }

                @Override
                public void cancelEdit() {
                    setText(getTableView().getItems().get(editingIndex));
                    editingIndex = -1 ;
                    super.cancelEdit();
                    setContentDisplay(ContentDisplay.TEXT_ONLY);
                }

            };
        }

    }

}
James_D
  • 201,275
  • 16
  • 291
  • 322
  • Many thanks for your answer! I updated the main post with a lengthy reply. I hope that you (or someone else) can help me solve this problem further more. – n247s Jan 28 '16 at 13:04
0

@kleopatra This is the post/answer that you requested :D.

Both questions have been answered at this point. And i will explain both solutions below, but first it is confirmed that a Table view is not capable of handling duplicated entries. which is rather unfortunate if you consider replicating 'Windows Excel like' behavior inside a TableView for example.

Anyways here is the solution for the index updating glitch: never ever use primitive data types as base type if you are going to duplicate entries! use an observable value instead! At the bottom there is an example class. The class wraps an observableString inside a ObservableProperty. That will solve the index problem!

For the editing part. As long as you only support 'Single cell editing', this part will enable the ability to scroll the editing cell out of view, without canceling the editing state of that specific cell. (this should go inside the onUpdateItem() method)

if(this.isEditing() && this.getTableView().getEditingCell() != null 
                        && this.getTableView().getEditingCell().getTableColumn().getCellData(
                                this.getTableView().getEditingCell().getRow()).equals(this.getItem()))
    {
        //...update this cell to view its editing state
    }

Again, this solution only works if dupedObjectA.equals(dupedObjectB); returns false, which wouldn't be the case if a primitive data type would have been used.

Here is a full example class:

package testpacket;

import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;


public class TableViewTest extends Application
{
    //Backing list of this TableView. I personally use a custom made Maps' entrySet, which is capable of handling duplicated entries.
    //So it would look like:
    //private static ObservableList<MapEntry<SimpleStringProperty>> exampleList;
    private static ObservableList<ObservableValue<SimpleStringProperty>> exampleList = FXCollections.observableArrayList();

    public static void main(String[] args)
    {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception
    {
        // basic ui setup
        AnchorPane parent = new AnchorPane();
        Scene scene = new Scene(parent);
        primaryStage.setScene(scene);

        //fill backinglist with duplicated data
        for(int i = 0 ; i < 50; i++)
            exampleList.add(new SimpleObjectProperty<SimpleStringProperty>(new SimpleStringProperty("Hello Test")));


        //create a basic tableView
        TableView<ObservableValue<SimpleStringProperty>> listView = new TableView<ObservableValue<SimpleStringProperty>>();
        listView.setEditable(true);

        TableColumn<ObservableValue<SimpleStringProperty>, SimpleStringProperty> column = new TableColumn<ObservableValue<SimpleStringProperty>, SimpleStringProperty>();
        column.setCellFactory(E -> new TableCellTest<ObservableValue<SimpleStringProperty>, SimpleStringProperty>());
        column.setCellValueFactory(E -> E.getValue());
        column.setEditable(true);

        // set listViews' backing list
        listView.setItems(exampleList);


        listView.getColumns().clear();
        listView.getColumns().add(column);
        parent.getChildren().add(listView);

        primaryStage.show();
    }

    public static class TableCellTest<S, T> extends TableCell<S, T>
    {
        // The editing textField. This can be static as only one cell can be edited at the same time.
        protected static TextField textField;

        @Override
        public void startEdit()
        {
            super.startEdit();

            // The same check as in the super#startEdit(), so technically you can call super#startEdit() after this check.
            if (!isEditable()
                    || (this.getTableView() != null && !this.getTableView().isEditable())
                    || (this.getTableColumn() != null && !this.getTableColumn().isEditable()))
                return;

            if(textField == null)
                this.createTextField();


            textField.setText(((SimpleStringProperty)this.getItem()).get());
            this.setGraphic(textField);
            textField.selectAll();
            this.setText(null);
        }

        @Override
        public void cancelEdit()
        {
            // The same check as in the super#cancelEdit()
            if (!this.isEditing())
                return;

            super.cancelEdit();

            this.setText(((SimpleStringProperty)this.getItem()).get());
            this.setGraphic(null);
        }

        @Override
        protected void updateItem(T item, boolean empty)
        {
            super.updateItem(item, empty);

            // checks cells index and set its color.
            if(this.getIndex() < 12)
                this.setStyle("-fx-background-color: rgba(253, 255, 150, 0.4);");
            else this.setStyle("");

            // Checks if visuals need an update.
            if(empty || item == null)
            {
                this.setText(null);
                this.setGraphic(null);
            }
            else
            {
                // These checks are needed to make sure this cell is the specific cell that is in editing mode.
                // Technically this#isEditing() can be left out, as it is not accurate enough at this point.
                if(this.isEditing() && this.getTableView().getEditingCell() != null 
                        && this.getTableView().getEditingCell().getTableColumn().getCellData(
                                this.getTableView().getEditingCell().getRow()).equals(this.getItem()))
                {
                    //change to TextField
                    this.setText(null);
                    this.setGraphic(textField);
                }
                else
                {
                    //change to actual value
                    this.setText(((SimpleStringProperty)this.getItem()).get());
                    this.setGraphic(null);
                }
            }
        }

        @SuppressWarnings("unchecked")
        public void createTextField()
        {
            textField = new TextField();
            textField.setOnKeyReleased((EventHandler<? super KeyEvent>)E -> {
                if(E.getCode() == KeyCode.ENTER)
                {
                    this.setItem((T)new SimpleStringProperty(textField.getText()));
                    this.commitEdit(this.getItem());
                }
                else if(E.getCode() == KeyCode.ESCAPE)
                    this.cancelEdit();
            });
        }
    }
}

I thank you all for thinking with me, fortunatly there is a rather easy workaround for both issues. I hope they will support duplicated entries in the future though!

n247s
  • 1,898
  • 1
  • 12
  • 30
  • a couple of comments: a) your data no longer contains duplicates (== identical objects), so it's supposed to work :-) b) don't understand (and didn't in your question) what you mean by _single cell editing_? As opposed to _multiple cell editing_ which would be ...? c) setting the item in the textFields keyHandler looks horrible .. I expect it to blow into your face sooner or later! d) still don't see - lazy me didn't run it, though - how you commit the edit back to your data: without the default binding, I would expect you to need a edit handler on the table – kleopatra Jan 28 '16 at 16:06
  • @kleopatra A) indeed because this wraps the duplicated data in an other object. thats the workaround ;). b) if you dont check if there is a cell editing already before calling startEdit. you can set the editing state (visually) to multiple cells. e.g. multiple cells display a TextField. that is what I refer to as 'multiple cell editing' c) it looks ugly? it works for sure :P. alternatives are welcome though! d) its commited at the point you just mentioned. inside the KeyHandler. – n247s Jan 28 '16 at 16:25
  • b) ahh ... but that would be a bug, wouldn't it :-) c) hmm ... no, it doesn't work or I still don't fully understand your requirement: start editing, scroll the cell out of the view port .. and the edit is cancelled. I thought you wanted to keep it in editing state? Even it would work: by-passing the usual workflow (inside the cell) by replacing stuff under its radar is a loud call for desaster. Think at me when it hits d) ... interesting, wouldn't have expected a simple property working (but does) - thanks! – kleopatra Jan 28 '16 at 16:48
  • I didnt fully understand point c. though if that is the reply on point b (multiple cell editing etc.). what you describe is exactually what I meant. with the 'single cell editing'. technically one cell can be edited at the same time. though since I manually set a TextField as graphic, it is possible to let multiple cells being edited (visually) at the same time. Since the commit change is handled inside the keyHandler of the TextField. every edit would be handled perfectly (trust me on that one :P). though it doesnt work when you scroll down, unless I save editing indexes, just like James_D do – n247s Jan 28 '16 at 17:16