Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.java.programmer > #34690 > unrolled thread

JavaFX: Bindings, StringConverters, and ChangeListeners

Started byTassilo Horn <tsdh@gnu.org>
First post2016-06-22 15:18 +0200
Last post2016-06-23 10:56 -0400
Articles 13 — 5 participants

Back to article view | Back to comp.lang.java.programmer


Contents

  JavaFX: Bindings, StringConverters, and ChangeListeners Tassilo Horn <tsdh@gnu.org> - 2016-06-22 15:18 +0200
    Re: JavaFX: Bindings, StringConverters, and ChangeListeners Nigel Wade <nmw@ion.le.ac.uk> - 2016-06-22 15:22 +0100
      Re: JavaFX: Bindings, StringConverters, and ChangeListeners Tassilo Horn <tsdh@gnu.org> - 2016-06-23 08:39 +0200
        Re: JavaFX: Bindings, StringConverters, and ChangeListeners Martin Gregorie <martin@address-in-sig.invalid> - 2016-06-23 09:59 +0000
          Re: JavaFX: Bindings, StringConverters, and ChangeListeners Nigel Wade <nmw@ion.le.ac.uk> - 2016-06-23 12:19 +0100
        Re: JavaFX: Bindings, StringConverters, and ChangeListeners Nigel Wade <nmw@ion.le.ac.uk> - 2016-06-23 12:39 +0100
          Re: JavaFX: Bindings, StringConverters, and ChangeListeners Tassilo Horn <tsdh@gnu.org> - 2016-06-23 14:39 +0200
    Re: JavaFX: Bindings, StringConverters, and ChangeListeners Eric Douglas <e.d.programmer@gmail.com> - 2016-06-22 08:06 -0700
      Re: JavaFX: Bindings, StringConverters, and ChangeListeners Tassilo Horn <tsdh@gnu.org> - 2016-06-23 08:45 +0200
        Re: JavaFX: Bindings, StringConverters, and ChangeListeners Eric Douglas <e.d.programmer@gmail.com> - 2016-06-23 05:11 -0700
          Re: JavaFX: Bindings, StringConverters, and ChangeListeners Eric Sosman <esosman@comcast-dot-net.invalid> - 2016-06-23 08:22 -0400
            Re: JavaFX: Bindings, StringConverters, and ChangeListeners Tassilo Horn <tsdh@gnu.org> - 2016-06-23 15:12 +0200
              Re: JavaFX: Bindings, StringConverters, and ChangeListeners Eric Sosman <esosman@comcast-dot-net.invalid> - 2016-06-23 10:56 -0400

#34690 — JavaFX: Bindings, StringConverters, and ChangeListeners

FromTassilo Horn <tsdh@gnu.org>
Date2016-06-22 15:18 +0200
SubjectJavaFX: Bindings, StringConverters, and ChangeListeners
Message-ID<87wplhbbvy.fsf@gnu.org>
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

[toc] | [next] | [standalone]


#34691

FromNigel Wade <nmw@ion.le.ac.uk>
Date2016-06-22 15:22 +0100
Message-ID<dsvl9oFqvv4U1@mid.individual.net>
In reply to#34690
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.



[toc] | [prev] | [next] | [standalone]


#34693

FromTassilo Horn <tsdh@gnu.org>
Date2016-06-23 08:39 +0200
Message-ID<877fdgl87c.fsf@gnu.org>
In reply to#34691
Nigel Wade <nmw@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

[toc] | [prev] | [next] | [standalone]


#34695

FromMartin Gregorie <martin@address-in-sig.invalid>
Date2016-06-23 09:59 +0000
Message-ID<nkgbuu$l6u$1@dont-email.me>
In reply to#34693
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       |

[toc] | [prev] | [next] | [standalone]


#34696

FromNigel Wade <nmw@ion.le.ac.uk>
Date2016-06-23 12:19 +0100
Message-ID<dt1utmFafi7U1@mid.individual.net>
In reply to#34695
On 23/06/16 10:59, Martin Gregorie wrote:
> 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
>
>

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.

[toc] | [prev] | [next] | [standalone]


#34697

FromNigel Wade <nmw@ion.le.ac.uk>
Date2016-06-23 12:39 +0100
Message-ID<dt203jFanfkU1@mid.individual.net>
In reply to#34693
On 23/06/16 07:39, Tassilo Horn wrote:
> Nigel Wade <nmw@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.

[toc] | [prev] | [next] | [standalone]


#34701

FromTassilo Horn <tsdh@gnu.org>
Date2016-06-23 14:39 +0200
Message-ID<87y45wjcyr.fsf@gnu.org>
In reply to#34697
Nigel Wade <nmw@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

[toc] | [prev] | [next] | [standalone]


#34692

FromEric Douglas <e.d.programmer@gmail.com>
Date2016-06-22 08:06 -0700
Message-ID<cf173549-4d73-4f6a-9d3f-031a070ce34c@googlegroups.com>
In reply to#34690
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.

[toc] | [prev] | [next] | [standalone]


#34694

FromTassilo Horn <tsdh@gnu.org>
Date2016-06-23 08:45 +0200
Message-ID<8737o4l7xo.fsf@gnu.org>
In reply to#34692
Eric Douglas <e.d.programmer@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

[toc] | [prev] | [next] | [standalone]


#34698

FromEric Douglas <e.d.programmer@gmail.com>
Date2016-06-23 05:11 -0700
Message-ID<cfe580ca-be4d-48db-9cdb-6ae8d059511c@googlegroups.com>
In reply to#34694
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.

[toc] | [prev] | [next] | [standalone]


#34700

FromEric Sosman <esosman@comcast-dot-net.invalid>
Date2016-06-23 08:22 -0400
Message-ID<nkgkao$k1b$1@dont-email.me>
In reply to#34698
On 6/23/2016 8:11 AM, Eric Douglas wrote:
> 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.

     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.

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

[toc] | [prev] | [next] | [standalone]


#34702

FromTassilo Horn <tsdh@gnu.org>
Date2016-06-23 15:12 +0200
Message-ID<87twgkjbge.fsf@gnu.org>
In reply to#34700
Eric Sosman <esosman@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

[toc] | [prev] | [next] | [standalone]


#34703

FromEric Sosman <esosman@comcast-dot-net.invalid>
Date2016-06-23 10:56 -0400
Message-ID<nkgtbj$kba$1@dont-email.me>
In reply to#34702
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 ...

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

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.java.programmer


csiph-web