Get Controller instance from Node - javafx

There is an possibility to get Controller instance from Node ?? for example AnchorPane on Tab ?
I have some AnchorPanes where I load different Controllers and I would like to verify which controller is already loaded

Nodes do not contain any information about a controller used with the fxml file used to create it by default, since fxml is just one way of creating a scene. However you could attach information like this to the userData/properties in the fxml:
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" fx:id="AnchorPane" prefHeight="400.0" prefWidth="600.0" onMouseClicked="#click" fx:controller="fxml.FXMLController">
<!-- store controller as userData property -->
<userData>
<fx:reference source="controller" />
</userData>
<properties>
<!-- store controller at key "foo" in properties map -->
<foo><fx:reference source="controller" /></foo>
</properties>
</AnchorPane>
If you do this, you can lookup the controller at closest ancestor of a node where you added this kind of information using
public static Object getController(Node node) {
Object controller = null;
do {
controller = node.getProperties().get("foo");
node = node.getParent();
} while (controller == null && node != null);
return controller;
}
to retrieve the info from the properties map or using
public static Object getController(Node node) {
Object controller = null;
do {
controller = node.getUserData();
node = node.getParent();
} while (controller == null && node != null);
return controller;
}
to retrieve the info from the userData property.
You should probably use just one way of adding the info though, not both. Also it would be better to replace foo as key...

It's an old question, but if you have a main window, where you include other fxml files like this:
<AnchorPane prefHeight="900.0" prefWidth="1600.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="MainController">
<!-- <HBox></HBox>, some elements here, your normal usual FXML-->
<fx:include fx:id="someAnchorPane" source="SomeAnchorPane.fxml"/>
</AnchorPane>
and your SomeAnchorPane.fxml has fx:controller property set to SomeOtherController, then you can add a controller field in your MainController like this:
#FXML private SomeOtherController someAnchorPaneController;
And it will inject appropriate controller in this field automatically.
The key thing here is that your field has to be named "fx:id+Controller" for it to work.

Related

JavaFX Change Color of Table Row when Button is clicked

I want my table to change the color of a row depending on a column is checked or not or when a button is clicked.
I searched for solutions but it never worked the way I wanted to. I am happy for every keyword or help you have. Maybe I was just looking for the wrong thing?
My code looks like this:
public class Cellexample_presenter implements Initializable{
#FXML
TableColumn<ExampleData, Boolean> checkcol;
#FXML
TableView<ExampleData> testtable;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
checkcol.setText("Check");
ObservableList<ExampleData> exList = FXCollections.observableArrayList();
exList.add(new ExampleData(true));
exList.add(new ExampleData(false));
checkcol.setCellValueFactory(
new Callback<CellDataFeatures<ExampleData, Boolean>, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(CellDataFeatures<ExampleData, Boolean> param) {
return param.getValue().checkProperty();
}
});
testtable.setItems(exList);
testtable.setEditable(true);
checkcol.setCellFactory(CheckBoxTableCell.forTableColumn(checkcol));
}
#FXML
private void clicked() {
ObservableList<ExampleData> tabledata = testtable.getItems();
for (ExampleData e : tabledata) {
System.out.println(e.isCheck());
}
}
}
I found solutions with another setCellFactory, but I doesn't seem to work when I already have a setCellFactory and I was unable to combine then. Moreover there is still the connection missing to the
change of the Checkbox.
Another solution I came across was something with a PseudoClass but this also required another setCellFactory
The method private void clicked() I used to check if the data in my object was really changed.
Also I want as another Feature to click a Button and change the color of rows with wrong data.
I was hoping there was a way to iterate trough the table, check the data and if something doesn't equals something else. It changes color.
Like this in pseudo-code
for (tablerow row: table)
if (row.ischecked()) {
row.changecolor();
}
Here is the .FXML for putting everything together:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.Pane?>
<AnchorPane prefHeight="254.0" prefWidth="700.0" xmlns="http://javafx.com/javafx/11.0.1"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="pathToFXML/Cellexample_presenter">
<children>
<Pane layoutX="-269.0" prefHeight="315.0" prefWidth="700.0" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<TableView fx:id="testtable" layoutX="142.0" layoutY="27.0" prefHeight="200.0"
prefWidth="340.0">
<columns>
<TableColumn fx:id="checkcol" prefWidth="75.0" text="C1" />
</columns>
</TableView>
<Button fx:id="checkbtn" layoutX="70.0" layoutY="36.0" mnemonicParsing="false"
onAction="#clicked" text="isChecked" />
</children>
</Pane>
</children>
</AnchorPane>
You can use a custom TableRow for this. I also prefer to use CSS and PseudoClasses to manage the color of rows.
What needs to happen here is that the row needs to know if the checked property in the ExampleData instance currently displayed by the row changes, and needs to change color if that happens.
Of course, for any given table row, the ExampleData instance displayed by that table row may also change (e.g. if the underlying data list changes, or if the user scrolls the table, etc.)
So the basic strategy here is:
Create a listener that observes the current item's checked property, and updates the pseudoclass state if it does (which in turn causes the style to change). In the code below I call this listener checkListener.
If the item displayed by the row changes, we need to stop observing the old item's checked property and start observing the new item's checked property instead. This can be achieved via a listener on the item property of the table row. That listener on the itemProperty can then remove the checkListener from the checkedProperty of the old item (if there was an old item), and add it to the checkedProperty of the new item (if there is one). It also needs to update the pseudoclass state according to the current state of the new item's checkedProperty.
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
// Existing code....
testTable.setRowFactory(tv -> new TableRow<ExampleData>() {
private final PseudoClass checkedPC = PseudoClass.getPseudoClass("checked");
private final ChangeListener<Boolean> checkListener = (obs, wasChecked, isNowChecked) ->
pseudoClassStateChanged(checkedPC, isNowChecked);
{
itemProperty().addListener((obs, oldItem, newItem) -> {
if (oldItem != null) {
oldItem.checkedProperty().removeListener(checkListener);
}
if (newItem == null) {
pseudoClassStateChanged(checkedPC, false);
} else {
pseudoClassStateChanged(checkedPC, newItem.isChecked());
newItem.checkedProperty().addListener(checkListener);
}
});
}
});
}
And then in your external CSS file include
.table-row-cell:checked {
-fx-background-color: red ;
}

