How to iterate through a JavaFX-combobox? - java

My question is simple: How do I iterate throught a javafx-combobox? I tried so much and nothing works. The foreach-construct does not work and combobox does not even have methods for size and single object retrieval, eliminating the possibility to use a simple-for-loop. Am I missing something?

Yes you are missing..or maybe not. But, nevertheless, just return ObservableList of objects from your ComboBox and use for each to iterate through that collection. Here is an example:
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class ComboBoxExample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
GridPane gridPane = new GridPane();
Button button = new Button("Print items from ComboBox");
final ComboBox<String> comboBox = new ComboBox<String>();
//Add some items
comboBox.getItems().addAll("John","Jane","Laina");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
//To iterate through ComboBox
ObservableList<String> items = comboBox.getItems();
for(String item : items){
System.out.println(item.toString());
}
}
});
gridPane.add(comboBox,0,0);
gridPane.add(button,0,1);
primaryStage.setScene(new Scene(gridPane,320,280));
primaryStage.show();
}
}

Related

Update JavaFX stage within method

I am writing a program that uses a lot of data to do several calculations and it takes quite a while to finish. This is why I want to update and inform the user on the current percentage of the program being done. However I don't know how I can update the stage within the calculation method.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
public void calculate() {
//Here I want to change the "percentage text"
//and update the stage.
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
Text percentage = new Text();
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
calculate();
}
});
final GridPane inputGridPane = new GridPane();
GridPane.setConstraints(btn, 0, 0);
GridPane.setConstraints(percentage, 1, 0);
inputGridPane.setHgap(6);
inputGridPane.setVgap(6);
inputGridPane.getChildren().addAll(btn, percentage);
final Pane rootGroup = new VBox(12);
rootGroup.setPrefWidth(400);
rootGroup.setPrefHeight(225);
rootGroup.getChildren().addAll(inputGridPane);
rootGroup.setPadding(new Insets(12,12,12,12));
primaryStage.setScene(new Scene(rootGroup));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
This is what my code roughly looks like. Thank you in advance.
JavaFx provides a class called "Task" which can be used for background calculations while keeping the progression syncronised!
I would suggest doing it that way:
Create a second class extending Task for doing the heavy calculation:
import javafx.concurrent.Task;
public class CalculationTask extends Task<String> {
#Override
protected String call() throws Exception {
for (int i = 0; i < 100000000;i++) {
updateProgress(i, 100000000);
}
succeeded();
return "This is the computed result... Datatype can be changed by generics if you are calculation numbers";
}
}
This is one way how you could use it in your application class:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Dialog;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setText("Say 'Hello World'");
Task<String> calculationTask = new CalculationTask();
ProgressBar progressBar = new ProgressBar(); //ProgressIndicator could also be used
progressBar.progressProperty().bind(calculationTask.progressProperty());
progressBar.visibleProperty().bind(calculationTask.runningProperty());
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
calculationTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent workerStateEvent) {
new Alert(Alert.AlertType.CONFIRMATION, calculationTask.getValue()).show();
}
});
new Thread(calculationTask).start();
}
});
final GridPane inputGridPane = new GridPane();
GridPane.setConstraints(btn, 0, 0);
GridPane.setConstraints(progressBar, 1, 0);
inputGridPane.setHgap(6);
inputGridPane.setVgap(6);
inputGridPane.getChildren().addAll(btn, progressBar);
final Pane rootGroup = new VBox(12);
rootGroup.setPrefWidth(400);
rootGroup.setPrefHeight(225);
rootGroup.getChildren().addAll(inputGridPane);
rootGroup.setPadding(new Insets(12,12,12,12));
primaryStage.setScene(new Scene(rootGroup));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I hope that helped, otherwise feel free to ask questions!

Add String row to TableView in JAVA FX

I can't find a way to add a simple "String" row to my tableView.
In fact i can add a row but its content is not visible ...
Here is my code:
#FXML
private TableView<String> table;
#FXML
private TableColumn<String, String> table2;
public ObservableList<String> getLanes()
{
ObservableList<String> lanes=FXCollections.observableArrayList();
lanes.add("TEST");
return lanes;
}
Then:
table.setItems(getLanes()); //Not working
and
table.getItems().add("TEST"); //Not working
But without success.
I read that and that as well as other documentations but it did not help me to do it in this simple way.
EDIT:
Adding this line solved my problem:
table2.setCellValueFactory(param -> new ReadOnlyStringWrapper(param.getValue()));
Here is a simple application where we are trying to load a single value into a TableView column. It also shows how to set a cellValueFactory() on a table column.
tableColumn.setCellValueFactory(param -> new ReadOnlyStringWrapper(param.getValue()));
MCVE
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TableView<String> tableView = new TableView<>();
TableColumn<String, String> tableColumn = new TableColumn<>("Name");
tableColumn.setCellValueFactory(param -> new ReadOnlyStringWrapper(param.getValue()));
tableView.getColumns().add(tableColumn);
ObservableList<String> items = FXCollections.observableArrayList("Itachi");
tableView.setItems(items);
VBox root = new VBox(tableView);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 300, 275);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX binding between TextField and a property

