[英]Canonical way to cancel a table cell edit if parse fails
編輯:
在 James_D 找到這個答案后,我首先投票關閉作為副本,它在TextField
上設置了一個TextFormatter
。 但首先我發現(在TableView
上下文中)方法TextFieldTableCell.forTableColumn()
在開始編輯時實際上並沒有繪制TextField
,而是一個LabeledText
,它沒有LabeledText
TextInputControl
,因此沒有setTextFormatter()
.
其次,我想要一些以熟悉的方式行事的東西。 我可能已經在我的回答中產生了“規范”的解決方案:讓別人來判斷。
這是TableView
的TableColumn
(所有 Groovy):
TableColumn<Person, String> ageCol = new TableColumn("Age")
ageCol.cellValueFactory = { cdf -> cdf.value.ageProperty() }
int oldAgeValue
ageCol.onEditStart = new EventHandler(){
@Override
public void handle( Event event) {
oldAgeValue = event.oldValue
}
}
ageCol.cellFactory = TextFieldTableCell.forTableColumn(new IntegerStringConverter() {
@Override
public Integer fromString(String value) {
try {
return super.fromString(value)
}
catch ( NumberFormatException e) {
// inform user by some means...
println "string could not be parsed as integer..."
// ... and cancel the edit
return oldAgeValue
}
}
})
摘自類人:
public class Person {
private IntegerProperty age;
public void setAge(Integer value) { ageProperty().set(value) }
public Integer getAge() { return ageProperty().get() }
public IntegerProperty ageProperty() {
if (age == null) age = new SimpleIntegerProperty(this, "age")
return age
}
...
如果沒有 start-edit Handler
,當我輸入一個無法解析為Integer
NumberFormatException
的String
,毫不奇怪地拋出。 但我也發現單元格中的數字隨后被設置為 0,這可能不是預期的結果。
但以上給我的印象是一個非常笨拙的解決方案。
我查看了ageCol
和ageCol.cellFactory
(因為它們可以從catch
塊內部訪問)但看不到任何更好和明顯的東西。 我還可以看到,可以輕松獲得Callback
( ageCol.cellFactory
),但調用它需要參數cdf
,即CellDataFeatures
實例,您必須再次將其存儲在某處。
我確信 Swing 涉及某種類型的驗證器機制:即在可以從編輯器組件(通過某些委托或其他方式)傳輸值之前,可以覆蓋某些驗證機制。 但是這個IntegerStringConverter
似乎起到了驗證器的作用,盡管如果驗證失敗,它似乎沒有提供任何方法來恢復到現有(“舊”)值。
有沒有比我上面展示的更笨拙的機制?
編輯
NB 在 kleopatra 的寶貴見解之后有所改進。
編輯2
在意識到最好的辦法是使用現有的默認編輯器並對其進行調整后,徹底進行了大修。
我想我會舉一個LocalDate
的例子,比Integer
稍微有趣一點。 鑒於以下類:
class Person(){
...
private ObjectProperty<LocalDate> dueDate;
public void setDueDate(LocalDate value) {
dueDateProperty().set(value);
}
public LocalDate getDueDate() {
return (LocalDate) dueDateProperty().get();
}
public ObjectProperty dueDateProperty() {
if (dueDate == null) dueDate = new SimpleObjectProperty(this, "dueDate");
return dueDate;
}
然后創建一個新的編輯器單元格類,它與TextFieldTreeTableCell
( TreeTableCell
子類)完全相同,默認情況下使用它為TreeTableView
的表格單元格創建編輯器。 但是,您不能真正繼承TextFieldTreeTableCell
子類,例如,它的基本字段textField
是private
。
所以你從源代碼中完整復制代碼*(只有大約 30 行),然后調用它
class DueDateEditor extends TreeTableCell<Person, LocalDate> {
...
然后,您必須創建一個新的StringConverter
類, LocalDateStringConverter
。 子類化的原因是,如果您不這樣做,則無法在收到無效日期時捕獲fromString()
拋出的DateTimeParseException
:如果您使用LocalDateStringConverter
,JavaFX 框架會不幸地捕獲它,堆棧跟蹤中沒有任何幀涉及您自己的代碼。 所以你這樣做:
class ValidatingLocalDateStringConverter extends LocalDateStringConverter {
boolean valid;
LocalDate fromString(String value) {
valid = true;
if (value.isBlank()) return null;
try {
return LocalDate.parse(value);
} catch (Exception e) {
valid = false;
}
return null;
}
}
回到您的DueDateEditor
類,然后按如下方式重寫startEdit
方法。 注意,與TextFieldTreeTableCell
類一樣, textField
實際上是在您第一次編輯時延遲創建的。
@Override
void startEdit() {
if (! isEditable()
|| ! getTreeTableView().isEditable()
|| ! getTableColumn().isEditable()) {
return;
}
super.startEdit();
if (isEditing()) {
if (textField == null) {
textField = CellUtils.createTextField(this, getConverter());
// this code added by me
ValidatingLocalDateStringConverter converter = getConverter();
Callable bindingFunc = new Callable(){
@Override
Object call() throws Exception {
// NB the return value from this is "captured" by the editor
converter.fromString( textField.getText() );
return converter.valid? '' : "-fx-background-color: red;";
}
}
def stringBinding = Bindings.createStringBinding( bindingFunc, textField.textProperty() );
textField.styleProperty().bind( stringBinding );
}
CellUtils.startEdit(this, getConverter(), null, null, textField);
}
}
注意不要費心去查找CellUtils
:這是包私有的,有問題的包是 javafx.scene.control.cell。
要進行設置,請執行以下操作:
Callback<TreeTableColumn, TreeTableCell> dueDateCellFactory =
new Callback<TreeTableColumn, TreeTableCell>() {
public TreeTableCell call(TreeTableColumn p) {
return new DueDateEditor( new ValidatingLocalDateStringConverter() );
}
}
dueDateColumn.setCellFactory(dueDateCellFactory);
... 結果是一個不錯的反應式編輯器單元格:當包含無效日期(可接受的模式yyyy-mm-dd
;請參閱其他LocalDate.parse()
其他格式的變體)時,背景為紅色,否則為正常。 輸入有效日期可以無縫工作。 您還可以輸入一個空String
,它作為null
LocalDate
返回。
使用上述方法,在無效日期下按 Enter 會將日期設置為null
。 但是,使用ValidatingLocalDateStringConverter
的valid
字段覆蓋防止這種情況發生的事情(即強制您輸入有效日期,或取消編輯,例如通過 Escape)是微不足道的:
@Override
void commitEdit( LocalDate newDueDate ){
if( getConverter().valid )
super.commitEdit( newDueDate );
}
* 我在網上找不到這個。 我從 javafx 源 .jar 文件中提取了 javafx-controls-11.0.2-sources.jar
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.