Javafx. Bug with TitledPane

Now I'm working with javafx and create small view with SplitPane.
In this SplitPane i have two TiteledPanes(named A and B). At first step of program working, only one TitledPane contains data (A). But after little user actions some information are put on B.
If i run my programm 10 times, at 7-8 times everything will be ok!
But in 2 times I have a problem with second titeledPane. It is consists with no data. This TitledPane loaded without any exceptions and all bindings are good, But it consists with no data. It is very strange, because in 80% of time everything is ok.
May be this is javafx bugs?
Any one have the same problem?
<fx:root type="MyView" dividerPositions="0.5" orientation="VERTICAL" style="-fx-background-color: darkgray">
<fx:define>
<TitledPane fx:id="A">
<AnhorPane>
<Label text="ALabel"/>
</AnhorPane>
</TitledPane>
<TitledPane fx:id="B"/>
<AnhorPane>
<Label text="BLabel"/>
</AnhorPane>
</TitledPane>
</fx:define>
</fx:root>
public class MyView extends SplitPane {
public MyView () {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("MyView .fxml"));
loader.setController(this);
loader.setRoot(this);
loader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
}
The problem was in loading fxml file proccess. There are a lot of data should be loaded after the load fxml file(in my model class relating to that fxml) and in future adding to existing fxml. Like binding properties and etc.
When I relocate above 80% of bindings in fxml file using ${} - the problem was solded.

Javafx FXML loader getcontroller returns null

