I placed a ChoiceBox inside an fxml with JavaFX Scene Builder.
The FXML has a controller assigned to it.
My question is: Which event do I need to register if I want to know about changed values?
onInputMethodTextChanged="#languageSelectionModified"
this does not work with the following code
public void languageSelectionModified(Event event) {
ChoiceBox<String> box = (ChoiceBox<String>) event.getSource();
System.out.println(box.getValue());
}
and this only works for the initial click (i.e. opening the list, not when selecting an item):
onMouseClicked="#languageSelectionModified"
Although the Mouse-Events would never be a good choise because of situations where the touch or keyboard is the input-method, it still proves that the System.out can be reached.
I have absolutly no idea where those things are documentated (in the default Java-API they are not)
Add a listener to your #FXML injected choicebox in your controller:
choicebox.getSelectionModel().selectedItemProperty().addListener(choiceboxSelectionChangeListener);
You can also bind to the selected item:
label.textProperty().bind(choicebox.getSelectionModel().selectedItemProperty());
Here is an example of hooking up a listener in a controller for a ComboBox defined in FXML. Logic for a ChoiceBox is pretty much identical.
You can also use FXML onAction attribute:
<ChoiceBox onAction="#languageSelectionModified" />
Related
In this JavaFX tutorial, address book application creation suggested. Registered person could be deleted, however before deleting, person must be selected in table view.
There will be an ArrayIndexOutOfBoundsException because it could not
remove a person item at index -1. The index -1 was returned by
getSelectedIndex() - which means that there was no selection.
To ignore such an error is not very nice, of course. We should let the
user know that he/she must select a person before deleting. (Even
better would be if we disabled the button so that the user doesn’t
even have the chance to do something wrong.)
Author is totally correct about "Even better would be if we disabled the button ...", however he selected the first way. I suppose, manipulation of the button state is the essential skill for JavaFX application development, so I tried to implement the better solution.
Of course, we can do it by way like this:
peopleTable.getSelectionModel().selectedItemProperty().addListener(
(observable, oldValue, newValue) -> {
showPersonDetails(newValue);
boolean somebodySelected = peopleTable.getSelectionModel().getSelectedIndex() >= 0;
button.setDisable(!somebodySelected);
}
);
How ever I am interested for the other way: using dynamic attribute value for button:
<Button
mnemonicParsing="false"
onAction="#handleDeletePerson"
text="Delete"
disable="disableDeleteButtonFlag"
/>
If dynamic attribute value is possible, there is no need explicitly invoke button.setDisable() anymore. However, below code does not work.
#FXML
private boolean disableDeleteButtonFlag = true;
// ...
#FXML
private void initialize() {
// ...
peopleTable.getSelectionModel().selectedItemProperty().addListener(
(observable, oldValue, newValue) -> {
showPersonDetails(newValue);
disableDeleteButtonFlag = peopleTable.getSelectionModel().getSelectedIndex() < 0;
}
);
}
First off, I'm not sure if referencing a boolean field that way would even let the FXML file be loaded (you don't specific exactly how it "doesn't work"). But ignoring that, fields are not observable in Java which means updating the field will not automagically cause the disable property of the Button to be updated. This is why JavaFX has the [ReadOnly]Property interfaces and introduces the "JavaFX Bean" (an extension to Java Bean that adds a "property getter"); it allows observers to see changes in an object's properties. Note you can bind a writable property to an ObservableValue so it always has the same value.
Now, I would have expected an expression binding would be what you're looking for, but the following:
<ListView fx:id="list"/>
<Button disable="${list.selectionModel.selectedIndex == -1}"/>
Doesn't appear to work—I get an exception (using JavaFX 12.0.1):
Caused by: java.lang.ClassCastException: class java.lang.Long cannot be cast to class java.lang.Integer (java.lang.Long and java.lang.Integer are in module java.base of loader 'bootstrap')
at java.base/java.lang.Integer.compareTo(Integer.java:64)
at javafx.fxml/com.sun.javafx.fxml.expression.Expression.lambda$equalTo$5(Expression.java:1105)
at javafx.fxml/com.sun.javafx.fxml.expression.BinaryExpression.evaluate(BinaryExpression.java:55)
at javafx.fxml/com.sun.javafx.fxml.expression.ExpressionValue.getValue(ExpressionValue.java:192)
at javafx.base/com.sun.javafx.binding.ExpressionHelper.addListener(ExpressionHelper.java:53)
at javafx.base/javafx.beans.value.ObservableValueBase.addListener(ObservableValueBase.java:55)
at javafx.fxml/com.sun.javafx.fxml.expression.ExpressionValue.addListener(ExpressionValue.java:201)
at javafx.base/javafx.beans.binding.BooleanBinding.bind(BooleanBinding.java:106)
at javafx.base/javafx.beans.property.BooleanPropertyBase$ValueWrapper.<init>(BooleanPropertyBase.java:254)
at javafx.base/javafx.beans.property.BooleanPropertyBase.bind(BooleanPropertyBase.java:168)
at javafx.fxml/javafx.fxml.FXMLLoader$Element.processPropertyAttribute(FXMLLoader.java:326)
at javafx.fxml/javafx.fxml.FXMLLoader$Element.processInstancePropertyAttributes(FXMLLoader.java:242)
at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:775)
at javafx.fxml/javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2838)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2557)
... 17 more
Using the selectedItem property instead:
<Button disable="${list.selectionModel.selectedItem == null}"/>
Gives a different exception:
Caused by: java.lang.NullPointerException
at javafx.fxml/com.sun.javafx.fxml.expression.Expression.lambda$equalTo$5(Expression.java:1105)
at javafx.fxml/com.sun.javafx.fxml.expression.BinaryExpression.evaluate(BinaryExpression.java:55)
at javafx.fxml/com.sun.javafx.fxml.expression.ExpressionValue.getValue(ExpressionValue.java:192)
at javafx.base/com.sun.javafx.binding.ExpressionHelper.addListener(ExpressionHelper.java:53)
at javafx.base/javafx.beans.value.ObservableValueBase.addListener(ObservableValueBase.java:55)
at javafx.fxml/com.sun.javafx.fxml.expression.ExpressionValue.addListener(ExpressionValue.java:201)
at javafx.base/javafx.beans.binding.BooleanBinding.bind(BooleanBinding.java:106)
at javafx.base/javafx.beans.property.BooleanPropertyBase$ValueWrapper.<init>(BooleanPropertyBase.java:254)
at javafx.base/javafx.beans.property.BooleanPropertyBase.bind(BooleanPropertyBase.java:168)
at javafx.fxml/javafx.fxml.FXMLLoader$Element.processPropertyAttribute(FXMLLoader.java:326)
at javafx.fxml/javafx.fxml.FXMLLoader$Element.processInstancePropertyAttributes(FXMLLoader.java:242)
at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:775)
at javafx.fxml/javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2838)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2557)
... 17 more
As both of these exceptions are emanating from JavaFX code (i.e. not our code) this behavior is probably a bug. That, or I'm using expression bindings incorrectly—in which case I hope someone corrects me. Since attempting to bind the property in FXML is not working, the workaround is to bind the property in code.
<VBox xmlns="http://javafx.com/" xmlns:fx="http://javafx.com/fxml" fx:controller="Controller">
<ListView fx:id="list"/>
<Button fx:id="button"/>
</VBox>
public class Controller {
#FXML private ListView<YourType> list;
#FXML private Button button;
#FXML
private void initialize() {
button.disableProperty()
.bind(list.getSelectionModel().selectedIndexProperty().isEqualTo(-1));
}
}
The isEqualTo method will return a BooleanBinding whose value is true whenever the selectedIndex property holds -1. The disable property is then bound to this BooleanBinding so it always has the same value. You can read Using JavaFX Properties and Binding for more information.
I'm creating a UI for a project in javafx. I'm using CSS for the styles, FXML for the structure, and also .java controllers. I'm trying to make my node(s) (i.e. button, borderpane) layout bind to the scene size, so that the window can be resized and the layout stay the same. Normally if the node was defined in a jar file I would use:
button.layoutYProperty().bind((scene.heightProperty().divide(2)));
or something similar, but my nodes are defined within the FXML file.
How can I access the nodes from outside of the FXML file so that I can define the layout or how can I define the layout within the FXML file.
(I am not using and do not intend to use scene builder).
First, you should never need to do things like this. If you use layout panes you should always be able to choose a combination of layout panes and settings that automatically position the nodes as you want them. The solutions outlined below should never really be needed.
In the controller, you typically don't have direct access to the scene, so you need to listen for when the sceneProperty() is initialized on the node and act accordingly:
public class Controller {
#FXML
private Button button ;
public void initialize() {
button.sceneProperty().addListener((obs, oldScene, newScene) -> {
button.layoutYProperty().unbind();
if (newScene != null) {
button.layoutYProperty().bind(newScene.heightProperty().divide(2));
}
});
// ...
}
}
But again, the correct approach here is to choose appropriate layout panes to achieve what you want.
Is it possible to activate multiple selection for a JavaFX TreeView with FXML?
If you want to set the selection mode programmatically, you would call
treeView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
But how can that be described in FXML? The best I came up with for now was:
<?import javafx.scene.control.TreeView.TreeViewBitSetSelectionModel?>
...
<TreeView>
<selectionModel>
<TreeViewBitSetSelectionModel selectionMode="MULTIPLE"/>
</selectionModel>
</TreeView>
This leads to this exception:
javafx.fxml.LoadException: TreeViewBitSetSelectionModel is not a valid type.
I assume that this is either because is not a public class or because it only has a non default constructor.
I read in the FXML documentation, that there are builders for various types, but I did not see a Builder or a Factory for selection model classes. Did I miss this?
Any hints on how to achieve this are very welcome!
I want to call handler method when any key on keyboard is pressed, and then get pressed key character. So I wrote this line for button in fxml file:
<Button fx:id="button" layoutX="126.0" layoutY="90.0" onKeyPressed="#handleButton" text="Test!" />
When any key is pressed, this should call handleButton method in controller class and pass KeyEvent parameter to it. So I wrote this method inside it:
#FXML
private void handleButton(KeyEvent event) {
System.out.println(event);
}
In the fxml file NetBeans shows error "Handler method is not accessible. Make public, or annotate with #FXML.", which I've already done.
As soon as I change from private void handleButton(KeyEvent event) to private void handleButton(Event event) NetBeans stops showing error and app works.
On this page I found answer, which uses onKeyPressed exactly the same as I do, so I'm really confused why it isn't working in my case.
Thanks for your help,
Vid
You probably imported wrong KeyEvent. It must be javafx.scene.input.KeyEvent.
Right-click with the mouse on your file.FXML and choose from options make controller and automatically the problem will be resolved and this error will be gone.
I'm starting working with JavaFX and wish to use the new tree view (as you can use multiple icons to represent your data - which is what I wish to take advantage of).
I have created a basic form/scene that has a tree view and one button on it. When this button is pressed I wish to populate the treeview.
Now, all the examples ive looked at are where the form/scene is generated in code and the treeview is bound to that control....how do I have a pre designed form with Scene builder and populate it from external code?
You could use the following code in a controller class. Inside the FXML file you will need to set the FXID to selectionTreeView. Tested in JDK 8u5 and it worked.
#FXML
TreeView selectionTreeView;
#FXML
private void handleButtonAction(ActionEvent event) {
createTree();
}
public void createTree(String... rootItems) {
//create root
TreeItem<String> root = new TreeItem<>("Root");
//root.setExpanded(true);
//create child
TreeItem<String> itemChild = new TreeItem<>("Child");
itemChild.setExpanded(false);
//root is the parent of itemChild
root.getChildren().add(itemChild);
selectionTreeView.setRoot(root);
}
Set the class name (including package) on the root node of your control in scene builder. If you click on, then go to the code tab on the right it is the top field.
Now set an ID on the TreeView in your control.
Now in the controller object add a TreeView field, the variable name should be the same as what you set the TreeView ID as in scene builder. Annotate with field with #FXML.
Now when the FXML is loaded, the controller is created and the TreeView field is set.