# JavaFX Größe der Parentknoten



## Goldfish (24. Nov 2013)

Hi, ich komm mal wieder mit einer Kleinigkeit nicht weiter...

Ich habe ne simple fxml-Datei gebaut, mit nur 8 Elementen im Moment. Oben sind ein paar Menüdinger und unten sollen zwei große Zeichenflächen sein. eine links und eine rechts, welche die komplette Größe des Feldes ausnutzen, die übrig bleibt. Die beiden Zeichenflächen (Canvas) habe ich in einer HBox verstaut (das geschieht später im Code). 
Was ich jetzt nicht hinbekomme, ist die Zeichenflächen an die Größe des Parents zu binden... ich hab alles mögliche getestet, aber nichts geht... die widthProperty des Parents, selbst bis hoch zum root gibt immer 0 zurück für alle Knoten. Für alle MaxWidth oder Height oder ähnliche Werte jeweils -1. Wie kann ich diese blöden Zeichenflächen an die Größe ihres Parents binden?
Da widthProperty immer 0 gibt, funktioniert das hinzufügen eines ChangeListeners leider auch nicht... ich weiß hier echt nicht mehr weiter...


----------



## dzim (25. Nov 2013)

Sorry, aber ich kann mir deine Oberfläche gerade nicht vorstellen... Screenshot? Skizze?

Hast du schon versucht dir eine UI mit dem SceneBuilder zuammenzubauen?

Da ich unter Eclipse entwickle und ich FXML als solches etwas unglücklich finde, verwende ich von e(fx)clipse die schlange DSL "FXGraph". Das wird parallel nach FXML übersetzt.

Schick also auch vielleicht das FXML dann schaue ich mal, ob ich die Zeit dafür finde - vielleicht zwar erst morgen, aber egal...


----------



## Goldfish (25. Nov 2013)

Ich habd ie Oberfläche mittels SceneBuilder gebaut.
Im folgenden ist das FXML dazu.

[XML]
<StackPane id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" fx:controller="main.view.MainViewController">
  <children>
    <VBox id="VBox" alignment="CENTER">
      <children>
        <Label graphicTextGap="0.0" text="Zeichenflächen" textFill="linear-gradient(from 0.0% 0.0% to 100.0% 100.0%, 0x20b2aaff 0.0%, 0x90ee90ff 30.0%, 0xffffe0ff 100.0%)">
          <effect>
            <DropShadow blurType="ONE_PASS_BOX" height="26.81845238095238" radius="12.466517857142858" spread="0.5238095238095238" width="25.047619047619047">
              <input>
                <InnerShadow choke="0.1746031746031746" color="#fffb99" height="5.0" radius="2.0" width="5.0" />
              </input>
            </DropShadow>
          </effect>
          <font>
            <Font size="25.0" />
          </font>
        </Label>
        <HBox id="HBox" fx:id="drawingContainer" alignment="CENTER" VBox.vgrow="ALWAYS" />
      </children>
      <padding>
        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
      </padding>
    </VBox>
  </children>
  <stylesheets>
    <URL value="@css/MainView.css" />
  </stylesheets>
</StackPane>
[/XML]

Die Canvas Flächen werden dann im Code mittels new Canvas erzeugt und die beiden Zeichenflächen sollen nun ind ie HBox mit der ID "drawingContainer eingefügt werden." Da das Fenster natürlich auch jederzeit dynamisch die Größe ändern können soll, sollen sich die Zeichenflächen immer an die Größe der HBox anpassen. Aber das krieg ich halt nicht hin, weil die Größen bis hin zum StackPane alle so angegeben sind, wie ichs oben im ersten Beitrag genannt habe...


----------



## dzim (26. Nov 2013)

