Is it possible to disable editable table columns on a row basis in JavaFX8?

笑着哭i 提交于 2019-12-08 06:17:17

问题


I have a use case which I would assume is pretty standard, however I haven't been able to find an example on exactly how to do this, or if it's possible.

Let's assume I have the following TableView

First Name    Last Name    Street    NewRecord

Tom           Smith        Main St.     Yes
Mike          Smith        First St.    No

In this case, the grid should have the first three cells editable since the record is new, however when the record is not new then the Last Name cell should be disabled.

I tried this in the CellFactory and RowFactory - but haven't seen a way to accomplish this.

Thanks for your help.


回答1:


The easiest way to do this is with a third-party binding library: ReactFX 2.0 has this functionality built-in, as described here. Using that you can do

TableColumn<Person, String> lastNameColumn = new TableColumn<>("Last Name");
lastNameColumn.setEditable(true);
lastNameColumn.setCellFactory(tc -> {
    TableCell<Person, String> cell = new TextFieldTableCell<>();
    cell.editableProperty().bind(
        // horrible cast needed because TableCell.tableRowProperty inexplicably returns a raw type:
        Val.flatMap(cell.tableRowProperty(), row -> (ObservableValue<Person>)row.itemProperty())
           .flatMap(Person::newRecordProperty)
           .orElseConst(false));
    return cell ;
});

(assumes a Person table model object with the obvious JavaFX properties and methods).

Without the library, you need a pretty miserable nested list of listeners:

TableColumn<Person, String> lastNameColumn = new TableColumn<>("Last Name");
lastNameColumn.setEditable(true);
lastNameColumn.setCellFactory(tc -> {
    TableCell<Person, String> cell = new TextFieldTableCell<>();
    ChangeListener<Boolean> newRecordListener = (obs, wasNewRecord, isNewRecord) -> updateEditability(cell);
    ChangeListener<Person> rowItemListener = (obs, oldPerson, newPerson) -> {
        if (oldPerson != null) {
            oldPerson.newRecordProperty().removeListener(newRecordListener);
        }
        if (newPerson != null) {
            newPerson.newRecordProperty().addListener(newRecordListener);
        }
        updateEditability(cell);
    };
    ChangeListener<TableRow> rowListener = (obs, oldRow, newRow) -> {
        if (oldRow != null) {
            ((ObservableValue<Person>)oldRow.itemProperty()).removeListener(rowItemListener);
            if (oldRow.getItem() != null) {
                ((Person)oldRow.getItem()).newRecordProperty().removeListener(newRecordListener);
            }
        }
        if (newRow != null) {
            ((ObservableValue<Person>)newRow.itemProperty()).addListener(rowItemListener);
            if (newRow.getItem() != null) {
                ((Person)newRow.getItem()).newRecordProperty().addListener(newRecordListener);
            }
        }
        updateEditability(cell);
    };
    cell.tableRowProperty().addListener(rowListener);
    return cell ;
});

and then

private void updateEditability(TableCell<Person, String> cell) {
    if (cell.getTableRow() == null) {
        cell.setEditable(false);
    } else {
        TableRow<Person> row = (TableRow<Person>) cell.getTableRow();
        if (row.getItem() == null) {
            cell.setEditable(false);
        } else {
            cell.setEditable(row.getItem().isNewRecord());
        }
    }
}

An alternative using a "legacy style" API is

TableColumn<Person, String> lastNameColumn = new TableColumn<>("Last Name");
lastNameColumn.setEditable(true);
lastNameColumn.setCellFactory(tc -> {
    TableCell<Person, String> cell = new TextFieldTableCell<>();
    cell.editableProperty().bind(
        Bindings.selectBoolean(cell.tableRowProperty(), "item", "newRecord"));
    return cell ;
});

I dislike this option, because it lacks any type safety (or indeed any compiler checks at all), and additionally in some earlier versions of JavaFX would generate almost endless warning messages if any of the properties in the "chain" had null values (which they will, frequently, in this case). I believe the latter issue is fixed, but the ReactFX version of this is far better, imho.




回答2:


You can always stop an editing by setting event handler to check if the action is legal.

        columnName.setOnEditStart(
            new EventHandler<CellEditEvent<ItemClass, String>>(){
                @Override
                public void handle(CellEditEvent<ItemClass, String> event) {
                    if(event.getTableColumn().getCellData(3).compareTo("yes") == 0) {
                        event.getTableView().edit(-1, null); 
                       //this prevents the editing in "progress"
                    }                       
                }   
            }
    );


来源:https://stackoverflow.com/questions/37010431/is-it-possible-to-disable-editable-table-columns-on-a-row-basis-in-javafx8

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!