If you create a binding between a JavaFX TextField and a property, then this binding is invalidated on every keystroke, which causes a change to the text.
If you have a chain of bindings the default behavior could cause problems, because in the middle of the editing values may be not valid.
Ok, I know I could create an uni-directional binding from the property to the textfield and register a change listener to get informed when the cursor leaves the field and update the property manually if necessary.
Is there an easy, elegant way to change this behavior so that the binding is only invalidated when the editing is complete, e.g. when the cursor leaves the field?
Thanks
I think you've pretty much described the only way to do it. Here's about the cleanest way I can see to implement it (using Java 8, though it's easy enough to convert the lambdas back to be JavaFX 2.2 compatible if you need):
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CommitBoundTextField extends Application {
#Override
public void start(Stage primaryStage) {
TextField tf1 = new TextField();
createCommitBinding(tf1).addListener((obs, oldText, newText) ->
System.out.printf("Text 1 changed from \"%s\" to \"%s\"%n", oldText, newText));
TextField tf2 = new TextField();
createCommitBinding(tf2).addListener((obs, oldText, newText) ->
System.out.printf("Text 2 changed from \"%s\" to \"%s\"%n", oldText, newText));
VBox root = new VBox(5, tf1, tf2);
Scene scene = new Scene(root, 250, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
private StringBinding createCommitBinding(TextField textField) {
StringBinding binding = Bindings.createStringBinding(() -> textField.getText());
textField.addEventHandler(ActionEvent.ACTION, evt -> binding.invalidate());
textField.focusedProperty().addListener((obs, wasFocused, isFocused)-> {
if (! isFocused) binding.invalidate();
});
return binding ;
}
public static void main(String[] args) {
launch(args);
}
}
I realize that I am a little late with a response, but thought this might be useful to someone.
When using TextFields, I often attach a TextFormatter to help validate entries. You can attach a listener to the formatters' valueProperty. That property is updated when the text is committed, rather than on every keystroke.
Here's an example of what I am talking about using a TextField specialized for integer inputs. When you make edits in the text field, the changes will be reflected in the Label when you tap Enter, lose focus by clicking the button, switch to a different window, and so on.
import javafx.application.Application;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.converter.IntegerStringConverter;
class IntTextField extends TextField {
private final IntegerProperty value;
TextFormatter<Integer> formatter;
public double getValue() {
return value.getValue();
}
public void setValue(int newValue) {
value.setValue(newValue);
}
public IntegerProperty valueProperty() {
return value;
}
public StringBinding getStringBinding () {
return value.asString();
}
IntTextField(int initValue) {
value = new SimpleIntegerProperty(initValue);
setText(initValue + "");
formatter = new TextFormatter(new IntegerStringConverter(), initValue);
formatter.valueProperty().addListener((ObservableValue<? extends Integer> obs,
Integer oldValue, Integer newValue) -> value.setValue(newValue));
setTextFormatter(formatter);
}
IntTextField() {
this(0);
}
}
public class TFBindingDemo extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("TFBindingDemo");
IntTextField intTextField = new IntTextField(12345);
intTextField.setMaxWidth(150);
Label label = new Label("Type in the TextField");
label.textProperty().bind(intTextField.getStringBinding());
Button removeFocusButton = new Button("Click Here to Remove Focus");
VBox root = new VBox(20, intTextField, label, removeFocusButton);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(20));
Scene scene = new Scene(root, 325, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}

JavaFx removing from pane object when I am in this object's class

I don't know if anyone could understand my problem from the title, but here's more specific description. I have class, in which I created a FlowPane, where I added objects of another class(images packed inside VBoxes). Each VBox have ContextMenu, where is MenuItem "Remove File". My problem is, how to remove this object while beeing inside the VBox class. Here is a little part of my code:
//removed, entire code is below after edit
The code where I'm accessing my CustomPane (my class of FlowPane, with specified attributes) works, because I can remove object if I'm doing it by their indexes, but when I remove one of them, other's indexes changes, so I'm looking for another solution. I need to specifically remove the object of the class in the code.
Okay so here is so called sscce, which of I had no idea, since now:
package sscce;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Sscce extends Application {
#Override
public void start(Stage primaryStage) {
CustomPane root = new CustomPane();
root.setPadding(new Insets(20));
root.setHgap(10);
root.setVgap(10);
for (int i = 0; i < 10; i++) {
RectangleBox recB = new RectangleBox();
root.getChildren().add(recB);
}
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class RectangleBox extends VBox {
static int index = 0;
public RectangleBox() {
Rectangle rec = new Rectangle(150, 150);
rec.setFill(Color.GREEN);
StackPane sp = new StackPane();
sp.getChildren().add(rec);
Label label = new Label(Integer.toString(index));
index++;
sp.getChildren().add(label);
getChildren().add(sp);
final ContextMenu cm = new ContextMenu();
MenuItem removeRec = new MenuItem("Remove Rectangle");
removeRec.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
((CustomPane) getParent()).getPane().getChildren().remove(0); //here is the problem, I want this line to remove the rectangle I clicked on(now it's removing first element in the pane).
}
});
cm.getItems().add(removeRec);
createContextMenuEvent(cm, rec);
}
private void createContextMenuEvent(final ContextMenu cm, final Rectangle rec) {
addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
if (t.getButton() == MouseButton.SECONDARY) {
cm.show(rec, t.getScreenX(), t.getScreenY());
}
}
});
}
}
class CustomPane extends FlowPane {
public CustomPane() {
//setAlignment(Pos.CENTER);
setHgap(25);
setVgap(25);
setPadding(new Insets(20));
}
public CustomPane getPane() {
return this;
}
}
It should work after copy/paste this entire code to java project. So I removed everything that is not neccessary, and I have replaced images to rectangles, now this program looks kind of stupid;p
I added comment to a line I have problem with. I hope now it's a lot clearer than before.
try this:
((CustomPane) RectangleBox.this.getParent()).getChildren().remove(RectangleBox.this);
hope it helps.

