AutoComplete ComboBox with ControlsFX triggers auto completion when selecting value with mouse - javafx

I have a simple ComboBox that I've used ControlsFX to make auto-completing. This works perfectly except for one annoying flaw: When the user uses the dropdown to select an item with the mouse, the autocomplete window opens up, essentially offering matching suggestions to the user.
The desired behavior is for the selection of a value with the mouse to close the popup altogether.
The code below will demonstrate:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import org.controlsfx.control.textfield.TextFields;
public class AutoCompleteComboBox extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// Create a standard ComboBox
ComboBox<Person> comboBox = new ComboBox<>();
// Sample items
ObservableList<Person> people = FXCollections.observableArrayList();
people.addAll(
new Person("William", 34),
new Person("Ashley", 12),
new Person("Marvin", 63),
new Person("Suresh", 18),
new Person("Jackson", 24)
);
comboBox.setItems(people);
// Add StringConverter
comboBox.setConverter(new StringConverter<Person>() {
#Override
public String toString(Person person) {
return (person == null ? null : person.getName());
}
#Override
public Person fromString(String string) {
for (Person person : comboBox.getItems()) {
if (person.getName().equalsIgnoreCase(string)) {
return person;
}
}
return null;
}
});
// Make the ComboBox autocomplete using ControlsFX
comboBox.setEditable(true);
TextFields.bindAutoCompletion(comboBox.getEditor(), comboBox.getItems());
root.getChildren().add(comboBox);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
}
class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
#Override
public String toString() {
return name;
}
}
So typing into the editor and selecting a value with the keyboard works fine. But if you click the arrow to open the dropdown and select a value that way, you can see the issue (it essentially forces the user to select a value twice).
How would I go about preventing this behavior?

Related

