Binding and FxmlComponent controls to allow bindings and loading of FXML Components.

This commit is contained in:
Konrad Neitzel
2025-04-12 20:38:20 +02:00
parent be6a8c853e
commit 2b60038774
4 changed files with 254 additions and 0 deletions
@@ -0,0 +1,67 @@
package de.neitzel.fx.component.controls;
import de.neitzel.fx.component.model.BindingData;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.layout.Region;
/**
* The BindingControl class represents a UI control that manages a list
* of {@link BindingData} objects. It extends the {@link Region} class and
* provides functionality to bind and monitor connections between source
* and target properties.
*
* The primary purpose of this control is to maintain an observable list
* of bindings, allowing developers to track or adjust the linked properties
* dynamically.
*
* The internal list of bindings is implemented as an {@link ObservableList},
* allowing property change notifications to be easily monitored for UI
* updates or other reactive behaviors.
*
* This class serves as an organizational component and does not provide
* any user interaction by default.
*/
public class Binding extends Region {
/**
* Represents an observable list of {@link BindingData} objects contained within the
* {@link Binding} instance. This list is utilized to manage and monitor
* the bindings between source and target properties dynamically.
*
* The list is implemented as an {@link ObservableList}, allowing changes in the
* collection to be observed and reacted to, such as triggering UI updates or
* responding to binding modifications.
*
* This field is initialized as an empty list using {@link FXCollections#observableArrayList()}.
* It is declared as final to ensure its reference cannot be changed, while the
* contents of the list remain mutable.
*/
private final ObservableList<BindingData> bindings = FXCollections.observableArrayList();
/**
* Retrieves the observable list of {@code Binding} objects associated with this control.
* The returned list allows monitoring and management of the bindings maintained
* by the {@code BindingControl}.
*
* @return an {@code ObservableList<Binding>} containing the bindings managed by this control
*/
public ObservableList<BindingData> getBindings() {
return bindings;
}
/**
* Constructs a new instance of the BindingControl class.
*
* This default constructor initializes the BindingControl without
* any pre-configured bindings. The instance will contain an empty
* {@link ObservableList} of {@link BindingData} objects, which can later
* be populated as needed.
*
* The constructor does not perform additional setup or initialization,
* allowing the class to be extended or customized as necessary.
*/
public Binding() {
// Absichtlich leer – wird später "ausgewertet"
}
}
@@ -0,0 +1,90 @@
package de.neitzel.fx.component.controls;
import de.neitzel.fx.component.ComponentLoader;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.layout.StackPane;
import java.io.IOException;
import java.util.Arrays;
public class FxmlComponent extends StackPane {
private final StringProperty fxml = new SimpleStringProperty();
private final StringProperty direction = new SimpleStringProperty("unidirectional");
private final ObjectProperty<Object> data = new SimpleObjectProperty<>();
public StringProperty fxmlProperty() { return fxml; }
public String getFxml() { return fxml.get(); }
public void setFxml(String fxml) { this.fxml.set(fxml); }
public StringProperty directionProperty() { return direction; }
public String getDirection() { return direction.get(); }
public void setDirection(String direction) { this.direction.set(direction); }
public ObjectProperty<Object> dataProperty() { return data; }
public Object getData() { return data.get(); }
public void setData(Object data) { this.data.set(data); }
public FxmlComponent() {
fxml.addListener((obs, oldVal, newVal) -> load());
data.addListener((obs, oldVal, newVal) -> injectData());
}
private void load() {
if (getFxml() == null || getFxml().isBlank()) return;
try {
ComponentLoader loader = new ComponentLoader();
// Option: ControllerFactory verwenden, wenn nötig
Parent content = loader.load(getClass().getResource(getFxml()));
getChildren().setAll(content);
Object controller = loader.getController();
if (controller != null && getData() != null) {
injectDataToController(controller, getData());
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void injectData() {
if (!getChildren().isEmpty() && getData() != null) {
Object controller = getControllerFromChild(getChildren().get(0));
if (controller != null) {
injectDataToController(controller, getData());
}
}
}
private void injectDataToController(Object controller, Object dataObject) {
// Daten-Objekt per Reflection zuweisen
// Beispiel: Controller hat `setData(User data)`
Arrays.stream(controller.getClass().getMethods())
.filter(m -> m.getName().equals("setData") && m.getParameterCount() == 1)
.findFirst()
.ifPresent(method -> {
try {
method.invoke(controller, dataObject);
} catch (Exception e) {
e.printStackTrace();
}
});
// Optional: automatisch Binding ausführen (s. u.)
}
private Object getControllerFromChild(Node node) {
if (node.getProperties().containsKey("fx:controller")) {
return node.getProperties().get("fx:controller");
}
return null;
}
}
@@ -0,0 +1,82 @@
package de.neitzel.fx.component.model;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
* The Binding class represents a connection between a source and a target,
* with an associated direction. It is typically used to define a binding
* relationship that determines how data flows between these two entities.
*
* The class provides three properties:
* - direction: Represents the type of the binding. Defaults to "unidirectional".
* - source: Represents the source of the binding.
* - target: Represents the target of the binding.
*
* It uses JavaFX property types, allowing these properties to be observed
* for changes.
*/
public class BindingData {
/**
* Represents the direction of the binding in the {@code Binding} class.
* It determines whether the binding is unidirectional or bidirectional.
* The default value is "unidirectional".
*
* This property is observed for changes, enabling dynamic updates
* within the JavaFX property system.
*/
private StringProperty direction = new SimpleStringProperty("unidirectional");
/**
* Represents the source of the binding. This property holds a string value
* that specifies the originating object or identifier in the binding connection.
* It can be observed for changes, allowing updates to the binding relationship
* when the source value is modified.
*/
private StringProperty source = new SimpleStringProperty();
/**
* Represents the target of the binding in the Binding class.
* This property holds the target value, which can be observed for changes.
*/
private StringProperty target = new SimpleStringProperty();
/**
* Gets the current direction of the binding.
* The direction determines how data flows between
* the source and the target, and typically defaults to "unidirectional".
*
* @return the current direction of the binding
*/
public String getDirection() { return direction.get(); }
/**
* Sets the direction of the binding.
*
* @param dir the new direction to set for the binding
*/
public void setDirection(String dir) { direction.set(dir); }
/**
* Gets the current source value of the binding.
*
* @return the source value as a String.
*/
public String getSource() { return source.get(); }
/**
* Sets the value of the source property for this binding.
*
* @param source the new source value to be set
*/
public void setSource(String source) { this.source.set(source); }
/**
* Retrieves the current value of the target property.
*
* @return the value of the target property as a String.
*/
public String getTarget() { return target.get(); }
/**
* Sets the target property of the binding.
*
* @param target the new value to set for the target property
*/
public void setTarget(String target) { this.target.set(target); }
}