2016-01-09 4 views
2

Я хотел бы привести пример, который объясняет мне, как я могу отменить редактирование и сбросить старое значение конкретной ячейки в TableView, который был отредактирован, но не прошел проверку. Дополнительную информацию см. В приведенном ниже коде.Отмените изменение ячейки 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

Что такая реализация у вас есть для вашей камеры? –

+0

Эй, Джеймс, мой спаситель JavaFX! Я отредактирую свой основной пост, чтобы предоставить вам дополнительную информацию. – EarthMind

+0

Мое редактирование, что вы просили? – EarthMind

ответ

2

я играл с этим некоторое время. Суть в том, что метод 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, которая просто наносит вето недействительные значения. Вы должны быть немного осторожны с ними, так как вы хотите разрешить частично редактируемые значения (так что в общем случае недостаточно только допустимых значений, поскольку они проверяются каждый раз, когда текст изменяется, а не только по фиксации). В этом случае единственное, что это означает, это то, что вы должны разрешить пустые строки в текстовом поле (иначе пользователь не сможет удалить текущее значение во время редактирования, что было бы неудобно). Вы можете использовать конвертер, чтобы вернуть текущее значение, если пользователь пытается зафиксировать пустую строку.

Так реализация этого может выглядеть

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); 
    } 
} 
+0

'TextFormatter' - хорошее решение, но иногда это неприменимо - например, для проверки даты или ячеек со списком. В моем проекте я считаю полезным создать «ValidatingTableCell», который имеет «BiPredicate» для проверки (принимает элемент строки и значение, которое должно быть зафиксировано), и строку, отображаемую как сообщение об ошибке для пользователя. Затем я подклассифицирую это для вариантов редактирования текста, даты, комбинированного кода и т. Д. – Itai

+0

Несомненно, с помощью комбинированного блока или выбора даты довольно легко обеспечить правильное выделение с помощью элемента управления? –

+0

Несомненно, мне просто нужно общее решение, поэтому мне не нужно писать часть взаимодействия с пользователем несколько раз (в настоящее время я просто показываю предупреждение, но позже я могу изменить это) – Itai

Смежные вопросы