Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

JavaFX: Bindings, StringConverters, and ChangeListeners

398 views
Skip to first unread message

Tassilo Horn

unread,
Jun 22, 2016, 9:18:19 AM6/22/16
to
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

Nigel Wade

unread,
Jun 22, 2016, 10:22:59 AM6/22/16
to
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.




Eric Douglas

unread,
Jun 22, 2016, 11:06:25 AM6/22/16
to
On Wednesday, June 22, 2016 at 9:18:19 AM UTC-4, Tassilo Horn wrote:
> 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.

Revert to old value? You don't want a user to be able to type something and see what they typed then change it on them. I don't know much about Java FX, I write Swing and have only needed the FX for the WebView, but for a numeric entry allowing only valid numbers I've used JTextField with a custom input filter. The input filters process every character they try to add to or remove from the control before adding or removing it.

Tassilo Horn

unread,
Jun 23, 2016, 2:40:08 AM6/23/16
to
Nigel Wade <n...@ion.le.ac.uk> writes:

Hi Nigel,

> 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.

Oh, flashing is actually a nice idea and can be done pretty easily from
my listener.

> 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.

Well, in the concrete case the number is the maximum number of matches
to be looked for in a database, so the numbers aren't that hard to
distinguish but I got your point.

> 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.

The problem is that I have no control over when and where the string in
the text field is parsed and converted to a number. I just specified a
declarative binding between the text field's text property and an
integer property with a standart converter and thereby gave up exact
control over these things. But bindings are such an integral part of FX
that I guessed there must be some standart way to treat invalid input so
that the binding's converter doesn't err.

Bye,
Tassilo

Tassilo Horn

unread,
Jun 23, 2016, 2:45:49 AM6/23/16
to
Eric Douglas <e.d.pro...@gmail.com> writes:

Hi Eric,

>> 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.
>
> Revert to old value?

Revert to old value in this case basically means the user types a key
and the value does not change.

> You don't want a user to be able to type something and see what they
> typed then change it on them.

Well, I think in this case it's not too bad, especially if the field
flashes when invalid input is entered.

> I don't know much about Java FX, I write Swing and have only needed
> the FX for the WebView, but for a numeric entry allowing only valid
> numbers I've used JTextField with a custom input filter. The input
> filters process every character they try to add to or remove from the
> control before adding or removing it.

Yes, that's what I did in Swing, too. And the FX ChangeListeners are
not too different. At least, they are called on any key the user typed
into the text field. My problem is more on the part on when the
binding's string-to-integer converter is invoked. When entering text in
the middle or end, it'll be called after my listener (good!) but when
entering text at the front, it'll be called before my listener (bad!).

Bye,
Tassilo

Martin Gregorie

unread,
Jun 23, 2016, 6:00:10 AM6/23/16
to
On Thu, 23 Jun 2016 08:39:51 +0200, Tassilo Horn wrote:

> Oh, flashing is actually a nice idea and can be done pretty easily from
> my listener.
>
Personal view: flashing is annoying if its more than a single character
that's doing it. I've recently used black on white for normal Swing
JTextField data entry fields and, if invalid/inappropriate/out of range,
setting that to white text on red as an alert. If the rest of the
window's palette is no closer to red than pastel yellow, its quite eye-
catching.

Actually this is numeric with the field in question being a total of
several inputs. The colours I used were:
- black on white = total too low
- green on white = total exactly right
- white on red = total too high


--
martin@ | Martin Gregorie
gregorie. | Essex, UK
org |

Nigel Wade

unread,
Jun 23, 2016, 7:19:28 AM6/23/16
to
You need to be careful with colours. I've had issues with UIManager overriding my colours because it decided there was
insufficient contrast between the foreground and background. I'd very carefully chosen the colours so that there was
sufficient contrast, but the UIManager had other ideas. As far as I could tell the UIManager only compares the
light/darkness of the colours, and doesn't take proper account of the colour contrast. I have no idea if this is similar
in JavaFX, my Java GUI work is with Swing.

I find that a single flash is not too intrusive. It's a reasonable indication to the user that there is something wrong
with the input somewhere on the dialog. Then a field highlight can be used to give feedback as to which actual field is
incorrect.


Nigel Wade

unread,
Jun 23, 2016, 7:39:42 AM6/23/16
to
On 23/06/16 07:39, Tassilo Horn wrote:
> Nigel Wade <n...@ion.le.ac.uk> writes:
>
> Hi Nigel,
>
>> 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.
>
> Oh, flashing is actually a nice idea and can be done pretty easily from
> my listener.

A single flash. Not flashing.

>
>> 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.
>
> Well, in the concrete case the number is the maximum number of matches
> to be looked for in a database, so the numbers aren't that hard to
> distinguish but I got your point.

It's about least surprise (and annoyance) for the user.

