Search code examples
javajavafx-2

How to add border to panel of javafx?


I am developing a application where I need some widgets to wrap up inside a panel. And I want to put a border around it. I am a swt programmer where in composite I can pass swt.border to put border. But how to do that in JavaFX. Any help on this is appreciated.

CODE:

Label Center=new Label();
Center.setText("Center Frequency");

GridPane.setConstraints(Center, 0, 0);
tb1[i].getChildren().add(Center);
TextField text=new TextField();
GridPane.setConstraints(text, 1, 0);
tb1[i].getChildren().add(text);

Label chiprate=new Label();
chiprate.setText("Chiprate");

GridPane.setConstraints(chiprate, 0, 1);
tb1[i].getChildren().add(chiprate);
TextField chip=new TextField();
GridPane.setConstraints(chip, 1, 1);
tb1[i].getChildren().add(chip);

Label frequency=new Label();
frequency.setText("Frequency deviation");

GridPane.setConstraints(frequency, 0, 2);
tb1[i].getChildren().add(frequency);
TextField frequencydeviation=new TextField();
GridPane.setConstraints(frequencydeviation, 1, 2);
tb1[i].getChildren().add(frequencydeviation);

Label outputLabel=new Label();
outputLabel.setText("Output Power");

GridPane.setConstraints(outputLabel, 0, 3);
tb1[i].getChildren().add(outputLabel);
TextField output=new TextField();
GridPane.setConstraints(output, 1, 3);
tb1[i].getChildren().add(output);

Solution

  • I create a BorderedTitledPane class which places a titled border around content.

    If you don't need to have a title placing a border around things is even easier - just set the the css border parameters on a region (e.g. -fx-border-color: black;).

    Here is a complete executable sample.

    borderedtitledpane

    import javafx.beans.InvalidationListener;
    import javafx.beans.Observable;
    import javafx.beans.binding.Bindings;
    import javafx.beans.property.ObjectProperty;
    import javafx.beans.property.SimpleObjectProperty;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Node;
    import javafx.scene.control.Label;
    import javafx.scene.layout.StackPane;
    
    /** Places content in a bordered pane with a title. */
    public class BorderedTitledPane extends StackPane {
      private StringProperty title = new SimpleStringProperty();
      private ObjectProperty<Node> graphic = new SimpleObjectProperty<>();
      private ObjectProperty<Node> content = new SimpleObjectProperty<>();
      private ObjectProperty<Pos>  titleAlignment = new SimpleObjectProperty<>();
      // todo other than TOP_LEFT other alignments aren't really supported correctly, due to translation fudge for indentation of the title label in css => best to implement layoutChildren and handle layout there.
      // todo work out how to make content the default node for fxml so you don't need to write a <content></content> tag.
    
      public BorderedTitledPane() {
        this("", null);
      }
    
      public BorderedTitledPane(String titleString, Node contentNode) {
        final Label titleLabel = new Label();
        titleLabel.textProperty().bind(Bindings.concat(title, " "));
        titleLabel.getStyleClass().add("bordered-titled-title");
        titleLabel.graphicProperty().bind(graphic);
    
        titleAlignment.addListener(new InvalidationListener() {
          @Override
          public void invalidated(Observable observable) {
            StackPane.setAlignment(titleLabel, titleAlignment.get());
          }
        });
    
        final StackPane contentPane = new StackPane();
    
        getStyleClass().add("bordered-titled-border");
        getChildren().addAll(titleLabel, contentPane);
    
        content.addListener(new InvalidationListener() {
          @Override
          public void invalidated(Observable observable) {
            if (content.get() == null) {
              contentPane.getChildren().clear();
            } else {
              if (!content.get().getStyleClass().contains("bordered-titled-content")) {
                content.get().getStyleClass().add("bordered-titled-content");  // todo would be nice to remove this style class when it is no longer required.
              }
              contentPane.getChildren().setAll(content.get());
            }
          }
        });
    
        titleAlignment.set(Pos.TOP_LEFT);
        this.title.set(titleString);
        this.content.set(contentNode);
      }
    
      public String getTitle() {
        return title.get();
      }
    
      public StringProperty getTitleStringProperty() {
        return title;
      }
    
      public void setTitle(String title) {
        this.title.set(title);
      }
    
      public Pos getTitleAlignment() {
        return titleAlignment.get();
      }
    
      public ObjectProperty<Pos> titleAlignmentProperty() {
        return titleAlignment;
      }
    
      public void setTitleAlignment(Pos titleAlignment) {
        this.titleAlignment.set(titleAlignment);
      }
    
      public Node getContent() {
        return content.get();
      }
    
      public ObjectProperty<Node> contentProperty() {
        return content;
      }
    
      public void setContent(Node content) {
        this.content.set(content);
      }
    
      public Node getGraphic() {
        return graphic.get();
      }
    
      public ObjectProperty<Node> graphicProperty() {
        return graphic;
      }
    
      public void setGraphic(Node graphic) {
        this.graphic.set(graphic);
      }
    }
    

    Related CSS.

    .bordered-titled-title {
      -fx-translate-x:  8;
      -fx-translate-y: -10;
      -fx-padding: 0 0 0 4;
      -fx-background-color: -fx-background;
    }
    
    .bordered-titled-border {
      -fx-content-display: top;
      -fx-border-insets: 2 0 0 0;
      -fx-border-color: -fx-text-box-border;
      -fx-border-width: 2;
    }
    
    .bordered-titled-content {
      -fx-padding: 18 5 5 5;
    }