Home > Mobile >  Set ComboBox to read only without disabling the ComboBox using JavaFX
Set ComboBox to read only without disabling the ComboBox using JavaFX

Time:09-07

I do not want to disable the ComboBox because I want the user to be able to select the ComboBox button and look through the ComboBox items. And if the user tries selecting an item in the ComboBox, a window should pop up saying the user is in read only mode and the ComboBox should still have the original item in the ComboBox button cell.

Is there a way to do this?

By the way, I saw a previous post that asks this same question but using CheckComboBox from ControlsFX. But since I'm using a normal ComboBox from JavaFX 8, the solution from that post does not apply to a standard ComboBox.

Here's a minimal reproducible code example:

public class Main extends Application {
Stage window;
Scene scene;
public static void main(String[] args) {
    launch(args);
}

@Override
public void start(Stage primaryStage) {
    window = primaryStage;
    window.setTitle("Read Only ComboBox");

    ObservableList<String> strings = FXCollections.observableArrayList();
    for (int i = 0; i <= 10; i  )
        strings.add("Item "   i);

    // Create the ComboBox with the data
    ComboBox<String> comboBox = new ComboBox<>(strings);
    comboBox.getSelectionModel().select(3);

    // Set comboBox to read only

    HBox layout = new HBox(10);
    layout.setPadding(new Insets(20, 20, 20,20));
    layout.getChildren().addAll(comboBox);

    scene = new Scene(layout, 300, 250);
    window.setScene(scene);
    window.show();
    }
}

And I'm trying to get the ComboBox to look like this when a user selects the ComboBox button cell. And then once they select any item, a window should pop up saying they are in Read Only mode and the ComboBox should still have "Item 3" selected.

Edit 1: Here is the full stacktrace using Abra's code. I did not modify any of the code.

Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException
at com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList.subList(ReadOnlyUnbackedObservableList.java:136)
at javafx.collections.ListChangeListener$Change.getAddedSubList(ListChangeListener.java:242)
at com.sun.javafx.scene.control.behavior.ListViewBehavior.lambda$new$59(ListViewBehavior.java:269)
at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList.callObservers(ReadOnlyUnbackedObservableList.java:75)
at javafx.scene.control.MultipleSelectionModelBase.clearAndSelect(MultipleSelectionModelBase.java:378)
at javafx.scene.control.ListView$ListViewBitSetSelectionModel.clearAndSelect(ListView.java:1403)
at com.sun.javafx.scene.control.behavior.CellBehaviorBase.simpleSelect(CellBehaviorBase.java:256)
at com.sun.javafx.scene.control.behavior.CellBehaviorBase.doSelect(CellBehaviorBase.java:220)
at com.sun.javafx.scene.control.behavior.CellBehaviorBase.mousePressed(CellBehaviorBase.java:150)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:95)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:394)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$358(GlassViewEventHandler.java:432)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:431)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:937)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$152(WinApplication.java:177)
at java.lang.Thread.run(Thread.java:748)

CodePudding user response:

I believe the ComboBox in JavaFX8 has an editable property which should do exactually what you want?

combobox.editableProperty().bind(combobox.valueProperty().isEqualTo("item 3"));

CodePudding user response:

Add a ChangeListener to the selection model of the ComboBox. Whenever the selection changes, the below code restores the original selection and displays a message. Note that I arbitrarily set the initial ComboBox selection to the first item in its list of values.

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ComboBox;
import javafx.scene.control.SingleSelectionModel;
import javafx.stage.Stage;

public class ApplicationMain extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        ComboBox<String> combo = new ComboBox<>(FXCollections.observableArrayList("First",
                                                                                  "Second",
                                                                                  "Third",
                                                                                  "Fourth",
                                                                                  "Last"));
        SingleSelectionModel<String> selectionModel = combo.getSelectionModel();
        selectionModel.select(0);
        selectionModel.selectedItemProperty().addListener(new ChangeListener<String>() {
            boolean flag = true;
            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
                if (flag) {
                    flag = false;
                    selectionModel.select(oldValue);
                    flag = true;
                    Alert alert = new Alert(AlertType.WARNING, "Read only mode.", ButtonType.CLOSE);
                    alert.setHeaderText(null);
                    alert.showAndWait();
                }
            }
        });
        Group root = new Group(combo);
        Scene scene = new Scene(root, 250.0D, 60.0D);
        stage.setTitle("Example");
        stage.setScene(scene);
        stage.show();
    }

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

CodePudding user response:

Here is a rough solution that will capture the event and revert it to the default selection and we can do whatever else we want like showing a message. If the application is not in read only mode, then we can process the action as normal:

//Value to track read only mode
Boolean readOnlyMode = true;

//Values to help revert the selection
int defaultSelection = 3;
//We use a flag so that the message is not displayed twice when we reset the selection
//You can remove then need for this by checking the item that was selected instead and only show the message if it was not te default item
boolean flagToggle = false;

//Add an event to revert the selection and show a message
comboBox.setOnAction((ActionEvent t) ->
{
    if(readOnlyMode){
        //Code here to show a message and revert the selection
        if(!flagToggle){
            //Replace this line with your pop up dialogue, etc
            System.out.println("The application is in Read Only mode");
            //flip the flag for displaying the message
            flagToggle = true;
        }
        //restore the flag
        else{
            flagToggle = false;
        }

        //reset the selection to default, if the selection is already back to the default then this will not trigger another selection event
        comboBox.getSelectionModel().select(defaultSelection);
    }
    else{
        System.out.println("The application is in Edit mode. Item "  comboBox.getValue()   " selected.");
        //perform normal actions
        //call some method here?
    }
});
  • Related