Ok. Ich habe es einmal importiert, dein Stylesheet und deinen Controller entfernt und konnte es mir immerhin anschauen.
Mir fallen aber spontan einige Punkte auf:

 *-Infinity* - mir ist es bis jetzt noch nicht untergekommen, das ich das brauchte - wird das von SceneBuilder angelegt? Wenn ja, dann ist es wohl in Ordnung, wenn nein, entferne es mal
 *pref size* - ist das dein Root-Layout? Ich würde die initiale Größe lieber in der Main-Klasse, beim Anlegen der Scene angeben: [c]Scene scene = new Scene(root, 960, 500);[/c] oder in der Primary Stage, bevor du es anzeigst: [c]primaryStage.setScene(scene); primaryStage.setMinWidth(960); primaryStage.setMinHeight(500); primaryStage.show();[/c]
 *StackLayout* als Root-Layout - Das Layout ist ganz nett, wenn du absichtlich Elemente überlagern möchtest, aber bei dir ist eher Sinnlos: Du hast nur ein Child - 

Ich hab das Stylesheet für die Vorschau immer im FXML so eingebunden
[XML]
<?scenebuilder-preview-i18n-resource ../res/strings.properties?>
<?scenebuilder-stylesheet ../res/application.css?>
[/XML]
Ansonsten setze ich es im Code auf die von mir angelgte Scene (siehe oben)

```
scene.getStylesheets().add(getClass().getResource("res/application.css").toExternalForm());
```
Wobei ich denke, das deine Variante absolut in Ordnung ist!

Was mir übrigens gefällt und ich noch nicht so gemacht hab ist die DropShadow-Geschichte - auch wenn mir die Schrift zu bunt ist 
Was mir nicht gefällt ist: Du verwendest ein Stylsheet, den textFill deines Labels aber setzt du im FXML... Das ist nicht sinnvoll! du kann im CSS so etwas machen wie

```
/* root - für die Scene - hier kann man einige Variablen definieren oder einen Globalen Background setzen, etc. */
.root {
    my-color: linear-gradient(from 0.0% 0.0% to 100.0% 100.0%, 0x20b2aaff 0.0%, 0x90ee90ff 30.0%, 0xffffe0ff 100.0%);
}

/* ALLE labels haben immer die Standart-Farbe weiß, es sei denn, man weißt ihnen andere Farben zu */
/* z.B. über die IDs und entsprechende Einträge (sieh weiter unten) oder */
/* indem man im Code oder FXML die textFill Property ändert * /
.label {
    -fx-text-fill: white;
}

/* node mit der ID my-label bekommt diesen CSS-Eintrag zugewiesen */
#my-label {
    -fx-text-fill: my-color;
}
```

Für den Controller verwende die 
	
	
	
	





```
fx:id="someNode"
```
-Tags! Damit kannst du dann im Controller die 
Variante [c]@FXML private Node someNode;[/c] verwenden.
Um z.B. dein Label mit dem CSS-Eintrag *#my-label* zu verknüpfen, verwende im FXML dort 
	
	
	
	





```
id="my-label"
```
.
Auch wenn diese Varianten fast gleich aussehen, sind sie signifikant unterschiedlich und es ist im Code besser die über *@FXML* injizierten Nodes zu verwenden, als sie über ihre CSS-ID zu suchen.
Anmerkung: Ersetze *Node* durch z.B. *Label*, wenn du diese FXML-ID einem Label zuweist!

Meine Handllungempfehlung:
Entledige dich als erstes des sinnlosen StackLayouts. Dann überlege dir, wie du genau deine Applikation stylen willst (es ist sinnvoll das so weit wie möglich nur im CSS zu machen!).

So das war jetzt so viel auf einmal, dass ich erst mal aufhören muss... Ich muss auch mal wieder arbeiten 
Ich hoffe mein geistiger Erguss war etwas aufschlußreich!

Grüsse,
Daniel

PS: StackPane erlaubt, einem Child vgrow und hgrow zuzuordnen, wie du es bereits bei der HBox mittels _VBox.vgrow="ALWAYS"_ gemacht hast...


----------



## Goldfish (26. Nov 2013)

Nunja, die gesamte FXML ist mit SceneBuilder gebaut, da hab ich manuell nichts dran verändert.
Meine CSS-Datei verwende ich in erster Linie eigentlich um Klassengruppen zu stylen. Wohingegen ich in der FXML mittels SceneBuilder die Stylings für einzelne Elemente wie Überschrift und ähnliches festlege. Auf individuelle Elemente greif ich von der css Datei aus nur zu, wenn ich am rumprobieren bin, oder wenn ich auch noch nicht sagen kann, wies aussehen soll. In der CSS kann man den Kram halt am Schnellsten zentral bearbeiten.
Aber davon mal abgesehen, löst das Ganze ja noch lange nicht mein Problem.
Meine Main-Class sieht wie folgt aus:


