JavaFX Bind valueProperty from TextField's TextFormatter to SimpleDoubleProperty

joshsweaney :

I have a TextField which has been given a TextFormatter, which has a filter that takes the input of the textfield, and ensures it is in a double format. It then uses a DoubleStringConverter to convert the filtered text into a double value. This is in a custom FXML component. See below for minimal example.

public SomeComponent implements Initializable {
    @FXML
    public TextField inputField;

    public int min;

    public void initialize(URL location, ResourceBundle resources) {
        UnaryOperator<TextFormatter.Change> doubleFilter = change -> {
            String newText = change.getControlNewText();
            if (newText.matches("-?[0-9]{1,13}(\\.[0-9]*)?")) { 
                return change;
            }
            return null;
        };

        inputField.setTextFormatter(new TextFormatter<>(new DoubleStringConverter(), min, doubleFilter)); 
    }
}

Another FXML component that uses this component ...

public SomeView implements Initializable {
    @FXML
    SomeComponent comp;
    public void initialize(URL location, ResourceBundle resources) {
        comp.inputField.getTextFormatter().valueProperty().bindBidirectional(SomeDataManagerClass.SomeSimpleDoubleProperty());
    }
}

The bindBidirectional() is attempting to bind the TextField's converted value, to a SimpleDoubleProperty that exists elsewhere. But this returns the following error:

method Property.bindBidirectional(Property<CAP#1>) is not applicable
(argument mismatch; SimpleDoubleProperty cannot be converted to Property<CAP#1>)

Essentially I want to make use of the TextField's value (not the getText(), but the converted value) for binding to other properties.

I realise there is something wrong with the captured type inference with the TextFormatter, but I can't see anywhere in the JavaDocs that shows how to make use of the TextFormatter's double value. What is the point in using a DoubleStringConverter to convert the value if there isn't a simple way to extract that double from the TextField? I could use Double.parseDouble(inputField.getText()) but this seems pointless if I have already gone to the trouble of setting up a nice TextFormatter for the field.

How do I make use of the TextFormatter's valueProperty outside of SomeComponent?

Slaw :

The textFormatter property is an ObjectProperty<TextFormatter<?>>. Since the TextInputControl class is not generic there's no way to properly parameterize the textFormatter property, hence the wildcard. This means whenever you query the property you'll get a TextFormatter<?> regardless of previously setting it to, for instance, a TextFormatter<Double>. There's a couple ways you could solve this:

  1. Cast the result to whatever generic type you expect:

    var formatter = (TextFormatter<Number>) comp.inputField.getTextFormatter();
    formatter.valueProperty().bindBidirectional(SomeDataManagerClass.SomeSimpleDoubleProperty());
    

    Of course, that will lead to an unchecked cast warning. You can suppress the warning if you want via a @SuppressWarnings("unchecked") annotation but the operation remains unsafe.

  2. Keep your own reference to the TextFormatter<Number> and provide a method for other code to get it.

    public SomeComponent implements Initializable {
    
        @FXML
        public TextField inputField;
    
        // maintain generic information
        private TextFormatter<Number> inputFieldFormatter;
    
        public int min;
    
        public void initialize(URL location, ResourceBundle resources) {
            UnaryOperator<TextFormatter.Change> doubleFilter = change -> {
                String newText = change.getControlNewText();
                if (newText.matches("-?[0-9]{1,13}(\\.[0-9]*)?")) { 
                    return change;
                }
                return null;
            };
    
            inputFieldFormatter = new TextFormatter<>(new NumberStringConverter(), min, doubleFilter);
            inputField.setTextFormatter(inputFieldFormatter)); 
        }
    
        // use this method in your other controller
        public TextFormatter<Number> getInputFieldFormatter() {
            return inputFieldFormatter;
        }
    }
    

Notice in each case I used a TextFormatter<Number>. This is because a DoubleProperty is a Property<Number> and the bindBidirectional method expects an exact generic match, unlike the bind method which is upper bounded. This also meant a NumberStringConverter had to be used.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=334454&siteId=1