En mi aplicación JavaFX, estoy usando Checkboxes de verificación en TreeView para cambiar la visibilidad de los nodos.
Sin embargo, en un caso especial, se debe solicitar al usuario que confirme su selección, ya que pueden surgir problemas al activar una casilla de verificación específica. Se abre una ventana de diálogo en la que el usuario puede elegir entre "Sí" y "No". Si el usuario elige "Sí", los nodos se vuelven visibles y todo está bien. Pero si el usuario elige "No", la casilla de verificación debe desmarcarse nuevamente.
Mi idea era verificar la condición (en este caso, presione "no" en una ventana de diálogo) en ChangeListener y, si es cierto, establezca el valor seleccionado en falso.
Pero por alguna razón, no funcionó. Después de eso, descubrí que funciona con el método refresh() de TreeView .
refresh() , por qué se ignora setSelected() ?refresh() ? El uso de la línea refresh() mostrará el comportamiento deseado: la casilla de verificación permanece sin seleccionar después de hacer clic porque 5> 4 (5>4 simula, por ejemplo, presionar "no" en una ventana de diálogo).
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(); }; } }La solución de @VGR con
Platform.runLater(() -> item.setSelected(previousChoice));
funciona para el ejemplo reproducible mínimo, pero no parece ser la mejor manera de hacerlo. Como ya se discutió en los comentarios, con Platform.runLater() es
trabajando la mayor parte del tiempo
y
no hay garantía porque no se especifica el tiempo exacto, se interrumpirá si algo en los eventos pendientes también usa runlater.
Se desea una solución/solución alternativa que siempre funcione...
Está restableciendo el elemento de la casilla de verificación demasiado pronto. Todavía no ha terminado su procesamiento de eventos.
Desea esperar hasta que termine antes de restablecerlo. Use Platform.runLater para asegurarse de restablecerlo después de que se hayan procesado todos los eventos pendientes:
Platform.runLater(() -> item.setSelected(previousChoice));Lo que estamos viendo en el ejemplo de OP en realidad es el comportamiento estándar de las propiedades enlazadas bidireccionalmente, reducido a un ejemplo simple (sin interfaz de usuario, solo propiedades).
El escenario:
Solo para mostrar que, aunque no se recomienda, revertir el estado del remitente en un oyente está funcionando: establecemos la propiedad de datos en verdadero. Al final, tanto los datos como la interfaz de usuario son falsos como queremos.
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 / falsePara la simulación real del contexto del OP: configure la propiedad ui (el usuario hace clic en la casilla de verificación); al final, la propiedad de datos se revierte mientras que la propiedad ui no. Técnicamente, eso se hace en BidireccionalBinding que ignora los cambios entrantes durante la actualización.
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 / trueEl ejemplo:
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()); } }