JavaFx :Default Message for Empty ListView

When there is no record in any table it shows a message 'No content in table', which is by default functionality of TableView in JavaFx.
So here my question is, does the same can be possible with ListView in JavaFx ? Like, if there is no item in any ListView then it will show a message same as TableView, instead of a blank/empty fields.
You have to try this:-
listView.setPlaceholder(new Label("No Content In List"));
its 100% working....
JavaFX8 has a setPlaceholder(...) method for ListView.
In earlier versions, you need to roll your own somehow. This is a bit of a hack: it wraps the ListView in a stack pane, with a white rectangle and the placeholder displayed over the top of the list view. The placeholder and rectangle have their visible property bound, so they are only visible if the list is empty.
There may be easier ways that I'm not seeing right away...
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ListViewPlaceholderTest extends Application {
#Override
public void start(Stage primaryStage) {
final ListView<String> listView = new ListView<>();
final IntegerProperty counter = new SimpleIntegerProperty();
final Button addButton = new Button("Add item");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
counter.set(counter.get()+1);
listView.getItems().add("Item "+counter.get());
}
});
final Button removeButton = new Button("Remove");
removeButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
listView.getItems().remove(listView.getSelectionModel().getSelectedIndex());
}
});
removeButton.disableProperty().bind(Bindings.equal(listView.getSelectionModel().selectedIndexProperty(), -1));
final HBox buttons = new HBox(5);
buttons.setPadding(new Insets(10));
buttons.getChildren().addAll(addButton, removeButton);
final BorderPane root = new BorderPane();
root.setCenter(createPlaceholderForListView(listView, new Label("No content in List")));
root.setBottom(buttons);
final Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private Node createPlaceholderForListView(ListView<?> listView, Node placeholder) {
final StackPane pane = new StackPane();
final Rectangle rect = new Rectangle(0, 0, Color.WHITE);
rect.widthProperty().bind(listView.widthProperty());
rect.heightProperty().bind(listView.heightProperty());
pane.getChildren().addAll(listView, rect, placeholder);
placeholder.visibleProperty().bind(Bindings.isEmpty(listView.getItems()));
rect.visibleProperty().bind(placeholder.visibleProperty());
rect.setMouseTransparent(true);
return pane ;
}
}
With fxml:
<ListView fx:id="foundContentList">
<placeholder>
<Label text="Nothing found" />
</placeholder>
</ListView>
Not entirely sure but I don't think there is a setPlaceholder method(to set the default message when no content in table) for ListView.
The workaround that I use is to create an Object in the list that indicate "No content" and show that on the listview and also disable it.
For example:
ObservableList noContent= FXCollections.observableArrayList("No content found");
ListView listView = new ListView(noContent);
listView.setDisable(true);

Categories