2016-02-08 128 views
1

我正在使用包含Pattern s的可編輯ListView。 用戶可以查看和編輯列表中的正則表達式,並且我想在提交值之前驗證正則表達式在語法上是否正確(並將反饋像紅色邊框一樣提供給用戶)。ListView驗證編輯並防止提交

有沒有辦法做到這一點?

patternList.setCellFactory(TextFieldListCell.forListView(new StringConverter<Pattern>() { 
    @Override 
    public String toString(Pattern pattern) { 
     return pattern.toString(); 
    } 

    @Override 
    public Pattern fromString(String string) { 
     try { 
      return Pattern.compile(string); 
     } catch (PatternSyntaxException e) { 
      return null; 
     } 
    } 
})); 
patternList.setOnEditCommit(e -> { 
    if (e.getNewValue() == null) { 
     // TODO pattern syntax error, prevent commit and stay in edit mode 
    } else { 
     patternList.getItems().set(e.getIndex(), e.getNewValue()); 
    } 
}); 

回答

0

我終於找到了一種方法,通過重寫的TextFieldListCellcommitEdit()方法:

patternList.setCellFactory(l -> new TextFieldListCell<Pattern>(new StringConverter<Pattern>() { 
    @Override 
    public String toString(Pattern pattern) { 
     return pattern.toString(); 
    } 

    @Override 
    public Pattern fromString(String string) { 
     try { 
      return Pattern.compile(string); 
     } catch (PatternSyntaxException e) { 
      return null; 
     } 
    } 
}) { 
    @Override 
    public void commitEdit(Pattern pattern) { 
     if (!isEditing()) return; 
     PseudoClass errorClass = PseudoClass.getPseudoClass("error"); 
     pseudoClassStateChanged(errorClass, pattern == null); 
     if (pattern != null) { 
      super.commitEdit(pattern); 
     } 
    } 
}); 
patternList.setOnEditCommit(e -> patternList.getItems().set(e.getIndex(), e.getNewValue())); 
3

我將通過創建一個TableCell實現這一點。例如爲:

import java.util.function.Predicate; 

import javafx.beans.binding.Bindings; 
import javafx.beans.property.BooleanProperty; 
import javafx.beans.property.SimpleBooleanProperty; 
import javafx.css.PseudoClass; 
import javafx.scene.control.ContentDisplay; 
import javafx.scene.control.TableCell; 
import javafx.scene.control.TextField; 
import javafx.scene.input.KeyCode; 
import javafx.scene.input.KeyEvent; 

public class ValidatingEditingCell<S> extends TableCell<S, String> { 

    private final TextField textField ; 

    private static final PseudoClass INVALID = PseudoClass.getPseudoClass("invalid"); 

    private BooleanProperty valid = new SimpleBooleanProperty(); 

    public ValidatingEditingCell(Predicate<String> validator) { 
     this.textField = new TextField(); 
     valid.bind(Bindings.createBooleanBinding(() -> textField.getText() != null && validator.test(textField.getText()), 
       textField.textProperty())); 
     valid.addListener((obs, wasValid, isValid) -> { 
      pseudoClassStateChanged(INVALID, ! isValid); 
     }); 
     pseudoClassStateChanged(INVALID, ! valid.get()); 

     textField.addEventHandler(KeyEvent.KEY_PRESSED, e -> { 
      if (e.getCode() == KeyCode.ENTER && valid.get()) { 
       commitEdit(textField.getText()); 
      } 
      if (e.getCode() == KeyCode.ESCAPE) { 
       cancelEdit(); 
      } 
     }); 

     setGraphic(textField); 
     setContentDisplay(ContentDisplay.TEXT_ONLY); 
    } 

    @Override 
    protected void updateItem(String item, boolean empty) { 
     super.updateItem(item, empty); 
     setText(empty ? null : item); 
     textField.setText(empty ? null : item); 
     setContentDisplay(isEditing() ? ContentDisplay.GRAPHIC_ONLY : ContentDisplay.TEXT_ONLY); 
    } 

    @Override 
    public void cancelEdit() { 
     super.cancelEdit(); 
     setContentDisplay(ContentDisplay.TEXT_ONLY); 
    } 

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