In my program i have a tabPane with each tab having in own FXML file and controller .I have loaded the FXML file for tab 2 (Schedular).I`m trying to call a function in my child controller (Scheduler) from my parent controller(FXML Document) . When i load the controller using FXML Loader it returns a null. How can i solve this .
Here is my main code:
Main FXMLDocument :
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" fx:id="AnchorPane" prefHeight="600" prefWidth="800" xmlns="http://javafx.com/javafx/8.0.65" fx:controller="showprojavafxml.FXMLDocumentController">
<children>
The FXMLLoader only instantiates the controller when it loads the fxml file (this has to be the case, since the controller class is specified in the fxml file...). Since you never call load() on the loader, the controller is never created.
It looks like you are referencing the FXML file twice through two different mechanisms: once in FXMLDocumentController.initialize(), where you create a FXMLLoader whose location is set to the fxml file, and once in the main FXML document itself, via a <fx:include>. The <fx:include> is causing the UI defined in Scheduler.fxml to be displayed; the FXMLLoader you create in the initialize() method is not (because you never call load() and display the result).
To reference a controller for an included fxml, use the "Nested controller" technique.
First, add an fx:id to your fx:include:
<Tab fx:id="tab2" text="Scheduler" >
<fx:include fx:id="scheduler" source="Scheduler.fxml" />
</Tab>
Now you can inject the controller into a field whose name is the fx:id with the text "Controller" appended:
public class FXMLDocumentController {
#FXML
private SchedulerController schedulerController ;
#Override
public void initialize(URL url, ResourceBundle rb) {
schedulerController.refreshList();
}
}

How to access UI components from SceneBuilder in JavaFX

(DUPLICATE & SOLVED - see answer below)
I'm doing my first steps in JavaFX and it seems quite hard to use the "SceneBuilder". I'm used to Android and the QtCreator. It looks to me that there is accessing the UI components much easier.
Something like findViewById(R.id.btnPushMe); <- Android Code
Actually I got an solution but it is quite uncomfortable to use. This looks as this:
FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("../fmxl/main.fxml"));
AnchorPane pane = loader.load();
System.out.println("panechilds:" + pane.getChildren().size());
BorderPane border = (BorderPane) pane.getChildren().get(0);
System.out.println("borderchilds:" + border.getChildren().size());
the xml..
<AnchorPane fx:id="mAnchor" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.progui.MainController">
<children>
<BorderPane layoutX="-1.0" prefHeight="600.0" prefWidth="800.0">
<top>
...
Thanks in advance
Martin
Edit:
This is a duplicate question (but I will not delete it, because I took some time to find the answer - maybe because JavaFX wasn't asked as much as Android questions were..)
AnchorPane anchor = (AnchorPane) scene.lookup("#mAnchor");
found here: How to find an element with an ID in JavaFX?
You should use a controller class and access the UI elements there.
Basically you do:
<AnchorPane fx:id="mAnchor" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.progui.MainController">
<children>
<BorderPane fx:id="border" layoutX="-1.0" prefHeight="600.0" prefWidth="800.0">
<top>
...
And then you can access the fx:id-attributed elements in the controller with
package app.progui ;
// ...
public class MainController {
#FXML
private BorderPane border ;
public void initialize() {
border.setStyle("-fx-background-color: antiquewhite;");
// ...
}
// ...
}
The field names in the controller class must match the fx:id values in the FXML file.
It is possible to access the fx:id-attributed elements in the class that invoked the FXMLLoader, but if you need to do this it is usually a sign that your overall design is wrong. You can do:
FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("../fmxl/main.fxml"));
AnchorPane pane = loader.load();
Map<String, Object> fxmlNamespace = loader.getNamespace();
BorderPane border = (BorderPane) fxmlNamespace.get("border");
assuming the fx:id defined in the FXML snipped above.
When you design for FXML, you typically design three things: the application logic, the GUI controller logic, and the FXML.
References to the UI controls you wish to access are injected by the FXML loader into your controller class during its loading and initialization so that you do not need to use a FindById() method.
A controller class looks similar to this:
class DCServRecEditor extends DialogController {
#FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;
#FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;
#FXML // fx:id="ancMatchSelector"
private AnchorPane ancMatchSelector; // Value injected by FXMLLoader
#FXML // fx:id="ancServEditor"
private AnchorPane ancServEditor; // Value injected by FXMLLoader
#FXML // fx:id="ancServRecEditor"
private AnchorPane ancServRecEditor; // Value injected by FXMLLoader
:
:
The FXML loading facility automatically injects references into instance fields that are annotated with the #FXML tag. To manipulate a UI control, just access its methods using the appropriate reference.
Separating the UI control logic from your application logic is highly desirable, and effects a "separation of concerns". When you get used to designing FXML UI's this way, you'll enjoy it.

How to add tags to an fxml file through controller in javafx?

<AnchorPane>
<TreeView fx:id="locationTreeView" focusTraversable="true" prefHeight="449.0" prefWidth="725.0" style="#tree
{
-fx-border-style:solid;
-fx-border-width:1px;
-fx-border-color:#ffffff;
}"/>
In the above fxml code I want to add one more <TreeView> but through the controller. How can I do this?
You will have to:
Give a fx:id to the AnchorPane:
<AnchorPane fx:id="theAnchorPane">
Add the corresponding field in the controller:
#FXML private AnchorPane theAnchorPane;
From the code that performs the addition you have to:
Create the new TreeView however you like:
TreeView newTreeView = ...;
Add it to the childen of the AnchorPane, possibly with some constraints:
theAnchorPane.getChildren().add(newTreeView);
AnchorPane.setTopAnchor(newTreeView, ...); // etc

Resources