The trickiness here is there is not a single property of the checkbox that you can listen or bind to for determining its current state. Instead there is both a selected property and an indeterminate property and the changes for those properties fire independently of each other. So if you write a binding or a change listener it will be fired with only partial information (e.g. if you check the indeterminate state in the selected property listener when the state changes from indeterminate to selected, then the indeterminate check will return true even though the checkbox is in the process of changing from indeterminate to selected). If there was a single state property you could listen to, this would be easier, but there is not. So what you need is some way to coalesce the property changes such that in a single method you can process the end state of the control after both the indeterminate and selected properties have changed. In that way you can correctly determine what to do. The way I have done that in the code below is by scheduling a runnable to be run later if either of the selected or indeterminate properties are changed. I also prevent the individual checkboxes from modifying the select all state if the runnable has been scheduled - this prevents the two different ways of modifying the select all state from interfering with each others processing.
An alternate implementation might be to use Tomas Mikula's ReactFX framework which has a provision for what he terms Inhibitable Bindings, which (if I understand their purpose correctly), would also allow you to accomplish the same task without resorting to a runnable implementation in your code. However, I haven't used ReactFX, so I couldn't say for sure if this would be a good fit for your problem.
posted 2 years ago
Huge thanks! This is perfect. I still need to analyse your code further, but I already learned new things, some of which I might use elsewhere in my code. I'll need a closer look at Runnable interface (something I avoided in the past) and the framework you referred to. I can now mark this thread as resolved.
I adjusted your code slightly to fit in my code and allow reusability. It works wonders. Thank you kindly.
The only (small) issue is that the indeterminate checkbox is smaller than its checked and unchecked states, which causes the GridPane to resize upon changing the state of Select All checkbox. It doesn't influence the usability of checkboxes so I'll let it be for the time being.