How can a choice box, checkbox, and text field be implemented into one button with JavaFX [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I'm trying to create a program that can take data from a choicebox, checkbox, and text field and put it into a single textArea as output. How can I have all the information combine from one button. I have already created the choicebox, checkbox, and text fields.
-There are two text fields. One of them takes a name, and the other takes a number. The number will determine how many times the program is looped.
-The checkbox will take what kind of prefix to put in front of the name (Mr, Mrs, etc).
-The choicebox give options on what follow up statement will be given. (How was your day?, I like your hat, etc)
After clicking the button, the program would display something like this in a textArea if the number in the textField had an input of 2
"1Hello Mr. Johnson. How was your day?"
"2Hello Mr. Johnson. How was your day?"
This is what I have in the button event handler. It only implements the text fields
private class CreateButtonHandler implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent event) {
int i = 0;
String myString = number.getText();
int foo = Integer.parseInt(myString);
do {
OutputTxtArea.setText(OutputTxtArea.getText() + (i + 1) + "Hello " + firstNameTxtFld.getText() + "\n");
} while(i<foo);
}
}
Bindings!
Things are easier to do if you separate the layout from the data and the actions, and then have the actions work on the data. This leaves the screen to just be a screen, and you're not worried about "how do I deal with getting the CheckBox values?" kind of questions.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.jetbrains.annotations.NotNull;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Greeter extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Model model = new Model();
ChoiceBox<String> questionChoiceBox = new ChoiceBox<>(model.questionList);
TextArea textArea = new TextArea("");
textArea.textProperty().bind(model.greetingProperty());
VBox vBox = new VBox(20,
createBoundCheckBox("Miss", model.isMissProperty()),
createBoundCheckBox("Missus", model.isMissusProperty()),
createBoundCheckBox("Mister", model.isMisterProperty()),
questionChoiceBox,
createBoundTextField(model.nameProperty()),
createBoundTextField(model.loopCountProperty()),
textArea);
primaryStage.setScene(new Scene(vBox, 200, 300));
primaryStage.show();
}
#NotNull
private Node createBoundCheckBox(String label, BooleanProperty boundProperty) {
CheckBox checkBox = new CheckBox(label);
boundProperty.bind(checkBox.selectedProperty());
return checkBox;
}
private Node createBoundTextField(StringProperty boundProperty) {
TextField textField = new TextField();
boundProperty.bind(textField.textProperty());
return textField;
}
class Model {
private StringProperty loopCount = new SimpleStringProperty("");
private BooleanProperty isMister = new SimpleBooleanProperty(false);
private BooleanProperty isMissus = new SimpleBooleanProperty(false);
private BooleanProperty isMiss = new SimpleBooleanProperty(false);
private StringProperty name = new SimpleStringProperty("");
private StringProperty question = new SimpleStringProperty("");
private ObservableList<String> questionList = FXCollections.observableArrayList();
Model() {
questionList.add("How was your day?");
questionList.add("Would you like kippers for breakfast");
}
public StringProperty loopCountProperty() {
return loopCount;
}
public BooleanProperty isMisterProperty() {
return isMister;
}
public BooleanProperty isMissusProperty() {
return isMissus;
}
public BooleanProperty isMissProperty() {
return isMiss;
}
public StringProperty nameProperty() {
return name;
}
public StringProperty questionProperty() {
return question;
}
public ObservableList<String> getQuestionList() {
return questionList;
}
public ObservableValue<String> greetingProperty() {
return Bindings.createStringBinding(() -> buildGreeting(), loopCount, isMiss, isMissus, isMister, question, name);
}
private String buildGreeting() {
int howMany;
try {
howMany = Integer.parseInt(loopCount.get());
} catch (Exception e) {
return "";
}
String title = "Master of Time and Space";
if (isMiss.get()) {
title = "Miss";
}
if (isMissus.get()) {
title = "Mrs.";
}
if (isMister.get()) {
title = "Mr.";
}
String greeting = "Hello " + title + " " + name.get() + " " + question.get();
return IntStream.range(1, howMany + 1).mapToObj(idx -> idx + greeting).collect(Collectors.joining("\n"));
}
}
}
So now the layout is just a layout, and all of the controls on the screen have their value properties bound to the Model. Then the model has the code to put all the stuff together to create the result. Technically, that code should belong in some sort of business logic class, but this should at least give you the idea.
I've left out the button because it doesn't really add any meaningful functionality, and it's slightly problematic to the UI. There's no way to indicate if the result in the TextArea is current to the last button click. For instance, you could click the button, then pick another title checkbox and now the selections on the screen don't match the TextArea. But there's no way for the user to know this, and to implement it you'd probably have to go to something like the DirtyFX library.
But if you really want a button, then all you have to do is introduce a new StringProperty for the result, then have the button call Model.buildGreeting() in its EventHandler:
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.jetbrains.annotations.NotNull;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class GreeterButton extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Model model = new Model();
ChoiceBox<String> questionChoiceBox = new ChoiceBox<>(model.questionList);
TextArea textArea = new TextArea("");
textArea.textProperty().bind(model.greetingProperty());
Button button = new Button("Say Hi!");
button.setOnAction(evt -> model.updateGreeting());
VBox vBox = new VBox(20,
createBoundCheckBox("Miss", model.isMissProperty()),
createBoundCheckBox("Missus", model.isMissusProperty()),
createBoundCheckBox("Mister", model.isMisterProperty()),
questionChoiceBox,
createBoundTextField(model.nameProperty()),
createBoundTextField(model.loopCountProperty()),
button,
textArea);
primaryStage.setScene(new Scene(vBox, 200, 300));
primaryStage.show();
}
#NotNull
private Node createBoundCheckBox(String label, BooleanProperty boundProperty) {
CheckBox checkBox = new CheckBox(label);
boundProperty.bind(checkBox.selectedProperty());
return checkBox;
}
private Node createBoundTextField(StringProperty boundProperty) {
TextField textField = new TextField();
boundProperty.bind(textField.textProperty());
return textField;
}
class Model {
private StringProperty loopCount = new SimpleStringProperty("");
private BooleanProperty isMister = new SimpleBooleanProperty(false);
private BooleanProperty isMissus = new SimpleBooleanProperty(false);
private BooleanProperty isMiss = new SimpleBooleanProperty(false);
private StringProperty name = new SimpleStringProperty("");
private StringProperty question = new SimpleStringProperty("");
private StringProperty greeting = new SimpleStringProperty("");
private ObservableList<String> questionList = FXCollections.observableArrayList();
Model() {
questionList.add("How was your day?");
questionList.add("Would you like kippers for breakfast");
}
public StringProperty loopCountProperty() {
return loopCount;
}
public BooleanProperty isMisterProperty() {
return isMister;
}
public BooleanProperty isMissusProperty() {
return isMissus;
}
public BooleanProperty isMissProperty() {
return isMiss;
}
public StringProperty nameProperty() {
return name;
}
public StringProperty questionProperty() {
return question;
}
public ObservableList<String> getQuestionList() {
return questionList;
}
String buildGreeting() {
int howMany;
try {
howMany = Integer.parseInt(loopCount.get());
} catch (Exception e) {
return "";
}
String title = "Master of Time and Space";
if (isMiss.get()) {
title = "Miss";
}
if (isMissus.get()) {
title = "Mrs.";
}
if (isMister.get()) {
title = "Mr.";
}
String greeting = "Hello " + title + " " + name.get() + " " + question.get();
return IntStream.range(1, howMany + 1).mapToObj(idx -> idx + greeting).collect(Collectors.joining("\n"));
}
public void updateGreeting() {
greeting.set(buildGreeting());
}
public StringProperty greetingProperty() {
return greeting;
}
}
}