>
>> 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.
>
> The problem is that I have no control over when and where the string in
> the text field is parsed and converted to a number. I just specified a
> declarative binding between the text field's text property and an
> integer property with a standart converter and thereby gave up exact
> control over these things. But bindings are such an integral part of FX
> that I guessed there must be some standart way to treat invalid input so
> that the binding's converter doesn't err.
>

I don't understand what implications there are with JavaFX. A normal scenario with Java/Swing is to either check the
user input when the field loses focus, is committed by key press (Return) or when the user indicates via some GUI
element that the input is complete. In Swing you do this by adding listeners to the components which get notified when
certain actions occur. To validate the field you would simply read the text from it and process it in whatever way you
see fit. You are in full control of how and when that text is checked and how it is interpreted. You may delegate to
some extent by using a JFormattedTextField, but as you are discovering that is not always the best option since you are
no longer in control of when/how the field contents are interpreted. Maintaining full control is often the only option.

Eric Douglas

unread,
Jun 23, 2016, 8:11:35 AM6/23/16
to
On Thursday, June 23, 2016 at 2:45:49 AM UTC-4, Tassilo Horn wrote:
> Yes, that's what I did in Swing, too. And the FX ChangeListeners are
> not too different. At least, they are called on any key the user typed
> into the text field. My problem is more on the part on when the
> binding's string-to-integer converter is invoked. When entering text in
> the middle or end, it'll be called after my listener (good!) but when
> entering text at the front, it'll be called before my listener (bad!).

It should be more than key typed. The input filters for JTextField give you character added or removed event which could be a key type, copy/paste, or drag/drop. This should happen before any listeners.

I write custom controls extending Swing GUI which have their own listeners. I add a listener interface array so I can manually tell my custom listeners when an event has occurred. I had some issues with JTable returning the events in an odd order, so I'd store an event which normally follows another event and pass it to the listeners after I get the other event.

Eric Sosman

unread,
Jun 23, 2016, 8:22:59 AM6/23/16
to
FWIW, in Swing applications I've had good experiences with using
an InputVerifier. Or, perhaps, "misusing" it: I don't use the verify()
method at all, but override shouldYieldFocus() instead. If the content
is invalid, my shouldYieldFocus() returns false and the user quickly
discovers that he's stuck in the field until he corrects it.

I'm not FXliterate, so I don't know whether there's an analog.
Could be worth a look, though.

--
eso...@comcast-dot-net.invalid
"Don't be afraid of work. Make work afraid of you." -- TLM

Tassilo Horn

unread,
Jun 23, 2016, 8:40:04 AM6/23/16
to
Nigel Wade <n...@ion.le.ac.uk> writes:

>> Oh, flashing is actually a nice idea and can be done pretty easily
>> from my listener.
>
> A single flash. Not flashing.

Yes, now I'm flashing once and really like the effect. And it's good
that I got my hands dirty with JavaFX animations thereby.

>> The problem is that I have no control over when and where the string
>> in the text field is parsed and converted to a number. I just
>> specified a declarative binding between the text field's text
>> property and an integer property with a standart converter and
>> thereby gave up exact control over these things. But bindings are
>> such an integral part of FX that I guessed there must be some
>> standart way to treat invalid input so that the binding's converter
>> doesn't err.
>>
>
> I don't understand what implications there are with JavaFX. A normal
> scenario with Java/Swing is to either check the user input when the
> field loses focus, is committed by key press (Return) or when the user
> indicates via some GUI element that the input is complete.

Yes, right. But with JavaFX, once you have established a binding
between a UI input control and a property, the synchronization seems to
happen immediately, that is, it doesn't wait for a loss of focus or some
special confirmation like pressing enter.

Bye,
Tassilo

Tassilo Horn

unread,
Jun 23, 2016, 9:12:42 AM6/23/16
to
Eric Sosman <eso...@comcast-dot-net.invalid> writes:

> FWIW, in Swing applications I've had good experiences with using
> an InputVerifier.

Yes, that works fine in Swing...

> I'm not FXliterate, so I don't know whether there's an analog.
> Could be worth a look, though.

... but there doesn't seem to be something similar in FX. Googling
suggests that input validation is currently one the missing pieces in FX
and it seems that right now everybody uses a FocusListener to validate
the input when the control loses focus.

Well, never mind, I've simply stopped using a bidirectional binding with
a number-string-converter and just use

maxTreffer.bind(Bindings.createIntegerBinding(() -> {
String txt = txtMaxTreffer.getText();
try {
return Integer.valueOf(txt);
} catch(NumberFormatException ignored) { }
return maxTreffer.get();
}, txtMaxTreffer.textProperty()));

Thanks for all suggestions,
Tassilo

Eric Sosman

unread,
Jun 23, 2016, 10:57:08 AM6/23/16
to
On 6/23/2016 9:12 AM, Tassilo Horn wrote:
>[...]
> ... but there doesn't seem to be something similar in FX. Googling
> suggests that input validation is currently one the missing pieces in FX

A *user* *interface* framework without input validation?

YIKES!

No wonder Oracle is so keen about Ineffectual Property ...
0 new messages