    @Override 
    public void startEdit() { 
     super.startEdit(); 
     setContentDisplay(ContentDisplay.GRAPHIC_ONLY); 
     textField.selectAll(); 
     textField.requestFocus(); 
    } 
} 

這需要謂詞作爲參數;謂詞對有效文本返回true,對無效文本返回false。它在單元格上設置了一個CSS僞類,因此您可以使用CSS來設置文本字段的樣式(如果需要,也可以使用單元格本身)。

這裏有一個簡單的例子驗證了三種不同的列是不同的:

import java.util.function.Function; 
import java.util.function.Predicate; 

import javafx.application.Application; 
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.TableColumn; 
import javafx.scene.control.TableView; 
import javafx.scene.layout.BorderPane; 
import javafx.scene.layout.HBox; 
import javafx.stage.Stage; 

public class ValidatingTableExample extends Application { 

    private static <S> TableColumn<S, String> column(String title, Function<S, StringProperty> property, 
      Predicate<String> validator) { 
     TableColumn<S, String> col = new TableColumn<>(title); 
     col.setCellValueFactory(cellData -> property.apply(cellData.getValue())); 
     col.setCellFactory(tc -> new ValidatingEditingCell<>(validator)); 
     col.setPrefWidth(150); 
     return col ; 
    } 

    @Override 
    public void start(Stage primaryStage) { 
     TableView<Address> table = new TableView<>(); 
     table.setEditable(true); 
     table.getColumns().add(column("City", Address::cityProperty, s -> ! s.isEmpty())); 
     table.getColumns().add(column("State", Address::stateProperty, s -> s.length()==2)); 
     table.getColumns().add(column("Zip", Address::zipProperty, s -> s.matches("\\d{5}"))); 

     Button newAddress = new Button("Add"); 
     newAddress.setOnAction(e -> { 
      table.getItems().add(new Address("City", "State", "Zip")); 
     }); 

     Button debug = new Button("Debug"); 
     debug.setOnAction(e -> 
       table.getItems().stream() 
        .map(address -> String.format("%s, %s %s", address.getCity(), address.getState(), address.getZip())) 
        .forEach(System.out::println)); 

     HBox buttons = new HBox(5, newAddress, debug); 
     buttons.setAlignment(Pos.CENTER); 
     buttons.setPadding(new Insets(5)); 

     BorderPane root = new BorderPane(table, null, null, buttons, null); 
     Scene scene = new Scene(root, 600, 600); 
     scene.getStylesheets().add(getClass().getResource("validating-cell.css").toExternalForm()); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 


    public static class Address { 
     private final StringProperty city = new SimpleStringProperty(); 
     private final StringProperty state = new SimpleStringProperty(); 
     private final StringProperty zip = new SimpleStringProperty(); 

     public Address(String city, String state, String zip) { 
      setCity(city); 
      setState(state); 
      setZip(zip); 
     } 

     public final StringProperty cityProperty() { 
      return this.city; 
     } 


     public final String getCity() { 
      return this.cityProperty().get(); 
     } 


     public final void setCity(final String city) { 
      this.cityProperty().set(city); 
     } 


     public final StringProperty stateProperty() { 
      return this.state; 
     } 


     public final String getState() { 
      return this.stateProperty().get(); 
     } 


     public final void setState(final String state) { 
      this.stateProperty().set(state); 
     } 


     public final StringProperty zipProperty() { 
      return this.zip; 
     } 


     public final String getZip() { 
      return this.zipProperty().get(); 
     } 


     public final void setZip(final String zip) { 
      this.zipProperty().set(zip); 
     } 



    } 

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

和一些示例CSS:

.table-cell:invalid .text-field { 
    -fx-focus-color: red ; 
    -fx-control-inner-background: #ffc0c0 ; 
    -fx-accent: red ; 
} 
+0

哎呀。在我測試解決方案時(這讓我的孩子上牀......沒有注意到你已經在此期間發佈了一個答案),這已經開放了很長時間。 –

+0

哈哈沒問題,你的回答也非常好:)我的猜測基本上是你的簡單版本,但不像你的那樣可以重用。 – Joffrey