I am currently developing some custom JavaFX components in my application and stumbled upon an issue with the binding.
The following is a component which allows to rate an item with one to five stars (Rating is an enum with the possible ratings).
public final class RatingControl extends HBox {
private final ReadOnlyObjectWrapper<Rating> ratingProperty = new ReadOnlyObjectWrapper<>( Rating.NO_RATING );
public RatingControl( final Rating initialRating ) {
createVisibleStars( );
ratingProperty.set( initialRating );
ratingProperty.addListener( ( observable, oldValue, newValue ) -> updateVisibleStars( newValue ) );
updateVisibleStars( ratingProperty.get( ) );
}
private void createVisibleStars( ) {
// omitted
}
private void onStarClicked( final Rating rating ) {
if ( ratingProperty.get( ) == rating ) {
setRating( Rating.NO_RATING );
} else {
setRating( rating );
}
}
private void updateVisibleStars( final Rating newValue ) {
// omitted
}
public ReadOnlyObjectProperty<Rating> ratingProperty( ) {
return ratingProperty.getReadOnlyProperty( );
}
public void setRating( final Rating rating ) {
ratingProperty.set( rating );
}
}
So my idea is as following: A click on a star changes the internal state (ratingProperty) by calling setRating. This can also be triggered from the outside. An update of this property changes the visual representation and makes sure that the visible rating is updated as well. This, again, can also be read readonly from the outside (via ratingProperty( )).
This works, but I am not happy with it. The calling component has to do something like the following (assuming that model has a Property named rating):
final RatingControl ratingControl = new RatingControl( model.rating( ).getValue( ) );
ratingControl.ratingProperty( ).addListener( ( observable, oldValue, newValue ) -> model.rating( ).set( newValue ) );
Using "normal" JavaFX components one would assume that a binding like the following is possible:
final RatingControl ratingControl = new RatingControl( );
ratingControl.ratingProperty( ).bindBidirectional( model.rating( ) );
final RatingControl ratingControl = new RatingControl( );
ratingControl.ratingProperty( ).bind( model.rating( ) );
So I wonder how I would achieve this. If I simply allow access to my internal property, I would not be able to set it when clicking a star, as a bound property cannot be set ("A bound value cannot be set).