Bind ToggleGroup bidirectionally in javafx

I don't think there is a way to do this directly. While a general-purpose

Bindings.bindBidirectional(Property<S> property1, Property<T> property2, Function<S,T> mapping, Function<T,S> inverseMapping)

might make a good addition to the API, even that wouldn't help in this case as the ToggleGroup's selectedProperty is read only (since selection needs to be handled when each Toggle's setSelected(...) method is invoked, as well as by the ToggleGroup's selectedProperty).

Using a couple of listeners is the way to go in this case.

The closest thing to the "custom bidirectional map" is the

Bindings.bindBiDirectional(StringProperty stringProperty, ObjectProperty<T> otherProperty, StringConverter<T> converter)

method. In the case where you have an (writeable) ObjectProperty<S> and (writeable) ObjectProperty<T> you can in theory use two bidirectional bindings and an intermediate StringProperty to bind them together. In practice, this is almost always more code than just using two listeners, and is also less efficient.


I have successfully made use of the ToggleGroupValue class in the JFXtras project.

Here is an example:

import java.util.Arrays;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class Main extends Application {
    Child myChild = new Child();
    @Override
    public void start( Stage stage ) throws Exception {
        stage.setTitle( "ToggleGroupValue Example" );
        GridPane gridPane = new GridPane();
        int rowIndex = 0;
        gridPane.add( new Label("Nickname: "), 0, rowIndex );
        
        ToggleGroupValue toggleGroupValue = new ToggleGroupValue();
        rowIndex = createAddRadioButtons( gridPane, rowIndex, toggleGroupValue );
        
        gridPane.add( new Label("Selected Nickname: "), 0, rowIndex );
        Label selectedNickNameValueLabel = new Label();
        gridPane.add( selectedNickNameValueLabel, 1, rowIndex );
        
        myChild.nicknameProperty().bindBidirectional( toggleGroupValue.valueProperty() );
        selectedNickNameValueLabel.textProperty().bind( toggleGroupValue.valueProperty() );
        
        stage.setScene( new Scene( gridPane, 300, 100 ) );
        stage.show();
    }

    private int createAddRadioButtons( GridPane gridPane, int rowIndex, ToggleGroupValue toggleGroupValue ) {
        RadioButton radioButtonPunkin = new RadioButton();
        radioButtonPunkin.setUserData( "Punkin" );
        RadioButton radioButtonLittleBoy = new RadioButton();
        radioButtonLittleBoy.setUserData( "Little Boy" );
        RadioButton radioButtonBuddy = new RadioButton();
        radioButtonBuddy.setUserData( "Buddy" );
        List<RadioButton> radioButtons = Arrays.asList( radioButtonPunkin, radioButtonLittleBoy, radioButtonBuddy );
        for ( RadioButton radioButton : radioButtons ) {
            toggleGroupValue.add( radioButton, radioButton.getUserData() );
            radioButton.setText( radioButton.getUserData().toString() );
            gridPane.add( radioButton, 1, rowIndex++ );
        }
        return rowIndex;
    }

    private static class Child {
        private StringProperty nickname = new SimpleStringProperty();
        public StringProperty nicknameProperty() {
            return nickname;
        }
        public String getNickname() {
            return nickname.get();
        }
        public void setNickname( String notesProperty ) {
            this.nickname.set( notesProperty );
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

screenshot of javafx example application