如何将CheckMenuItem.selectedProperty()绑定到另一个ObservableValue?

时间:2017-05-23 15:31:24

标签: java javafx binding menuitem

我想将CheckMenuItem' s selectedProperty绑定到另一个可观察值,例如cmi.selectedProperty().bind(myObs)。但是,这是不可能的,因为框架在单击检查菜单项时设置selection属性(请参阅ContextMenuContent.java的line 1394)。

有没有办法拦截点击 - 这样我可以自己进行自定义处理 - 并仍然将选择属性绑定到另一个可观察对象?

我想我将点击视为更新某个州的请求。用户单击菜单项,然后程序尝试相应地更改某个状态,如果状态成功更新,则选择会更改。在' normal'条件,检查应在每次点击时切换;但是,如果发生了不好的事情,我更倾向于检查不会切换,而是反映程序的真实状态。

1 个答案:

答案 0 :(得分:1)

执行此操作的一种方法(无需编写菜单项的外观)是使用图形滚动自己的菜单项。您可以只使用图形区域并从标准的模式样式表中窃取CSS。然后将图形的visible属性绑定到observable值,并在菜单项的action处理程序中切换observable值:

import java.util.Random;

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Region;
import javafx.stage.Stage;

public class VetoableMenuItemWithCheck extends Application {

    @Override
    public void start(Stage primaryStage) {
        BorderPane root = new BorderPane();
        MenuBar menuBar = new MenuBar() ;
        Menu choices = new Menu("Choices");

        // observable boolean value to which we're going to bind:
        BooleanProperty selected = new SimpleBooleanProperty();

        // graphic for displaying checkmark
        Region checkmark = new Region();
        checkmark.getStyleClass().add("check-mark");

        // bind visibility of graphic to observable value:
        checkmark.visibleProperty().bind(selected);

        MenuItem option = new MenuItem("Option", checkmark);

        choices.getItems().add(option);

        Random rng = new Random();

        // when menu item action occurs, randomly fail (with error alert),
        // or update boolean property (which will result in toggling check mark):
        option.setOnAction(e -> {
            if (rng.nextDouble() < 0.25) {
                Alert alert = new Alert(AlertType.ERROR, "I'm sorry Dave, I'm afraid I can't do that", ButtonType.OK);
                alert.showAndWait();
            } else {
                selected.set(! selected.get());
            }
        });

        menuBar.getMenus().add(choices);
        root.setTop(menuBar);

        Scene scene = new Scene(root, 400, 400);
        scene.getStylesheets().add("check-menu.css");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

签menu.css:

.check-mark {
    -fx-background-color: -fx-mark-color;
    -fx-shape: "M0,5H2L4,8L8,0H10L5,10H3Z";
    -fx-scale-shape: false;
    -fx-padding: 0em 0.11777em 0em 0em;
}

可能有一种更简单的方法,但这似乎并不太糟糕。

否决广播菜单项的版本可以遵循相同的基本想法,但使用

ObjectProperty<MenuItem> selectedItem = new SimpleObjectProperty<>();

然后为每个菜单项执行

checkmark.visibleProperty().bind(selectedItem.isEqualTo(option));

option.setOnAction(e -> {
    if (successful()) {
        selectedItem.set(option);
    }
});