JavaFx property binding with multiple objects on on screen

I use JavaFx with property binding.
I got a object 'Person' with the properties 'name' and age.
These objects are stored in a ObservableList.
The properties are bound to labels on the gui. When I change the person in the ListBox the data also change on the right hand side.
GUI with person list:
And now it comes to my problem.
I want to disply all persons on one window, like the next picture shows.
GUI with multiple persons on one view:
How can I handle this. I thought about HBox but the binding doesn't work.
FYI: Here you can find the tutorial I used.
https://code.makery.ch/library/javafx-tutorial/part1/
This looks like a perfect time to use a ListView with custom ListCell implementations.
The sample application below shows a very basic application that displays each Person object in a ListView. We will provide our own ListCell so we can control exactly how each Person gets displayed.
I also added a profile photo just for fun :)
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.Separator;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ListViewDetailSample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// First, let's create our list of Persons
ObservableList<Person> persons = FXCollections.observableArrayList();
persons.addAll(
new Person("John", 34),
new Person("Cheyenne", 24),
new Person("Micah", 17),
new Person("Katelyn", 28)
);
// Create a ListView
ListView<Person> listView = new ListView<>();
// Bind our list to the ListView
listView.setItems(persons);
// Now, for the magic. We'll create our own ListCells for the ListView. This allows us to create a custom
// layout for each individual cell. For this sample, we'll include a profile picture, the name, and the age.
listView.setCellFactory(new Callback<ListView<Person>, ListCell<Person>>() {
#Override
public ListCell<Person> call(ListView<Person> param) {
return new ListCell<Person>() {
#Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
// Set any empty cells to show nothing
if (person == null || empty) {
setText(null);
setGraphic(null);
} else {
// Here we can build our layout. We'll use a HBox for our root container
HBox cellRoot = new HBox(5);
cellRoot.setAlignment(Pos.CENTER_LEFT);
cellRoot.setPadding(new Insets(5));
// Add our profile picture
ImageView imgProfilePic = new ImageView("/sample/user.png");
imgProfilePic.setFitHeight(24);
imgProfilePic.setFitWidth(24);
cellRoot.getChildren().add(imgProfilePic);
// A simple Separator between the photo and the details
cellRoot.getChildren().add(new Separator(Orientation.VERTICAL));
// Now, create a VBox to hold the name and age
VBox vBox = new VBox(5);
vBox.setAlignment(Pos.CENTER_LEFT);
vBox.setPadding(new Insets(5));
// Add our Person details
vBox.getChildren().addAll(
new Label("Name: " + person.getName()),
new Label("Age: " + person.getAge())
);
// Add our VBox to the cellRoot
cellRoot.getChildren().add(vBox);
// Finally, set this cell to display our custom layout
setGraphic(cellRoot);
}
}
};
}
});
// Now, add our ListView to the root layout
root.getChildren().add(listView);
// Show the Stage
primaryStage.setWidth(450);
primaryStage.setHeight(400);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
// Simple Person class
class Person {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty age = new SimpleIntegerProperty();
public Person(String name, int age) {
this.name.set(name);
this.age.set(age);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public int getAge() {
return age.get();
}
public IntegerProperty ageProperty() {
return age;
}
public void setAge(int age) {
this.age.set(age);
}
}
The Result:
Without ListView:
If you'd prefer not to use a ListView for this display, you can keep another list of your Person displays and bind that to the children list of whichever container you want:
// Create a list to hold our individual Person displays
ObservableList<Node> personDisplays = FXCollections.observableArrayList();
// Now add a new PersonDisplay to the list for each Person in the personsList
persons.forEach(person -> personDisplays.add(new PersonDisplay(person)));
// Bind our personsDisplay list to the children of our root VBox
Bindings.bindContent(root.getChildren(), personDisplays);
PersonDisplay class:
class PersonDisplay extends HBox {
public PersonDisplay(Person person) {
// First, let's configure our root layout
setSpacing(5);
setAlignment(Pos.CENTER_LEFT);
setPadding(new Insets(5));
// Add our profile picture
ImageView imgProfilePic = new ImageView("/user.png");
imgProfilePic.setFitHeight(24);
imgProfilePic.setFitWidth(24);
getChildren().add(imgProfilePic);
// A simple Separator between the photo and the details
getChildren().add(new Separator(Orientation.VERTICAL));
// Now, create a VBox to hold the name and age
VBox vBox = new VBox(5);
vBox.setAlignment(Pos.CENTER_LEFT);
vBox.setPadding(new Insets(5));
// Add our Person details
vBox.getChildren().addAll(
new Label("Name: " + person.getName()),
new Label("Age: " + person.getAge())
);
// Add our VBox to the layout
getChildren().add(vBox);
}
}
The Result:

Sliding Down TableView in JavaFx

Im trying to do sliding down efect with my tableview like this:
http://jsfiddle.net/43nGr/90/
and I dont really know how to do that.
This is my simple program which dispalys a button and a tableView.
This is my code of main class:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application{
Stage window;
TableView <Product> table;
Button showTableButton;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
window.setTitle("Liga Pilkarska");
//First Column
TableColumn<Product, String> nameColumn = new TableColumn<>("Name");
nameColumn.setMinWidth(200);
nameColumn.setMaxWidth(200);
nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
//Second COlumn
TableColumn<Product, Double> priceColumn = new TableColumn<>("Price");
priceColumn.setMinWidth(100);
priceColumn.setMaxWidth(100);
priceColumn.setCellValueFactory(new PropertyValueFactory<>("price"));
//Third Column
TableColumn<Product, String> quantityColumn = new TableColumn<>("Quantity");
quantityColumn.setMinWidth(200);
quantityColumn.setMaxWidth(200);
quantityColumn.setCellValueFactory(new PropertyValueFactory<>("quantity"));
//Creating a table and adding columns
table = new TableView();
table.setItems(getProduct());
table.getColumns().addAll(nameColumn, priceColumn, quantityColumn);
table.setFixedCellSize(25);
//setting height of table
table.prefHeightProperty().bind(table.fixedCellSizeProperty().multiply(Bindings.size(table.getItems()).add(1.01)));
table.minHeightProperty().bind(table.prefHeightProperty());
table.maxHeightProperty().bind(table.prefHeightProperty());
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
showTableButton = new Button("Show Table");
VBox vBox = new VBox();
vBox.getChildren().addAll(showTableButton,table);
//Creating scene
Scene scene = new Scene(vBox, 800,600);
scene.getStylesheets().add("Viper.css");
window.setScene(scene);
window.show();
}
public ObservableList<Product> getProduct(){
ObservableList<Product> products = FXCollections.observableArrayList();
products.add(new Product("Laptop", 2300, 10));
products.add(new Product("Latarka", 50, 19));
products.add(new Product("Suszarka", 89.99, 22));
products.add(new Product("Monitor", 100, 44));
products.add(new Product("Kukurydza", 1.5, 1));
return products;
}
}
and this is my code of "Products" class which are in the table.
public class Product {
public String name;
public double price;
public int quantity;
public Product(){
this.name = "";
this.price = 0;
this.quantity = 0;
}
public Product(String name, double price, int quantity){
this.name = name;
this.price = price;
this.quantity = quantity;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
So if i click or hover on a Button , tableView should start to do sliding down efect.
If anyone know how to do that , would be great.
I found the solution, maybe it helps someone.
We need to override interpolate method from Transition.
So I created new class which extends Transition.
import javafx.animation.Transition;
import javafx.scene.control.TableView;
import javafx.util.Duration;
public class ResizeTheHeightOfTableView extends Transition{
public TableView<Product> table;
public int maxSize;
public ResizeTheHeightOfTableView(Duration duration, TableView<Product> table, int maxSize){
setCycleDuration(duration);
this.maxSize = maxSize;
this.table = table;
}
#Override
protected void interpolate(double fraction) {
table.setFixedCellSize(fraction * maxSize );
}
}
and then in Main class just add two lines of code:
ResizeTheHeightOfTableView ft = new ResizeTheHeightOfTableView(Duration.millis(500), table, 25);
showTableButton.setOnAction(e -> ft.play());
and dont forget to import duration:
import javafx.util.Duration;
You have easy option with animated TitledPane(container) where you set animated flag to TRUE and call expand when desired event is fired.
Or even better way create separated thread that animates prefered/min size of your TableView , depends in what kind of container you have your component placed in.From animation thread dont forget to set size on JFXAT and sleep on animation thread.
To prevent 2 animations to run simultanliously use lock/flag that thread has to check before it executes.Simple boolean woud be sufficient.

Understanding CheckBoxTableCell changelistener using setSelectedStateCallback

I'm trying to follow: CheckBoxTableCell changelistener not working
The given code answer to that question is below and dependent on the model 'Trainee'
final CheckBoxTableCell<Trainee, Boolean> ctCell = new CheckBoxTableCell<>();
ctCell.setSelectedStateCallback(new Callback<Integer, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(Integer index) {
return table.getItems().get(index).selectedProperty();
}
});
I would like to obtain that selected property value and add a listener to it, but I don't think I'm doing it right. I attempted to add all kind of listeners to it so that I know when the checkbox in each row is changed and I can add logic to each. I presume the code above allow ctCell to now observe changes and I can just call a change listener to it and detect selection per given row.
I tried some change properties here just to detect the changes:
ctCell.selectedStateCallbackProperty().addListener(change -> {
System.out.println("1Change happened in selected state property");
});
ctCell.selectedProperty().addListener(change -> {
System.out.println("2Change happened in selected property");
});
ctCell.itemProperty().addListener(change -> {
System.out.println("3Change happened in item property");
});
ctCell.indexProperty().addListener(change -> {
System.out.println("4Change happened in index property");
});
...but none seemed to be called.
This is the shorten set up that I have:
requestedFaxCol.setCellValueFactory(new PropertyValueFactory("clientHasRequestedFax"));
requestedFaxCol.setCellFactory(CheckBoxTableCell.forTableColumn(requestedFaxCol));
final CheckBoxTableCell<ClinicClientInfo, Boolean> ctCell = new CheckBoxTableCell<>();
ctCell.setSelectedStateCallback(new Callback<Integer, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(Integer index) {
return clinicLinkTable.getItems().get(index).clientHasRequestedFaxProperty();}
});
Let me know if I need to provide a more information! What am I not understanding in terms of why I cannot bridge a change listener to my table cell check boxes? Or if someone can point out the a direction for me to try. Thanks!
UPDATE to depict the ultimate goal of this question
package testapp;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TestApp extends Application {
private TableView<ClinicClientInfo> clientTable = new TableView<>();
private TableColumn<ClinicClientInfo, String> faxCol = new TableColumn<>("Fax");
private TableColumn<ClinicClientInfo, Boolean> requestedFaxCol = new TableColumn<>("Requested Fax");
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
ObservableList<ClinicClientInfo> list = FXCollections.observableArrayList(
new ClinicClientInfo("", false),
new ClinicClientInfo("945-342-4324", true));
root.getChildren().add(clientTable);
clientTable.getColumns().addAll(faxCol, requestedFaxCol);
clientTable.setItems(list);
clientTable.setEditable(true);
clientTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
faxCol.setCellValueFactory(new PropertyValueFactory<>("clinicFax"));
faxCol.setVisible(true);
requestedFaxCol.setCellValueFactory(new PropertyValueFactory("clientHasRequestedFax"));
requestedFaxCol.setCellFactory(CheckBoxTableCell.forTableColumn(requestedFaxCol));
requestedFaxCol.setVisible(true);
requestedFaxCol.setEditable(true);
//My attempt to connect the listener
//If user selects checkbox and the fax value is empty, the alert should prompt
CheckBoxTableCell<ClinicClientInfo, Boolean> ctCell = new CheckBoxTableCell<>();
ctCell.setSelectedStateCallback(new Callback<Integer, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(Integer index) {
ObservableValue<Boolean> itemBoolean = clientTable.getItems().get(index).clientHasRequestedFaxProperty();
itemBoolean.addListener(change -> {
ClinicClientInfo item = clientTable.getItems().get(index);
if(item.getClinicFax().isEmpty() && item.getClientHasRequestedFax()){
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setTitle("Warning");
alert.show();
}
});
return itemBoolean;
}
});
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public class ClinicClientInfo {
private final StringProperty clinicFax;
private final BooleanProperty clientHasRequestedFax;
public ClinicClientInfo(String fax, boolean clientHasRequestedFax){
this.clinicFax = new SimpleStringProperty(fax);
this.clientHasRequestedFax = new SimpleBooleanProperty(clientHasRequestedFax);
}
public String getClinicFax(){
return clinicFax.get();
}
public void setClinicFax(String clinicFax){
this.clinicFax.set(clinicFax);
}
public StringProperty clinicFaxProperty(){
return clinicFax;
}
public boolean getClientHasRequestedFax(){
return clientHasRequestedFax.get();
}
public void setClientHasRequestedFax(boolean clientHasRequestedFax){
this.clientHasRequestedFax.set(clientHasRequestedFax);
}
public BooleanProperty clientHasRequestedFaxProperty(){
return clientHasRequestedFax;
}
}
}
The goal is to get a prompt when the user tries to select fax request when the fax string is empty.
This is already fully explained in the question you already linked, so I don't know what more I can add here other than just to restate it.
The check boxes in the cell are bidirectionally bound to the property that is returned by the selectedStateCallback. If no selectedStateCallback is set, and the cell is attached to a column whose cellValueFactory returns a BooleanProperty (which covers almost all use cases), then the check box's state is bidirectionally bound to that property.
In your code sample, I don't understand what ctCell is for. You just create it, set a selectedStateCallBack on it, and then don't do anything with it. It has nothing to do with your table and nothing to do with the cell factory you set.
So in your case, no selected state callback is set on the cells produced by your cell factory, and the cell value factory returns a boolean property, so the default applies, and the check box state is bidirectionally bound to the property returned by the cell value factory. All you have to do is register a listener with those properties.
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class CheckBoxTableCellTestApp extends Application {
private TableView<ClinicClientInfo> clientTable = new TableView<>();
private TableColumn<ClinicClientInfo, String> faxCol = new TableColumn<>("Fax");
private TableColumn<ClinicClientInfo, Boolean> requestedFaxCol = new TableColumn<>("Requested Fax");
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
ObservableList<ClinicClientInfo> list = FXCollections.observableArrayList(
new ClinicClientInfo("", false),
new ClinicClientInfo("945-342-4324", true));
// add listeners to boolean properties:
for (ClinicClientInfo clinic : list) {
clinic.clientHasRequestedFaxProperty().addListener((obs, faxWasRequested, faxIsNowRequested) ->{
System.out.printf("%s changed fax request from %s to %s %n",
clinic.getClinicFax(), faxWasRequested, faxIsNowRequested);
});
}
root.getChildren().add(clientTable);
clientTable.getColumns().addAll(faxCol, requestedFaxCol);
clientTable.setItems(list);
clientTable.setEditable(true);
clientTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
faxCol.setCellValueFactory(new PropertyValueFactory<>("clinicFax"));
faxCol.setVisible(true);
requestedFaxCol.setCellValueFactory(new PropertyValueFactory<>("clientHasRequestedFax"));
requestedFaxCol.setCellFactory(CheckBoxTableCell.forTableColumn(requestedFaxCol));
requestedFaxCol.setVisible(true);
requestedFaxCol.setEditable(true);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public class ClinicClientInfo {
private final StringProperty clinicFax;
private final BooleanProperty clientHasRequestedFax;
public ClinicClientInfo(String fax, boolean clientHasRequestedFax){
this.clinicFax = new SimpleStringProperty(fax);
this.clientHasRequestedFax = new SimpleBooleanProperty(clientHasRequestedFax);
}
public String getClinicFax(){
return clinicFax.get();
}
public void setClinicFax(String clinicFax){
this.clinicFax.set(clinicFax);
}
public StringProperty clinicFaxProperty(){
return clinicFax;
}
public boolean getClientHasRequestedFax(){
return clientHasRequestedFax.get();
}
public void setClientHasRequestedFax(boolean clientHasRequestedFax){
this.clientHasRequestedFax.set(clientHasRequestedFax);
}
public BooleanProperty clientHasRequestedFaxProperty(){
return clientHasRequestedFax;
}
}
}

