#javafx
Вопрос:
Я хочу создать элемент управления, который будет прикреплять сообщения проверки к любому другому элементу управления JavaFX, например:
<ValidationDecorator fx:id="usernameE" GridPane.columnIndex="2">
<decorated>
<TextField fx:id="usernameF" />
</decorated>
</ValidationDecorator>
Пока это работает довольно хорошо:
Но я столкнулся с проблемой с построителем сцен, он не показывает дочерние элементы для моего пользовательского элемента управления:
Код для моего пользовательского компонента (обратите внимание, что я добавил @DefaultProperty("decorated")
:
package org.foo.javafx.validation.extra;
import javafx.beans.DefaultProperty;
import javafx.beans.InvalidationListener;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;
import org.foo.javafx.validation.lib.Objection;
import org.foo.javafx.validation.lib.ObjectionSeverity;
import org.foo.javafx.validation.lib.Input;
import java.util.List;
@DefaultProperty(value = "decorated")
public class ValidationDecorator extends VBox {
private final VBox decoratedComponentsContainer;
private final VBox validationMessagesContainer;
private final SimpleListProperty<Objection> objectionsProperty =
new SimpleListProperty<>(FXCollections.observableArrayList());
public ValidationDecorator() {
decoratedComponentsContainer = new VBox();
decoratedComponentsContainer.setPrefHeight(VBox.USE_COMPUTED_SIZE);
decoratedComponentsContainer.setPrefWidth(VBox.USE_COMPUTED_SIZE);
validationMessagesContainer = new VBox();
validationMessagesContainer.setPrefHeight(VBox.USE_COMPUTED_SIZE);
validationMessagesContainer.setPrefWidth(VBox.USE_COMPUTED_SIZE);
validationMessagesContainer.setSpacing(2);
this.setPrefHeight(USE_COMPUTED_SIZE);
this.setPrefWidth(USE_COMPUTED_SIZE);
this.setSpacing(2);
super.getChildren().addAll(decoratedComponentsContainer, validationMessagesContainer);
objectionsProperty.addListener((InvalidationListener) observable -> {
List<Objection> objections = this.objectionsProperty.getValue();
var messagesFx = validationMessagesContainer.getChildren();
if (messagesFx.size() > objections.size()) {
messagesFx.remove(objections.size(), messagesFx.size());
}
while (messagesFx.size() < objections.size()) {
messagesFx.add(new ValidationMessageFx());
}
for (int i = 0; i < objections.size(); i ) {
((ValidationMessageFx)messagesFx.get(i)).setObjection(objections.get(i));
}
});
}
public ObservableList<Node> getDecorated() {
return decoratedComponentsContainer.getChildren();
}
public SimpleListProperty<Objection> objectionsProperty() {
return objectionsProperty;
}
public <UIV,MV> void displayErrorsFor(Input<UIV, MV> input) {
this.objectionsProperty().bind(input.objectionsProperty());
}
public class ValidationMessageFx extends HBox {
private final Circle circle;
private final Text text;
public ValidationMessageFx() {
super.setSpacing(5);
super.setAlignment(Pos.BASELINE_LEFT);
this.circle = new Circle(5.0, Color.RED);
this.text = new Text();
this.getChildren().addAll(circle, text);
}
public void setObjection(Objection e) {
this.text.setText("!!!" e.message);
this.circle.setFill(e.severity == ObjectionSeverity.ERROR ? Color.RED : Color.ORANGE);
}
}
}
Since my custom control extends HBox
overriding getChildren()
property does not work (it breaks internal HBox logic). I also checked ScrollPane
source code, as it has single content
property but ScrollPane
do not inherit from Pane
class that defines getChildren()
accessor.
Any idea how I can do this without writing a custom control from scratch (I want to reuse VBox layout logic).