```
public class MainClass extends Application {

    @Override
    public void start( Stage stage ) throws IOException {
        MainClass.stage = stage;
        final StackPane root = FXMLLoader.load(MainClass.class.getResource("view/MainView.fxml"));
        Scene scene = new Scene(root, 950, 600);
        stage.setScene(scene);
        stage.show();
    }

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

Das ist im Prinzip immer alles was ich in meinen JavaFX Anwendungen in der Main-Klasse habe und bin bisher auch immer gut damit ausgekommen.

Aber wie lös ich nun das Problem, dass ich keinerlei Größen auf meinen Elementen habe und ich die Canvas Flächen gerne an die parents binden würde?
Wie gesagt widthProperty ist bspw. stets 0 und an die widthProperty nen ChangeListener zu heften klappt aus irgendeinem Grund auch nicht, obwohl ich das schon erfolgreich in anderen Applikationen so gemacht habe, wo es auch funktioniert...

hier nochmal ein kurzer Überblick über meinen Controller entsprechend. Die ganzen Versuche die Zeichenflächen an die parents zu binden, hab ich schon wieder entfernt, also nicht wundern.


```
public class MainViewController
        implements Initializable {

    @FXML
    private HBox drawingContainer;


    @FXML //  fx:id="playFieldParent"
    private StackPane playFieldParent; // Value injected by FXMLLoader


    @Override // This method is called by the FXMLLoader when initialization is complete
    public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
        PlayField left = new ShipField(drawingContainer);
        PlayField right = new ShootField( drawingContainer );
        drawingContainer.getChildren().addAll(left, right);
    }

}
```

Dabei ist Playfield einfach nur von Canvas abgeleitet und darin passiert aktuell nichts. Es wird gerade nur der Parent gespeichert. Wie also krieg ich den Kram jetzt hin?


----------



## dzim (26. Nov 2013)

Anscheinend habe ich es nicht klar genug rüber bringen können: Ich vermute, das hier das StackPane das Problem verursacht.
Da es nur ein Kind hat - die VBox - ist es sinnlos! (In Android gibt es einen Check, der dich vor so etwas warnt)
Das allein aber ist es natürlich nicht - so wie du es hier verwendest, mag das StackPane zwar die volle Größe der Scene einnehmen, die darin enthaltene VBox aber nicht. Ich hatte zwar anscheinend  unrecht damit, das StackPane auch vgrow und hgrow unterstützen - entschuldige das bitte! - aber du hast keine maxWidth an deine VBox gehängt. Das würde ich einmal versuchen!

Um Probleme mit Layouts zu testen mache ich übrigens immer folgendes: Ich adde nach und nach von innen beginnend ein style-Property: [c]style="-fx-border-color: red"[/c]. Dann sehe ich, wo der Rand des betrachteten Layouts ist und kann den Fehler besser zurückverfolgen.

Kurze Erläuterung, warum ich das mache: Ich hab gestern auch vor einem ähnlichen Problem gestanden und wollte nun die Ursache finden. Ich verwende als Root ein BorderPane, im center ist der Inhalt, left und right sind "Menüs", die ich per slide-in/-out einblende oder verschwinden lasse. Die slide-Operation wurde per translateX gemacht - logisch.
Das Problem war: Ich hab vergessen, dass das Layout trotz translateX davon ausgeht, dass left und right noch in ihrer vollen Breite da wären und der Center somit nur wenig Platz hat. Erschwerend kam hinzu, dass auch center translateX verwendet (hatte - ist jetzt unnötig).
Ich hab es gelöst, indem ich im Controller die prefWidth von center an die width vom Root (bei dir das StackPane) gebunden hab und die width von center an prefWidth - et voilá - es funktioniert. Vielleicht würde dir das im Zweifelsfall auch helfen.


----------