How to fetch value of selected item in choice box from a table view coloumn in javafx

How can i fetch the value of the selected choice from the choce box in the following table.
column3 has 13 choice box nodes populated using following code.I want to fetch the selected item.
final ObservableList LogLevelList=FXCollections.observableArrayList("FATAL", "ERROR", "WARN", "INFO", "INOUT", "DEBUG");
column3.setCellFactory(new Callback<TableColumn<Feature,String>,TableCell<Feature,String>>(){
#Override
public TableCell<Feature,String> call(TableColumn<Feature,String> param) {
TableCell<Feature,String> cell = new TableCell<Feature,String>(){
#Override
public void updateItem(String item, boolean empty) {
System.out.println("Inside UpdateItem");
ChoiceBox choice = new ChoiceBox(LogLevelList);
choice.getSelectionModel().select(LogLevelList.indexOf(item));
//SETTING ALL THE GRAPHICS COMPONENT FOR CELL
setGraphic(choice);
}
};
return cell;
}
});
Does the predefined ChoiceBoxTableCell do what you need?
column3.setCellFactory(ChoiceBoxTableCell.forTableColumn(logLevelList));
See if this helps:
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ChoiceBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TableChoiceBoxTest extends Application {
#Override
public void start(Stage primaryStage) {
final TableView<Feature> table = new TableView<>();
table.setEditable(true);
final TableColumn<Feature, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
final TableColumn<Feature, String> logLevelCol = new TableColumn<>("Log level");
logLevelCol.setCellValueFactory(new PropertyValueFactory<>("logLevel"));
logLevelCol.setPrefWidth(150);
final ObservableList<String> logLevelList = FXCollections.observableArrayList("FATAL", "ERROR", "WARN", "INFO", "INOUT", "DEBUG");
logLevelCol.setCellFactory(ChoiceBoxTableCell.forTableColumn(logLevelList));
table.getColumns().addAll(nameCol, logLevelCol);
table.getItems().setAll(
IntStream.rangeClosed(1, 20)
.mapToObj(i -> new Feature("Item "+i, "FATAL"))
.collect(Collectors.toList())
);
Button showDataButton = new Button("Dump data");
showDataButton.setOnAction(event -> table.getItems().forEach(System.out::println));
BorderPane root = new BorderPane();
root.setCenter(table);
root.setBottom(showDataButton);
Scene scene = new Scene(root, 400, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class Feature {
private final StringProperty name ;
private final StringProperty logLevel ;
public Feature(String name, String logLevel) {
this.name = new SimpleStringProperty(this, "name", name);
this.logLevel = new SimpleStringProperty(this, "logLevel", logLevel);
}
public StringProperty nameProperty() {
return name ;
}
public final String getName() {
return name.get();
}
public final void setName(String name) {
this.name.set(name);
}
public StringProperty logLevelProperty() {
return logLevel ;
}
public final String getLogLevel() {
return logLevel.get();
}
public final void setLogLevel(String logLevel) {
this.logLevel.set(logLevel);
}
#Override
public String toString() {
return getName() + ": " + getLogLevel();
}
}
public static void main(String[] args) {
launch(args);
}
}
The provided ChoiceBoxTableCell updates the property of the associated item for you, so there's never any need to get the value from the ChoiceBox; you can just get the value from your model object.
I think there are mistakes in your code. You do not want to display your Choice box in each and every cell of that column (i.e Emptied Row's Cell) and Also you should call super class function.
Now for getting the selected value of ChoiceBox , instead of just displaying your choicebox with the values you will have to save them in some ArrayList or Map or best options is to save inside your Feature class. So that you can finally use
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item,empty);
if(item != null){
ChoiceBox choice = new ChoiceBox(LogLevelList);
choice.getSelectionModel().select(LogLevelList.indexOf(item));
choice.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String t, String t1) {
//either use : myMap.put(getIndex(),t1);
//or : item.setChoice(t1);
}
});
//SETTING ALL THE GRAPHICS COMPONENT FOR CELL
setGraphic(choice);
}
}
Also for demo of ChoiceBox in TableView there is one blog post for you :http://blog.ngopal.com.np/2011/10/01/tableview-cell-modifiy-in-javafx/

Resources