Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

216
Views
Reset checkbox selection in ChangeListener under condition

In my JavaFX application I'm using Checkboxes in a TreeView to change the visibility of nodes.

  • checkbox selected = some nodes are visible
  • checkbox deselected = some nodes are invisible

In a special case, however, the user should be prompted to confirm their selection, because problems can arise when activating a specific checkbox. A dialog window opens in which the user can choose between "Yes" and "No". If the user chooses "Yes", nodes become visible and everything is fine. But if the user chooses "No", the checkbox should be deselected again.

My idea was to check the condition (in this case press "no" in a dialog window) in the ChangeListener and if it's true, set the selected value to false.

But for whatever reason, it didn't work. After that, I figured out that it works with the refresh() method of the TreeView.

Questions

  1. Why doesn't it work without the refresh() method, why setSelected() is ignored?
  2. Should I use the refresh() method?
  3. Is there a better workaround to change the selection status?

Minimal reproducible example

Using the refresh() line will show the desired behavior: The checkbox remains unselected after clicking because 5 > 4 (5>4 simulates for example pressing "no" in a dialog window).

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.control.CheckTreeView;

public class HelloApplication extends Application {

    enum Names { TEST1, TEST2, TEST3, TEST4 }

    private final CheckTreeView<String> checkTreeView = new CheckTreeView<>();

    @Override
    public void start(Stage stage) {
        VBox vBox = new VBox();
        Scene scene = new Scene(vBox, 500, 500);
        setTreeView();
        vBox.getChildren().add(checkTreeView);
        stage.setScene(scene);
        stage.show();
    }

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


    public void setTreeView() {
        CheckBoxTreeItem<String> rootItem = new CheckBoxTreeItem<>("Root");
        rootItem.setExpanded(true);
        for (Names name : Names.values()) {
            CheckBoxTreeItem<String> item = new CheckBoxTreeItem<>(name.toString(), null);
            item.selectedProperty().addListener(this.onSelectionChanged(item));
            rootItem.getChildren().add(item);
        }
        this.checkTreeView.setRoot(rootItem);
    }

    private ChangeListener<Boolean> onSelectionChanged(CheckBoxTreeItem<String> item) {
        return (observableValue, previousChoice, newChoice) -> {

            if (newChoice) { // if checkbox is selected
                // if anything happens... for example press a "no" button in a dialog window
                if (5 > 4) {
                    System.out.println("reset checkbox status");
                    item.setSelected(previousChoice);
                }
            }

            // it works with refresh:
            // this.checkTreeView.refresh();
        };
    }
}

EDIT:

The solution of @VGR with

Platform.runLater(() -> item.setSelected(previousChoice));

works for the minimal reproducible example, but it doesn't seem to be the best way to do that. As already discussed in the comments, with Platform.runLater() it's

working most of the time

and

there's no guarantee because the exact timing is unspecified, will break f.i. if something in the pending events does also use runlater.

A solution / workaround that always works is desired...

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

You are resetting the checkbox item too soon. It hasn’t finished its event processing yet.

You want to wait until it’s finished before you reset it. Use Platform.runLater to make sure you reset it after all pending events have been processed:

Platform.runLater(() -> item.setSelected(previousChoice));
over 4 years ago · Santiago Trujillo Report

0

What we are seeing in the OPs example actually is the standard behavior of bidirectionally bound properties, reduced to a simple (no ui, just properties) example.

The scenario:

  • there are two boolean properties, representing the data (checkBoxTreeItem) and ui (checkBox) selected properties
  • they are wired the same way as in a checkBoxTreeCell, that is the ui is bidirectionally bound to the data
  • a listener to the data reverts the data to false on receiving a change from false -> true

Just for showing that - though nor recommended - reverting a the state of the sender in a listener is working: we set the data property to true. At the end, both data and ui are false as we want it.

before data.set(true)
    state of data/ui: false / false
enter listener to ui: true
enter listener to data: true
enter listener to ui: false
enter listener to data: false
 ... returning
listener to data - after reset false
after data.set(true) - state of data/ui: false / false

For the real simulation of the OP's context: set the ui property (user clicks the checkBox) - at the end, the data property is reverted while the ui property is not. Technically, that's done in BidirectionalBinding which ignores incoming changes while updating.

before ui.set(true)
    state of data/ui: false / false
enter listener to data: true
enter listener to data: false
 ... returning
listener to data - after reset false
enter listener to ui: true
after ui.set(true) - state of data/ui: false / true

The example:

public class BidiBindingUpdating {

    public static void main(String[] args) {
        BooleanProperty data = new SimpleBooleanProperty();
        BooleanProperty ui = new SimpleBooleanProperty();
        ui.bindBidirectional(data);

        // listener to item: revert to false
        data.addListener((src, ov, nv) -> {
            System.out.println("enter listener to data: " + nv);
            if (!nv) {
                System.out.println(" ... returning");
                return;
            }
            data.set(ov);
            System.out.println("listener to data - after reset " + data.get());
        });

        // listener to check: logging only
        ui.addListener((src, ov, nv) -> {
            System.out.println("enter listener to ui: " + nv);
        });

        // set item directly:
        System.out.println("before data.set(true)");
        System.out.println("    state of data/ui: " + data.get() + " / " + ui.get());
        data.set(true);
        System.out.println("after data.set(true) - state of data/ui: " + data.get() + " / " + ui.get());

        // set bound property:
        System.out.println("\nbefore ui.set(true)");
        System.out.println("    state of data/ui: " + data.get() + " / " + ui.get());
        ui.set(true);
        System.out.println("after ui.set(true) - state of data/ui: " + data.get() + " / " + ui.get());

    }
}
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!