我希望有一个示例,它解释了我如何取消编辑并重置已编辑但未能通过验证的TableView中特定单元格的旧值。有关详细信息,请参阅以下代码。
tcAantalDagen.setOnEditCommit(cell -> {
int dagen = Integer.parseInt(cell.getNewValue());
if (Integer.parseInt(cell.getNewValue()) < 1 || Integer.parseInt(cell.getNewValue()) > 31) {
// This shows an Alert Dialog
Main.toonFoutbericht("Het item kan maar tussen 1 en 31 dagen uitgeleend worden");
// The "return;" is successful in canceling the passing through of the new value of the cell to the backend code,
// but in the TableView the new value is still set in the cell, which can confuse the user
return;
}
}
单元格值设置如下:
// getAantalDagen() returns an Integer
tcAantalDagen.setCellValueFactory(cell -> {
return new ReadOnlyObjectWrapper<String>(Integer.toString(cell.getValue().getAantalDagen()));
});
// Make the cells of the tcAantalDagen column editable
tcAantalDagen.setCellFactory(TextFieldTableCell.forTableColumn());
// The table needs to be set to editable too for the above column to work
tblUitleningen.setEditable(true);
答案 0 :(得分:3)
我玩了一会儿。底线是commitEdit
中的默认TableCell
方法,当您在TextFieldTableCell
的文本字段中提交更改时调用该方法,在表格单元格上调用updateItem(...)
有了新的价值。这就是为什么即使你没有在模型中改变它,单元格中的值也会发生变化。
为了防止这种情况,您需要自己实现表格单元格,这不是太难。最简单的实现可能只是使用文本字段表单元格并覆盖updateItem(...)
来检查值的有效性。像
tcAantalDagen.setCellFactory(col -> new TextFieldTableCell<T, Integer>(new IntegerStringConverter()) {
@Override
public void updateItem(Integer item, boolean empty) {
if (empty) {
super.updateItem(item, empty) ;
} else {
// if out of range, revert to previous value:
if (item.intValue() < 1 || item.intValue() > 31) {
item = getItem();
}
super.updateItem(item, empty);
}
}
});
应该有用(虽然我还没有测试过)。显然用表中任何类型的项替换T
。请注意,我在此处使用Integer
作为列类型,如果您为TextFieldTableCell
提供了适当的转换器,则可以执行此操作。您可以将单元格值工厂修改为
tcAantalDagen.setCellValueFactory(cellData -> cellData.getValue().aantalDagenProperty().asObject());
但是......一旦你遇到了实现表格单元格的所有麻烦,你也可以提供一个根本不允许用户输入无效值的单词,这是一个很大的问题。更好的用户体验imho。您可以通过为使用仅TextFormatter
的单元格创建文本字段来执行此操作,该{{3}}只会否决无效值。你必须要小心这些,因为你想允许部分编辑的值(所以一般来说,它不足以只允许有效的值,因为每次文本都会检查它们变化,而不仅仅是提交)。在这种情况下,唯一的意思是你应该在文本字段中允许空字符串(否则用户在编辑时不能删除当前值,这将是不方便的)。如果用户尝试使用空字符串提交,则可以使用转换器返回当前值。
所以这个实现可能看起来像
public class IntegerEditingCell extends TableCell<Item, Integer> {
private TextField textField ;
private TextFormatter<Integer> textFormatter ;
public IntegerEditingCell(int min, int max) {
textField = new TextField();
UnaryOperator<TextFormatter.Change> filter = c -> {
String newText = c.getControlNewText() ;
// always allow deleting all characters:
if (newText.isEmpty()) {
return c ;
}
// otherwise, must have all digits:
if (! newText.matches("\\d+")) {
return null ;
}
// check range:
int value = Integer.parseInt(newText) ;
if (value < min || value > max) {
return null ;
} else {
return c ;
}
};
StringConverter<Integer> converter = new StringConverter<Integer>() {
@Override
public String toString(Integer value) {
return value == null ? "" : value.toString() ;
}
@Override
public Integer fromString(String string) {
// if it's an int, return the parsed value
if (string.matches("\\d+")) {
return Integer.valueOf(string);
} else {
// otherwise, just return the current value of the cell:
return getItem() ;
}
}
};
textFormatter = new TextFormatter<Integer>(converter, 0, filter) ;
textField.setTextFormatter(textFormatter);
textField.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
});
textField.setOnAction(e -> commitEdit(converter.fromString(textField.getText())));
textProperty().bind(Bindings
.when(emptyProperty())
.then((String)null)
.otherwise(itemProperty().asString()));
setGraphic(textField);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
@Override
protected void updateItem(Integer value, boolean empty) {
super.updateItem(value, empty);
if (isEditing()) {
textField.requestFocus();
textField.selectAll();
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
@Override
public void startEdit() {
super.startEdit();
textFormatter.setValue(getItem());
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.requestFocus();
textField.selectAll();
}
@Override
public void commitEdit(Integer newValue) {
super.commitEdit(newValue);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
@Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
这看起来像很多代码,但大多数只是设置文本字段和格式化程序,然后有一些非常标准的单元格实现,只是确保文本字段以编辑模式显示,纯文本以非编辑模式显示。
现在您根本不需要担心检查输入的有效性,因为用户无法输入无效值。
这是一个简单的用法示例:
import java.util.Random;
import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
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.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class ValidatingTableColumn extends Application {
@Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.setEditable(true);
TableColumn<Item, String> nameColumn = new TableColumn<>("Item");
nameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
TableColumn<Item, Integer> valueColumn = new TableColumn<>("Value");
valueColumn.setCellValueFactory(cellData -> cellData.getValue().valueProperty().asObject());
table.getColumns().add(nameColumn);
table.getColumns().add(valueColumn);
valueColumn.setCellFactory(col -> new IntegerEditingCell(1, 31));
valueColumn.setOnEditCommit(e -> {
table.getItems().get(e.getTablePosition().getRow()).setValue(e.getNewValue());
});
Random rng = new Random();
for (int i = 1; i <= 20; i++) {
table.getItems().add(new Item("Item "+i, rng.nextInt(31)+1));
}
Button debug = new Button("Show all values");
debug.setOnAction(e -> table.getItems().forEach(item -> System.out.println(item.getName()+" ("+item.getValue()+")")));
BorderPane.setAlignment(debug, Pos.CENTER);
BorderPane.setMargin(debug, new Insets(5));
BorderPane root = new BorderPane(table, null, null, debug, null);
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.show();
}
public static class Item {
private final IntegerProperty value = new SimpleIntegerProperty() ;
private final StringProperty name = new SimpleStringProperty();
public Item(String name, int value) {
setName(name);
setValue(value);
}
public final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
}
public static void main(String[] args) {
launch(args);
}
}
答案 1 :(得分:0)
我遇到了与您相同的问题,并且找到了解决方案。如果新值无效,则获取当前编辑行的索引,并将数据设置为旧对象。
int row = event.getTablePosition().getRow();
ProductInOrder productInOrder = event.getTableView().getItems().get(row);
if (newCount <0){
Messenger.erro(bundle.getString("txt_number_must_large_or_equal_0"));
tbProductInOrder.getItems().set(row, productInOrder);
return;
}
productInOrder.setCount(newCount);