Path: csiph.com!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail From: Nigel Wade Newsgroups: comp.lang.java.programmer Subject: Re: JavaFX: Bindings, StringConverters, and ChangeListeners Date: Wed, 22 Jun 2016 15:22:48 +0100 Lines: 69 Message-ID: References: <87wplhbbvy.fsf@gnu.org> Mime-Version: 1.0 Content-Type: text/plain; charset=windows-1252; format=flowed Content-Transfer-Encoding: 7bit X-Trace: individual.net qi9tDaw8piAmn5Sdu9y/ygGso+3tWqNumCkM3CnEsRJ1aN6C5D Cancel-Lock: sha1:irtzMe0jMf/ie8hCrDhvK7O+1n4= User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.7.0 In-Reply-To: <87wplhbbvy.fsf@gnu.org> Xref: csiph.com comp.lang.java.programmer:34691 On 22/06/16 14:18, Tassilo Horn wrote: > Hi all, > > I'm doing my first JavaFX UI stuff and basically come along quite well. > However, there's one absurdly simple problem which I've not been able to > solve completely. > > I have a TextField txtMaxTreffer whose textProperty() is bidirectionally > bound to some IntegerProperty maxTreffer using a NumberStringConverter. > > txtMaxTreffer.textProperty().bindBidirectional(maxTreffer, > new NumberStringConverter()); > > To prevent the user from entering invalid input, the textProperty() also > has a ChangeListener that resets to the old value in case the new value > cannot be parsed to an Integer. > > In general, that works just fine meaning that the user cannot enter > wrong input, and thus the NumberStringConverter which is implicitly > invoked by the binding is always able to transfer the TextField value to > the IntegerProperty. > > Well, there is one situation where it doesn't work so well: When you > prepend to the input of the TextField (or the input has been empty). In > that case (and only in that case), it seems the NumberStringConverter is > invoked first and only afterwards the ChangeListener is called. > Obviously, my mechanics rely on the opposite order. > > That is, let's say the textfield's value is "100" and now you prepend an > X to make the value "X100". Then the NumberStringConverter.fromString() > method gets invoked with "X100" and throws a ParseException. Then, my > ChangeListener is called and changes the invalid value back to "100". > > In the other two cases where I enter the invalid input in the middle > ("1X00") or at the end ("100X"), first my ChangeListener is called and > resets back to "100" before the NumberStringConverter has its turn. > > Ok, the only bad effect here is that a ParseException is logged to > stderr but I guess there must be some way to make that work without > exception, too. > > Of course I could provide my own NumberStringConverter which checks for > valid input before trying to parse the string to a number but then I'd > have the very same checks in both the converter and the change listener. > > Any other ideas? Maybe a ChangeListener is not the right thing for > validation and correction anyhow? > > Thanks, > Tassilo > The first thing I'd suggest is to think about the problem from the user's perspective rather than the programmer's. If I, as a user, enter a number into a text field and inadvertently enter an invalid character, I'd be rather annoyed if the GUI element wiped out what I'd entered and replaced it with something which I didn't want. It would be better from my (user) perspective if the text element warned me of my transgression (flash, highlight, red background whatever) and left my text there for me to edit, rather than make me enter it again with the likelihood of making a similar mistake again. Imagine if the current contents were 002324561234, and I wanted to change it to 002324560234 but instead changed it to 00232456O234. Your current method would revert it back to 002324561234, and I may never notice that my change had been overridden. What's wrong with parsing the entered text and, if there is an exception, catch it and display some feedback, leaving the focus on the text field? If you really, really want to set the text back to the original value you could still do that in the catch block. I don't see any benefit from using a ChangeListener.