JavaFX - 为什么更改对话框的 header 文本会使按钮栏不可见?

JavaFX - Why does changing the header text of a dialog make the button bar invisible?

这是我的自定义对话框中的一个小示例,用于显示 运行 javafx.concurrent.Task.

的进度
DialogPane pane = this.getDialogPane()
pane.getButtonTypes().addAll(ButtonType.CANCEL);

pane.headerTextProperty().bind(task.titleProperty());
pane.contentTextProperty().bind(task.messageProperty());

由于某种原因,按钮栏中的按钮消失了,但只是在更新了一些文本之后。进一步调查,我发现绑定一个对话框的header文本似乎把按钮栏中的所有按钮都去掉了。为什么会发生这种情况,我该怎么做才能阻止按钮被隐藏?

编辑:这是一个演示问题的 MCVE。

import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import javafx.stage.Modality;
import javafx.stage.Stage;

public class MCVE extends Application {
  public static class CustomDialog extends Dialog<ButtonType> {

    public CustomDialog(Task<?> task) {
      this.initModality(Modality.APPLICATION_MODAL);

      DialogPane pane = this.getDialogPane();
      {
        pane.getButtonTypes().addAll(ButtonType.CANCEL);
        pane.headerTextProperty().bind(task.titleProperty());
      }

      setOnCloseRequest(event -> {
        if (task.isRunning()) event.consume();
      });
    }
  }

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

  @Override
  public void start(Stage stage) {
    StackPane root = new StackPane();
    Button starter = new Button("Showcase");
    starter.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);

    root.getChildren().add(starter);
    Scene scene = new Scene(root, 500, 500);
    stage.setScene(scene);
    stage.show();


    starter.setOnAction(event -> {
      Task<Void> task = new Task<>() {
        @Override
        protected Void call() throws Exception {
          updateTitle("Before loop");
          try {
            Thread.sleep(1000);
          } catch (InterruptedException ignored) {
            if (isCancelled()) return null;
          }
          for (int i = 1; i <= 20; i++) {
            updateTitle("loop " + i);

            try {
              Thread.sleep(1000);
            } catch (InterruptedException ignored) {
              if (isCancelled()) return null;
            }
          }
          return null;
        }
      };
      Thread worker = new Thread(task);

      worker.start();
      new CustomDialog(task).showAndWait();
    });
  }
}

看来问题需要在 JavaFX 端解决。事实上,按钮栏只是被推到了视线之外。虽然有一个相对不错的解决方法。 Dialog 内部使用样式化的 GridPane 来显示 header 文本和图形,因此我只是使用外部 GridPane 复制了它,而不是绑定到 textProperty() Label.

  public static class CustomDialog extends Dialog<ButtonType> {
    public CustomDialog(Task<?> task) {
      DialogPane pane = this.getDialogPane(); {
        pane.getButtonTypes().addAll(ButtonType.CANCEL);
        //construct custom header
        GridPane headerRoot = new GridPane(); {
          Label headerText = new Label();
          //headerText is the label containing the header text
          headerText.textProperty().bind(task.titleProperty());

          headerRoot.add(headerText, 0, 0);
        }
        headerRoot.getStyleClass().addAll("header-panel");
        pane.setHeader(headerRoot);

        pane.setContentText("Placeholder content");

        pane.getScene().getWindow().setOnCloseRequest(event -> {
          if (!task.isDone()) {
            event.consume();
          }
        });
      }
    }

看起来和默认对话框一模一样,没有上面问题提到的问题。