In the application above I have a tabpane (the one with "age", "gender" and "zipcode") where each tab contains a VBox. The VBox is split in two:
The upper part of the VBox lets you view a list of lists, the lower part of the VBox contains a menu that allows you to change the list of lists.
The button in the lower part of the VBox updates the aforementioned list of lists. I want the upper node in the VBox to be updated anew when the underlying lists are changed.
The relevant code snippet might be
Node createHierarchySplitMenu(HierarchiesFromFile hierarchies, String hierarchyName){
VBox vBox = new VBox();
vBox.getChildren().add(createHierarchyScrollPane(hierarchies, hierarchyName));
vBox.getChildren().add(createHierarchyMenu());
return vBox;
}
When the button in the node in the lower part of the VBox (created by createHierarchyMenu()) I want createHierarchyScrollPane() to be called again to show the new list of lists. How do I do that?
Is there a regular pattern/way of updating one node from another (when they are at the same level.)?
What have you tried? Nothing worth mentioning- I am stuck.
If you need more info, please ask. Didn't want to bog you down with code.
From what I understand the solution can be next:
Create class to handle createHierarchyScrollPane():
private class HierarchyScrollPane extends ScrollPane {
public void update(HierarchiesFromFile hierarchies, String hierarchyName) {
// code from createHierarchyScrollPane() which works with "this" instead of new Scroll Pane
}
}
Pass instance to createHierarchyMenu():
Node createHierarchySplitMenu(HierarchiesFromFile hierarchies, String hierarchyName){
VBox vBox = new VBox();
ScrollPaneUpdate hsp = new HierarchyScrollPane();
hsp.update(hierarchies, hierarchyName);
vBox.getChildren().add(hsp);
vBox.getChildren().add(createHierarchyMenu(hsp));
return vBox;
}
Somewhere in createHierarchyMenu():
public void createHierarchyMenu(final HierarchyScrollPane hsp) {
// ...
Button btnSetMin = new Button("Set Min");
btnSetMinsetOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
// your code for data update
hsp.update(hierarchies, hierarchyName);
}
});
}
Related
I need help creating a delete button on a stack pane in JavaFX.
It should remove the chosen stack pane from the group.
RinStackPane is extended javafx StackPane, gp is java fx Group.
Thank you in advance!
Here is my code:
private void setOnCreateMathOperation(Button createMathOperationBtn) {
createMathOperationBtn.setOnAction(event -> {
var mathOperation = createMathOperation();
RinStackPane stack1 = new RinStackPane();
stack1.setNodeType(NodesTypes.MathOperation);
stack1.setUnderlyingNode(mathOperation);
stack1.setName(mathOperation.getName());
stack1.getChildren().add(mathOperation);
TextField newtf1 = new TextField();
GridPane grid = new GridPane();
grid.addRow(1, newtf1);
stack1.getChildren().add(grid);
makeDraggable(stack1);
addToRectCollection(stack1);
gp.getChildren().add(stack1);
});
}
I am not sure if you want to keep the delete button inside or outside the StackPane.
Anyway you can easily bind an ObservableList to the Group's children. When an elemente will be deleted from the list, automatically this event will be reflected on Group's children:
//Create observable list of Node (you can constrain to RinStackPane if you prefer):
ObservableList<Node> list = FXCollections.observableArrayList();
//bind lists to gp's children:
Bindings.bindContentBidirectional(list, gp.getChildren());
Your code:
private void setOnCreateMathOperation(Button createMathOperationBtn) {
createMathOperationBtn.setOnAction(event -> {
RinStackPane stack1 = new RinStackPane();
//...
//setup the stack
//...
//Add the delete button to the stack:
Button deleteButton = new Button("Delete me!");
deleteButton.setOnMouseClicked(event -> {
list.remove(stack1); //<--- remove action
});
stack1.getChildren().add(deleteButton);
//add to the list instead of gp's children
list.add(stack1);
});
}
Note how the stack is not added nor removed to/from the gp's children, instead the list is used.
If you want the deleteButton outside the stack the code is the same, just be sure that both list and stack1 are somehow passed to the button's click handler.
Hope this helps.
I'm currently made an Form with JavaFX.
Always i press a Button, i call the "addAnswer()"-Method.
In that I create a RadioButton, a Label and a delete-Button, which i bundle in a HBox. All that HBoxes i pack in a vBox.
The Problem now is the delete-Button. I want to delte just THAT HBox in which the clicked Button is.
Here is my code:
public void addAnswer() {
this.rB = new RadioButton();
checkAnswer.getToggles().add(rB);
hBox = new HBox();
tF = new TextField();
delAnswer = new Button("Löschen");
delAnswer.setId(Integer.toString(counter));
hBox.getChildren().addAll(rB, tF, delAnswer);
hBox.setId(Integer.toString(counter));
delAnswer.setOnAction(e -> delAnswer(Integer.parseInt(hBox.getId())));
System.out.println(delAnswer.getId());
vBox.getChildren().addAll(hBox);
counter++;
}
public void delAnswer(int e){
vBox.getChildren().remove(delAnswer.getId());
}
i tried this one above but i realized, that all the delAnswers-Buttons have the same ID: the number of how often i pressed the add-Button.
Is there any solution where i can just select that one i pressed with that dynamic way? Cause i don't kow how often somebody will press or delete something.
Thanks
hbox is a field and this is why always the HBox last added is used. (hBox is evaluated, when lambda body is executed, not at the time of the lambda creation). This would be different, if you used a (effectively) final local variable:
final HBox hBoxLocal = hBox;
delAnswer.setOnAction(e -> delAnswer(Integer.parseInt(hBoxLocal.getId())));
However I'd like to present a different solution which would allow you to use the same EventHandler<ActionEvent> for all delete Buttons:
You can get the Node that triggered the event using getSource. From this Node you can get the parent, which is the HBox. You can remove this from the VBox using the remove(Object) method
delAnswer.setOnAction(e -> {
// get button
Node source = (Node) e.getSource();
// remove parent of button from VBox
vBox.getChildren().remove(source.getParent());
});
I think your problem is that you give the same event to all your button,Begin by creating a list that stores your buttons and then increments the value of the ID after affecting it to an item :
List<Button> buttons = new ArrayList<>();
/*
Create Button and call IDEvt method to create new event
for each button
*/
private void IDEvt(Button btn){
btn.setId(String.valueOf(IDRank));
btn.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println(btn.getId());
}
});
IDRank++;
}
In order for the end-user to constrain a search to some columns of the main TableView, I needed a treeview with checkboxes.
I decided to embed this TreeView in a popup, showing on click on a custom button.
I have created the following class, inspired from the question:
Java FX8 TreeView in a table cell
public class CustomTreeMenuButton extends MenuButton {
private PopupControl popup = new PopupControl();
private TreeView<? extends Object> tree;
private CustomTreeMenuButton me = this;
public void setTree(TreeView<? extends Object> tree) {
this.tree = tree;
}
public CustomTreeMenuButton() {
super();
this.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (!popup.isShowing()) {
Bounds b = me.localToScreen(me.getBoundsInLocal());
double x = b.getMinX();
double y = b.getMaxY();
popup.setAutoHide(true);
// popup.setAutoFix(true);
popup.setAnchorX(x);
popup.setAnchorY(y);
popup.setSkin(new Skin<Skinnable>() {
#Override
public void dispose() {
}
#Override
public Node getNode() {
return tree;
}
#Override
public Skinnable getSkinnable() {
return null;
}
});
popup.show(me.getScene().getWindow());
}
}
});
}
}
The tree I am working with contains CheckBoxTreeItem objects, and while the popup is working, there is some weird blur on all checkboxes, whenever the focus is not on a checkbox. (See GIF below)
First, I was thinking it was maybe an antialiasing problem, but popup.getScene().getAntiAliasing().toString() returns DISABLED
Then, I saw that non integer anchor points could cause problems. However popup.setAutoFix(true) did nothing, nor did the following:
popup.setAnchorX(new Double(x).intValue());
popup.setAnchorY(new Double(y).intValue());
It might be worth noting that I am working with FXML.
How can I get sharp checkboxes regardless of their focus ?
I would suggest a built-in control, CustomMenuItem, rather than reinventing the wheel:
A MenuItem that allows for arbitrary nodes to be embedded within it,
by assigning a Node to the content property.
An example
// Create the tree
CheckBoxTreeItem<String> rootItem = new CheckBoxTreeItem<String>("All stuff");
rootItem.setExpanded(true);
final TreeView<String> tree = new TreeView<String>(rootItem);
tree.setEditable(true);
tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
for (int i = 0; i < 8; i++) {
final CheckBoxTreeItem<String> checkBoxTreeItem =
new CheckBoxTreeItem<String>("Stuff" + (i+1));
rootItem.getChildren().add(checkBoxTreeItem);
}
tree.setRoot(rootItem);
tree.setShowRoot(true);
// Create a custom menu item
CustomMenuItem customMenuItem = new CustomMenuItem(tree);
customMenuItem.setHideOnClick(false);
// Create the menu button
MenuButton mb = new MenuButton("Stuffs");
mb.getItems().add(customMenuItem);
And the output
Note: It is important to set the hideOnClickProperty to true, to avoid closing when the user clicks in the tree, which can be even done in the contructor, so you can shorten the initialization to:
CustomMenuItem customMenuItem = new CustomMenuItem(tree, false);
If you want to remove the hover glow, you can add the following CSS class:
.menu-item {
-fx-padding: 0;
}
Given
I have a model class Model that contains the following fields:
StringProperty stringProperty; ListProperty<String> listOfStringsProperty
I have a view class View extends VBox that has the following:
TextField stringTextField; TextFieldList stringsTextFieldList;
TextFieldList extends VBox is a custom class that I created that handles multiple TextFields with nice + and - buttons to add and remove input text fields.
TextFieldList class contains the field ObservableList<Node> inputTextFields and I can get the data from these InputTextFields by a method call List<String> getData()
Question
I was able to do the following:
stringTextField.textProperty().bindBidirectional(model.getStringProperty());
in order to bind the result of the stringTextField in View to the stringProperty in Model
And I need to do something like
stringsTextFieldList.listProperty().bindBidirectional(model.getListOfStringsProperty());
How can I do that?
If this design would not work, then how do you suggest I fix it? Is there a built-in class that does the same as TextFieldList but instead extends Control?
If you decide to make your own control you should create the "binding manually", that means that in the input ObservableList you add a ListChangeListener then you process the Change like in the example: check whether a new item is added, removed or updated and maintain your TextFields accordingly. It is possible, but my answer is mainly about proposing an existing control to re-use rather than create your own one.
So if you don't want to re-invent the wheel:
I don't know your exact use-case, but maybe it is reasonable to reuse a control that actually support a data model, like a ListView.
In the example I have modified the Model class to have an ObservableList<StringProperty> rather than a ListProperty<String> (note: it is also possible to simply have String objects in the list, I just modified it to make the binding really clear). I have added a ListView and used setCellFactory to draw TextFields as elements in the list which are bidirectionally bounded to the corresponding StringProperty in the list. I have also added several buttons to add and remove elements and a button to print the current content of the model.
Example:
Model.java
public class Model {
public ObservableList<StringProperty> listOfStringsProperty;
public Model(){
listOfStringsProperty = FXCollections.observableArrayList();
}
}
Main.java
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
Model m = new Model();
m.listOfStringsProperty.addAll(new SimpleStringProperty("First"),
new SimpleStringProperty("Second"),
new SimpleStringProperty("Third"));
ListView<StringProperty> lv = new ListView<StringProperty>();
lv.setCellFactory(new Callback<ListView<StringProperty>, ListCell<StringProperty>>() {
#Override
public ListCell<StringProperty> call(ListView<StringProperty> param) {
return new ListCell<StringProperty>(){
#Override
protected void updateItem(StringProperty item, boolean empty) {
super.updateItem(item, empty);
if(item == null){
setText(null);
setGraphic(null);
return;
}
TextField tf = new TextField();
tf.textProperty().bindBidirectional(item);
setGraphic(tf);
}
};
}
});
lv.setItems(m.listOfStringsProperty);
root.setCenter(lv);
// Control buttons
HBox hbox = new HBox();
Button buttonAdd = new Button("Add");
buttonAdd.setOnAction(e -> m.listOfStringsProperty.add(new SimpleStringProperty("")));
Button buttonRemove = new Button("Remove last");
buttonRemove.setOnAction(e -> m.listOfStringsProperty.remove(m.listOfStringsProperty.size()-1));
Button buttonPrintModel = new Button("Print model");
buttonPrintModel.setOnAction(e -> System.out.println(m.listOfStringsProperty.toString()));
hbox.getChildren().addAll(buttonAdd, buttonRemove, buttonPrintModel);
root.setBottom(hbox);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
This will produce the following window:
You can use
Bindings.bindContent(List list1, ObservableList list2).
This is a special binding that keeps the list in sync with the observable list. Keep in mind that this is not bidirectional though.
If bidirectorionality is what you want, you should use invalidation listeners or change listeners to detect changes and synchronize the lists manually. You might need some crude locking mechanism to prevent a stack overflow.
I am working on an application in java FX. I need a data structure similar to JList in java swings.I have to use it in my project for displaying data on a scroll pane. I have tried using observable arraylist, with listview and Vbox. Here is my code snippet of the controller class.
public class Controller_class
implements Initializable {
#FXML // fx:id="myButton"
private Button dfctsave;
#FXML
final TextField dfctname = new TextField();
#FXML
ScrollPane dfctscroll = new ScrollPane ();
static ArrayList<String> jlstDefects=new ArrayList<String>();
#Override // This method is called by the FXMLLoader when initialization is complete
public void initialize(URL fxmlFileLocation, ResourceBundle resources)
{
dfctsave.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
ObservableList ad;
VBox v;
String d=dfctname.getText();
jlstDefects.add(d);
System.out.println(jlstDefects);
ad = FXCollections.observableArrayList(jlstDefects);
System.out.println(ad);
ListView lv = new ListView();
lv.setItems(ad);
v=new VBox();
v.getChildren().addAll(lv);
dfctscroll.setContent(v);
}
});
}}
It worked well and I got entries on to the scrollpane, but I need the index of the selected data items for swapping and further processing on scrollpane. I heard about SwingList which would work like the same. Can anyone explain the best alternates for Jlist in FX or explain how to use SwingList in FX.
I tried working with List view. Its working fine.For selecting a particular value on the list view and getting its index, I used the following code snippet.
public ListView<String> jlstDefects ;
public TextField fldDefectName;
private void jltDefectsListItemSelected()
{
int ndx = jlstDefects.getSelectionModel().getSelectedIndex();
if (ndxJlstDefectSelector == ndx)
return;
ndxJlstDefectSelector = ndx;
String strSelectedDefectName = lstDefectList.getDefect(ndx);
fldDefectName.setText(strLocalDefectName);
}
I have assigned the item which I got from the list view to a text field in my application.
Thanks for the suggestions provided.