SeanIdzenga
SeanIdzenga

Reputation: 111

Combobox fires action event when underlying data is modified

I've been puzzling over this one for a while now and just wanted to see if others had run into this problem before or if maybe I'm doing something wrong.

Given a javafx implementation in which we use a combobox whose items are set from an ObservableArrayList which can be updated, modified, replaced, etc. and a combobox with an action listener just logging out whenever it's triggered.

package sample;

import java.util.Arrays;
import java.util.List;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class Main extends Application {

    ObservableList<String> theList = FXCollections.observableArrayList();

    @Override
    public void start(Stage primaryStage) throws Exception{
        primaryStage.setTitle("Sample");
        FlowPane root = new FlowPane(Orientation.VERTICAL);
        root.setVgap(20);

        List<String> initialColors = Arrays.asList("red", "green", "blue", "black");
        theList.addAll(initialColors);

        ComboBox<String> theComboBox = new ComboBox<>();
        theComboBox.setItems(theList);
        theComboBox.setOnAction( event -> {
            System.out.println(String.format("theComboBox action listener triggered, current value is %s", theComboBox.getValue()));
        });
        
        Button bttn1 = new Button("Press me");
        bttn1.setOnAction(event -> {
            List<String> someColors = Arrays.asList("red", "orange", "mauve", "pink", "blue", "salmon", "chiffon");
            System.out.println("About to issue setAll against observable list");
            theList.setAll(someColors);
        });

        root.getChildren().add(theComboBox);
        root.getChildren().add(bttn1);

        primaryStage.setScene(new Scene(root, 100, 150));
        primaryStage.show();

        System.out.println("Setting initial selection to \"blue\"");
        theComboBox.setValue("blue");

    }


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

I think the action event should only be triggered when the user changes the combobox through a direct action but I'm seeing the event trigger when the observablelist is modified.

We're using javafx version 11.0.2

Should I log this as a bug with Gluon?

EDIT: Updated example

In this example you can see that whenever the underlying data is modified via setAll the action event is triggered. This would be fine if the action event had some sort of way to differentiate between whether it's from a direct user interaction or from programmatic changes to the underlying data.

Other strange behaviour, the action event won't trigger if you select "black" and then hit the "press me" button and the combobox will then select pink because it's in the same position in the list - but then select red or even pink again and you'll get null, red, null and null, pink, null respectively.

I'd expect the combobox to retain its value even if the selection is not present anymore and I also would not expect these events to be triggered when the observable list is being modified - if you needed/wanted to listen for events when the observable list is modified you can attach a listener directly to it instead.

Upvotes: 4

Views: 633

Answers (1)

SeanIdzenga
SeanIdzenga

Reputation: 111

Just to wrap this up, take a look through the comments on the question for more detail.

We think this is a bug in javafx 11.0.2 - I've filed a bug report through the openjdk jira.

A workaround for now is to set a flag boolean variable and only perform actions within the listener when it is true.

something as simple as adding:

private boolean actionEventsOn = true;
theComboBox.setOnAction( event -> {
    if(actionEventsOn){
        System.out.println(String.format("theComboBox action listener triggered, current value is %s", theComboBox.getValue()));
    }
});
bttn1.setOnAction(event -> {
    List<String> someColors = Arrays.asList("red", "orange", "mauve", "pink", "blue", "salmon", "chiffon");
    System.out.println("About to issue setAll against observable list");
    actionEventsOn = false;
    theList.setAll(someColors);
    actionEventsOn = true;
});

This should prevent action events triggering unnecessarily.

Upvotes: 2

Related Questions