This discussion is archived
2 Replies Latest reply: Oct 28, 2013 10:00 AM by AndreaVacondio RSS

Button in custom component not showing

AndreaVacondio Newbie
Currently Being Moderated

I made a very simple custom component with a TextField and Button but when I add multiple instances of it to a Layout, only the first Button shows up while the other show only when I focus the corresponding TextField. I'm quite new to fx and I'm not sure I did everything correctly but I don't see any obvious error in my code.

 

The component:

public class TestComponent extends BorderPane {
    @FXML
    private Button browseButton;
    @FXML
    private TextField textField;
    public TestComponent() {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("TestComponent.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);
        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }
}

 

The fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.HBox?>
<fx:root type="javafx.scene.layout.BorderPane" xmlns:fx="http://javafx.com/fxml/1"
    xmlns="http://javafx.com/javafx/2.2">
    <center>
        <TextField fx:id="textField" prefWidth="200.0" />
    </center>
    <right>
        <Button fx:id="browseButton" mnemonicParsing="false" maxHeight="-Infinity"
            minHeight="-Infinity" prefHeight="${textField.height}" text="Browse"
            textAlignment="CENTER"  />
    </right>
</fx:root>

 

and the test code

@Override
    public void start(Stage primaryStage) {
        VBox box = new VBox(5);
        box.setPadding(new Insets(5));
        TestComponent a = new TestComponent();
        TestComponent b = new TestComponent();
        TestComponent c = new TestComponent();
        box.getChildren().addAll(a, b, c);
        Scene scene = new Scene(box);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

 

I'm running on Ubuntu with jdk-8-ea-bin-b111-linux-i586-10_oct_2013. I tested with jdk 1.7.0_40 and the buttons don't show.

I'd include screenshots but the button to add images is disabled.

Thanks for the help

  • 1. Re: Button in custom component not showing
    jsmith Guru
    Currently Being Moderated

    The issue is with the bind definition in the FXML, if you remove that definition, the buttons will display.

       prefHeight="${textField.height}"

     

    I think the binding is working, but when there is some kind of error (bug) in the layout process such that the scene is not automatically laid out correctly when the binding occurs.

     

    You can get exactly the same behaviour by removing the binding definition in FXML and placing it in code after the load.

     

                browseButton.prefHeightProperty().bind(textField.heightProperty());

     

    When the scene is initially displayed, the height of all of the text fields is 0, as they have not been laid out yet, and the browser button prefHeight gets set to 0 through the binding.

    That's OK and expected.

    Then the scene is shown and a layout pass occurs, which sets the height of the text fields to 26 and the prefHeight of all of the browser buttons adjust correctly.

    That's also OK and expected.

    Next the height of one of the buttons is adjusted via a layout pass.

    That's also OK and expected.

    But the height of the other buttons is not adjusted to match their preferred height (probably because a layout pass is not run on them).

    That is not OK and not expected (and I think a bug).

     

    If you manually trigger a layout pass on one of the components which did not render completely, the button will be displayed - but that should not be necessary.

     

    You can file a bug against the Runtime project at:

       https://javafx-jira.kenai.com/

    You will need to sign up at the link on the login page, but anybody can sign up and log a bug.

     

    Here is some sample code.

     

    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    public class ComponentTestApp extends Application {
      @Override
      public void start(Stage primaryStage) {
        VBox box = new VBox(5);
        box.setPadding(new Insets(5));
        TestComponent a = new TestComponent();
        TestComponent b = new TestComponent();
        TestComponent c = new TestComponent();
        box.getChildren().addAll(a, b, c);
        Scene scene = new Scene(box);
        primaryStage.setScene(scene);
        primaryStage.show();
        b.requestLayout(); // I don't understand why this call is necessary -> looks like a bug to me . . .
      }
      public static void main(String[] args) {
        launch(args);
      }
    }

     

    ---------

     

    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.control.Button;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.BorderPane;
    import java.io.IOException;
    public class TestComponent extends BorderPane {
        private static int nextComponentNum = 1;
        private final int componentNum = nextComponentNum;
        @FXML
        private TextField textField;
        @FXML
        private Button browseButton;
        public TestComponent() {
          nextComponentNum++;
            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("TestComponent.fxml"));
            fxmlLoader.setRoot(this);  
            fxmlLoader.setController(this);  
            try {  
                fxmlLoader.load();
                browseButton.prefHeightProperty().bind(textField.heightProperty());
                System.out.println(componentNum + " " + browseButton + " prefHeight " + browseButton.getPrefHeight());
                textField.heightProperty().addListener(new ChangeListener<Number>() {
                  @Override
                  public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                    System.out.println(componentNum + " " + textField + " height " + newValue);
                  }
                });
                browseButton.prefHeightProperty().addListener(new ChangeListener<Number>() {
                  @Override
                  public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                    System.out.println(componentNum + " " + browseButton + " prefHeight " + newValue);
                  }
                });
                browseButton.heightProperty().addListener(new ChangeListener<Number>() {
                  @Override
                  public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                    System.out.println(componentNum + " " + browseButton + " height " + newValue);
                    new Exception("Not a real exception - just a debugging stack trace").printStackTrace();
                  }
                });
            } catch (IOException exception) {
                throw new RuntimeException(exception);  
            }  
        }  
    }  

     

    ---------

     

    <?xml version="1.0" encoding="UTF-8"?>
    <?import java.lang.*?>
    <?import javafx.scene.control.*?>
    <?import javafx.scene.layout.HBox?>
    <fx:root type="javafx.scene.layout.BorderPane" xmlns:fx="http://javafx.com/fxml/1"
             xmlns="http://javafx.com/javafx/2.2">
        <center>
            <TextField fx:id="textField" prefWidth="200.0" />
        </center>
        <right>
            <Button fx:id="browseButton" mnemonicParsing="false" maxHeight="-Infinity"
                    minHeight="-Infinity" text="Browse"
                    textAlignment="CENTER"  />
            <!--<Button fx:id="browseButton" mnemonicParsing="false" maxHeight="-Infinity"-->
                    <!--minHeight="-Infinity" prefHeight="${textField.height}" text="Browse"-->
                    <!--textAlignment="CENTER"  />-->
        </right>
    </fx:root>

     

    --------

     

    Here is the output of the sample code:

     

    1 Button[id=browseButton, styleClass=button]'Browse' prefHeight 0.0

    2 Button[id=browseButton, styleClass=button]'Browse' prefHeight 0.0

    3 Button[id=browseButton, styleClass=button]'Browse' prefHeight 0.0

    1 Button[id=browseButton, styleClass=button]'Browse' prefHeight 26.0

    1 TextField[id=textField, styleClass=text-input text-field] height 26.0

    2 Button[id=browseButton, styleClass=button]'Browse' prefHeight 26.0

    2 TextField[id=textField, styleClass=text-input text-field] height 26.0

    3 Button[id=browseButton, styleClass=button]'Browse' prefHeight 26.0

    3 TextField[id=textField, styleClass=text-input text-field] height 26.0

    2 Button[id=browseButton, styleClass=button]'Browse' height 26.0

    java.lang.Exception: Not a real exception - just a debugging stack trace

      at testcomponent.TestComponent$3.changed(TestComponent.java:69)

      at testcomponent.TestComponent$3.changed(TestComponent.java:65)

      at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:347)

      at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)

      at javafx.beans.property.ReadOnlyDoubleWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyDoubleWrapper.java:177)

      at javafx.beans.property.ReadOnlyDoubleWrapper.fireValueChangedEvent(ReadOnlyDoubleWrapper.java:143)

      at javafx.beans.property.DoublePropertyBase.markInvalid(DoublePropertyBase.java:113)

      at javafx.beans.property.DoublePropertyBase.set(DoublePropertyBase.java:146)

      at javafx.scene.layout.Region.setHeight(Region.java:915)

      at javafx.scene.layout.Region.resize(Region.java:1362)

      at javafx.scene.layout.BorderPane.layoutChildren(BorderPane.java:583)

      at javafx.scene.Parent.layout(Parent.java:1063)

      at javafx.scene.Parent.layout(Parent.java:1069)

      at javafx.scene.Scene.doLayoutPass(Scene.java:564)

      at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2341)

      at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:329)

      at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:479)

      at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:460)

      at com.sun.javafx.tk.quantum.QuantumToolkit$13.run(QuantumToolkit.java:327)

      at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)

    java.lang.Exception: Not a real exception - just a debugging stack trace

      at testcomponent.TestComponent$3.changed(TestComponent.java:69)

      at testcomponent.TestComponent$3.changed(TestComponent.java:65)

      at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:347)

      at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)

      at javafx.beans.property.ReadOnlyDoubleWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyDoubleWrapper.java:177)

      at javafx.beans.property.ReadOnlyDoubleWrapper.fireValueChangedEvent(ReadOnlyDoubleWrapper.java:143)

      at javafx.beans.property.DoublePropertyBase.markInvalid(DoublePropertyBase.java:113)

      at javafx.beans.property.DoublePropertyBase.set(DoublePropertyBase.java:146)

      at javafx.scene.layout.Region.setHeight(Region.java:915)

      at javafx.scene.layout.Region.resize(Region.java:1362)

      at javafx.scene.layout.BorderPane.layoutChildren(BorderPane.java:583)

      at javafx.scene.Parent.layout(Parent.java:1063)

      at javafx.scene.Parent.layout(Parent.java:1069)

      at javafx.scene.Scene.doLayoutPass(Scene.java:564)

      at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2341)

      at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:329)

      at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:479)

      at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:460)

      at com.sun.javafx.tk.quantum.QuantumToolkit$13.run(QuantumToolkit.java:327)

      at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)

    1 Button[id=browseButton, styleClass=button]'Browse' height 26.0

  • 2. Re: Button in custom component not showing
    AndreaVacondio Newbie
    Currently Being Moderated

    Thanks for the answer,

    it seemed a but to me too but since I'm kind of new to JavaFX I wasn't sure and I decided to post here before opening an issue on JIRA.

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points