Reputation: 3186
I don't know if I miss something, but I cannot manage to achieve that, I have the same label twice or more on the same view. I don't really want to duplicate it just use the same label with the same text/tooltip/style.
A simple example:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Label?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="stackoverflow.dummy.Controller">
<Label fx:id="myLabel"/>
<!--<Label fx:id="myLabel"/>--> ofc this doesn't work.
<!--<Label fx:id="myLabel"/>-->
</AnchorPane>
If I try with <fx:reference>
it appears only once, if I try with <fx:copy>
it says I need a copy constructor, so I think I simply miss some easy solution. It should exist.
Note: I don't really want want to duplicate the code like: myLabel1, myLabel2, ..., since all of them are having the same text/tooltip/style.
I also know I can create a separate .fxml
file where I build the label and use it via <fx:include>
but I would prefer solving it in the same .fxml
since it is a really simple thing I think it doesn't worth creating a new .fxml
only for this.
Upvotes: 1
Views: 1699
Reputation: 46116
It is not possible to add the same Label
multiple times to the scene graph. This is documented by Node
(emphasis mine):
A node may occur at most once anywhere in the scene graph. Specifically, a node must appear no more than once in all of the following: as the root node of a
Scene
, the childrenObservableList
of aParent
, or as the clip of aNode
.The scene graph must not have cycles. A cycle would exist if a node is an ancestor of itself in the tree, considering the
Group
contentObservableList
,Parent
childrenObservableList
, andNode
clip relationships mentioned above.If a program adds a child node to a
Parent
(includingGroup
,Region
, etc) and that node is already a child of a differentParent
or the root of aScene
, the node is automatically (and silently) removed from its former parent. If a program attempts to modify the scene graph in any other way that violates the above rules, an exception is thrown, the modification attempt is ignored and the scene graph is restored to its previous state.
The emphasized part explains why the Label
only appears once when using <fx:reference>
.
The only solution to your problem is to duplicate the Label
. There are at least two options you can use to accomplish this.
<fx:copy>
One way to do this by using <fx:copy>
(emphasis mine):
The
<fx:copy>
element creates a copy of an existing element. Like<fx:reference>
, it is used with thefx:id
attribute or a script variable. The element's "source" attribute specifies the name of the object that will be copied. The source type must define a copy constructor that will be used to construct the copy from the source value.At the moment, no JavaFX platform classes provide such a copy constructor, so this element is provided primarily for use by application developers. This may change in a future release.
As stated in the documentation, using <fx:copy>
requires the class to have a copy constructor. The documentation also states that none of the core JavaFX classes provide copy constructors. This means you'll have to subclass Label
and provide the needed constructor, as shown in funkyjelly's answer. To ensure the properties stay up-to-date you can bind the properties inside the constructor:
public class CopyableLabel extends Label {
public CopyableLabel(CopyableLabel label) {
// You only mentioned the text, tooltip, and style properties
// in your question. Bind more properties as needed.
textProperty().bind(label.textProperty());
tooltipProperty().bind(label.tooltipProperty());
styleProperty().bind(label.styleProperty());
}
}
That way you only need to inject the "master" Label
and any updates to it will be propagated to all the copies. Using this design you must keep in mind that bound properties cannot be set directly; trying to set a bound property will result in exceptions. You might want to document the constructor will bind the properties, not just copy the values.
Another option is to bind the needed properties in the FXML file. You would still be creating multiple Label
s but you would only need to inject the "master" Label
, as before.
<VBox xmlns="http://javafx.com/javafx/" xmlns:fx="http://javafx.com/fxml/1">
<Label fx:id="master"/>
<Label text="${master.text}" tooltip="${master.tooltip}" style="${master.style}"/>
<Label text="${master.text}" tooltip="${master.tooltip}" style="${master.style}"/>
<Label text="${master.text}" tooltip="${master.tooltip}" style="${master.style}"/>
<!-- repeat as needed -->
</VBox>
This uses the expression binding capabilities of JavaFX FXML. This option may clutter the FXML file but it doesn't require you to create a subclass of Label
.
Upvotes: 3
Reputation: 4574
"I don't really want want to duplicate the code like: myLabel1, myLabel2, ..., since all of them are having the same text/tooltip/style." What if you just create multiple references pointing to the same label object ?
Or extend Label to include a copy constructor :
public MyLabel(MyLabel aLabel) {
this.property1 = aLabel.property1;
this.property2 = aLabel.property2;
...
}
Then in FXML using copy:
<MyLabel fx:id="myLabel1"/>
<fx:copy source="myLabel1"/>
Upvotes: 0