Why my filed component is not removing dynamically in javafx - javafx

I want to add textfield and combobox dynamically on button click but not able to do it. Below is the code which I tried. In this code I have added the field successfully but not able to remove the item one by one on button click. only last item is removing after adding the field multiple times. so please check my code where I did mistake.
package application;
import java.awt.Color;
import java.net.URL;
import java.util.ResourceBundle;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXTextField;
import com.sun.javafx.scene.layout.region.Margins;
import javafx.event.ActionEvent;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.control.Alert;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
public class CustomerController implements Initializable {
public JFXTextField
acc_no,ifsc_code,micr_code,acc_no1,ifsc_code1,micr_code1;
public JFXComboBox<String> bank_name,bank_name1;
public JFXButton add_row,rmv_row;
public GridPane grid_component;
public VBox vBox2_component, vbox1_component;
public AnchorPane anchor_pane;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
// TODO Auto-generated method stub
bank_name.getItems().removeAll(bank_name.getItems());
bank_name.getItems().addAll("Bank of India", "Dena Bank", "HDFC Bank");
new AutoCompleteComboBoxListener(bank_name);
}
// add button functionality
public void AddBankDetails(ActionEvent event) {
/*if(count == max_row-1){
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Maximum of 10 rows can be added\",\"Failed!!");
alert.showAndWait();
return;
}
count++;*/
bank_name1 = new JFXComboBox();
bank_name1.getStyleClass().add("textfield_margin");
bank_name1.setLabelFloat(true);
bank_name1.setFocusColor(javafx.scene.paint.Color.valueOf("#2196F3"));
bank_name1.setPromptText("Bank Name");
bank_name1.setUnFocusColor(javafx.scene.paint.Color.valueOf("939393"));
vbox1_component.setMargin(bank_name1, new Insets(20, 10, 10, 10));
ifsc_code1 = new JFXTextField();
ifsc_code1.getStyleClass().add("textfield_margin");
ifsc_code1.setLabelFloat(true);
ifsc_code1.setFocusColor(javafx.scene.paint.Color.valueOf("#2196F3"));
ifsc_code1.setPromptText("IFSC Code");
ifsc_code1.setUnFocusColor(javafx.scene.paint.Color.valueOf("939393"));
vbox1_component.setMargin(ifsc_code1, new Insets(20, 10, 10, 10));
acc_no1 = new JFXTextField();
acc_no1.getStyleClass().add("textfield_margin");
acc_no1.setLabelFloat(true);
acc_no1.setFocusColor(javafx.scene.paint.Color.valueOf("#2196F3"));
acc_no1.setPromptText("Account number");
acc_no1.setUnFocusColor(javafx.scene.paint.Color.valueOf("939393"));
vBox2_component.setMargin(acc_no1, new Insets(20, 10, 10, 10));
micr_code1 = new JFXTextField();
micr_code1.getStyleClass().add("textfield_margin");
micr_code1.setLabelFloat(true);
micr_code1.setFocusColor(javafx.scene.paint.Color.valueOf("#2196F3"));
micr_code1.setPromptText("MICR Code");
micr_code1.setUnFocusColor(javafx.scene.paint.Color.valueOf("939393"));
vBox2_component.setMargin(micr_code1, new Insets(20, 10, 10, 10));
vbox1_component.getChildren().addAll(bank_name1,ifsc_code1);
vBox2_component.getChildren().addAll(acc_no1,micr_code1);
}
//remove button functionality
public void rmvBankDetails(ActionEvent events) {
vbox1_component.getChildren().removeAll(bank_name1,ifsc_code1);
vBox2_component.getChildren().removeAll(acc_no1,micr_code1);
}
}

You only store a reference to the controls that are created for the last call of AddBankDetails. This means if you click the add button multiple times and then click the remove button multiple times. You only get a single successful remove operation for the VBoxes.
Assuming this is the only code modifying the VBoxes and the VBoxes don't contain other children, you could simply remove the last 2 children from each list:
private static void removeElements(List<?> list, int count) {
int size = list.size();
if (size >= count) {
list.subList(size - count, size).clear();
}
}
public void rmvBankDetails(ActionEvent events) {
removeElements(vbox1_component.getChildren(), 2);
removeElements(vBox2_component.getChildren(), 2);
}
Additional notes:
Depending on your layout using GridPane could be a option. TableView may also be worth a look.
You probably need access to all your controls, so keeping a list of the newly added controls in a list could be desireable. Using this data you could also retrieve the controls to remove.
private static class Container {
final JFXComboBox bank_name1;
final JFXTextField ifsc_code1;
final JFXTextField acc_no1;
final JFXTextField micr_code1;
Container(JFXComboBox bank_name1, JFXTextField ifsc_code1, JFXTextField acc_no1, JFXTextField micr_code1) {
this.bank_name1 = bank_name1;
this.ifsc_code1 = ifsc_code1;
this.acc_no1 = acc_no1;
this.micr_code1 = micr_code1;
}
}
private final List<Container> bankDetailsContainers = new ArrayList<>();
public void AddBankDetails(ActionEvent event) {
...
bankDetailsContainers.add(new Container(bank_name1, ifsc_code1, acc_no1, micr_code1));
}
public void rmvBankDetails(ActionEvent events) {
if (!bankDetailsContainers.isEmpty()) {
Container c = bankDetailsContainers.remove(bankDetailsContainers.size() - 1);
vbox1_component.getChildren().removeAll(c.bank_name1, c.ifsc_code1);
vBox2_component.getChildren().removeAll(c.acc_no1, c.micr_code1);
}
}

Related

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

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?

How to concatenate event handler with another method call?

I have two buttons in two separate classes, and I want to change the onAction of the first button when the second button has been pressed to be the original action plus one additional method call. Once the first button has been pressed I want its onAction to revert to the original.
What I currently have working is essientially
Button b1 = new Button("b1");
b1.setOnAction((event)-> {
oldActionMethod();
});
public void oldActionMethod(){
//actual code
}
b2.setOnAction((event)-> {
//some stuff
Button b1 = getB1();
EventHandler<ActionEvent> temp = b1.getOnAction();
b1.setOnAction((event) -> {
b1class.oldActionMethod();
additionalMethod();
b1.setOnAction(temp);
});
});
In order to make this work I had to move the block of code that was originally in the setOnAction lambda expression to a helper function. Is there a cleaner way to do this? Something like this which would eliminate the need for the helper function?
b1.setOnAction((event)-> {
//actual code
});
b2.setOnAction((event) -> {
//stuff
Button b1 = getB1();
EventHandler<ActionEvent> temp = b1.getOnAction();
b1.setOnAction(b1.getOnAction() + methodCall());
b1.setOnAction(temp);
//stuff
});
The way I have it currently does work but it feels really hack-y so I am just interested to know if there is a better option where you could essentially concatenate an actionEvent with another method. Also if there is a way to not require storing the original event in a temp object and resetting it at the end. A possible solution would be if I could tell b2 to listen for the next time b1 is pressed, but I don't know if there is any way to do that when they are in two separate classes.
One solution is to have a shared model class between the two classes containing the buttos.
See the following mcve. For conviniense the entire code can be copy-pasted into one file (FaMain.java) and run:
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class FxMain extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Model model = new Model();
AClass aClass = new AClass(model);
BClass bClass = new BClass(model);
Label show = new Label();
show.textProperty().bind(model.getTextProperty());
VBox root = new VBox(10);
root.getChildren().addAll(aClass.getButton(),show, bClass.getButton());
primaryStage.setScene(new Scene(root, 400,100));
primaryStage.sizeToScene();
primaryStage.show();
}
public static void main(final String[] args) {
launch(args);
}
}
class Model {
private final BooleanProperty aButtonSelected;
private final SimpleStringProperty textProperty;
Model(){
aButtonSelected = new SimpleBooleanProperty();
textProperty= new SimpleStringProperty();
}
ObservableValue<? extends String> getTextProperty() {
return textProperty;
}
BooleanProperty aButtonSelectedProperty(){
return aButtonSelected;
}
void bButtonClicked() {
textProperty.set(aButtonSelected.get() ? "Button clicked. Toggle IS selected" :
"Button clicked. Toggle is NOT selected");
}
}
class AClass{
private final ToggleButton aButton;
AClass(Model model) {
aButton = new ToggleButton("Toogle");
model.aButtonSelectedProperty().bind(aButton.selectedProperty());
}
ToggleButton getButton(){
return aButton;
}
}
class BClass{
private final Button bButton;
BClass(Model model) {
bButton = new Button("Click");
bButton.setOnAction(e->model.bButtonClicked());
}
Button getButton(){
return bButton;
}
}

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:

Get Response from JFoenix Dialog

I am using JFoenix in my new JavaFX Application. I've successfully created my own message dialog.
package Dialog;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDialog;
import com.jfoenix.controls.JFXDialogLayout;
import com.jfoenix.controls.events.JFXDialogEvent;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.VPos;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
public class OK_Message extends JFXDialog {
private StackPane Container;
private JFXDialogLayout Content;
String DialogText;
String Headline;
JFXButton btn;
boolean OK = false;
private BooleanProperty okval;
public OK_Message(
StackPane Container,
String Headline,
String DialogText
){
this.Container = Container;
this.DialogText = DialogText;
this.Headline = Headline;
}
public void ShowDialog(){
setDialogContainer(Container);
setContent(getDialogContent());
setTransitionType(JFXDialog.DialogTransition.TOP);
setOverlayClose(false);
setFocusTraversable(true);
setOnDialogOpened((JFXDialogEvent event) -> {
Platform.runLater(()->{
btn.requestFocus();
});});
setOnKeyPressed((KeyEvent e)->{
e.consume();
if(e.getCode()== KeyCode.ENTER){close();}
else if(e.getCode()== KeyCode.SPACE){btn.requestFocus();}
else{}
});
show();
}
private JFXDialogLayout getDialogContent(){
Content = new JFXDialogLayout();
Content.setHeading(HeadLine());
Content.setBody(Body());
// Platform.runLater(()->{b.requestFocus();});
Content.setActions(getButton());
return Content;
}
private Label HeadLine(){
Label l = new Label(Headline);
l.setFont(new Font(18));
return l;
}
private GridPane Body(){
Label l = new Label(DialogText);
l.setFont(new Font(14));
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(8);
GridPane.setConstraints(l, 0, 0, 1, 1, HPos.LEFT, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS, new Insets(0, 0, 0, 0));
grid.getChildren().addAll(l);
return grid;
}
private JFXButton getButton(){
btn = new JFXButton("OK");
btn.setButtonType(JFXButton.ButtonType.FLAT);
btn.setPrefWidth(50);
btn.setTextFill(Color.WHITE);
btn.setOnAction((ActionEvent event) -> {
CloseDialog();
});
btn.setStyle("-fx-background-color:#FFFFFF");
btn.focusedProperty().addListener(new ChangeListener<Boolean>(){
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(newValue){
btn.setStyle("-fx-background-color:red");
}
}
});
return btn;
}
private void CloseDialog(){
close();
}
}
Now I can call my dialog anywhere: like this:
Dialog.OK_Message message =
new Dialog.OK_Message((StackPane)app_setup.getParent(),
"Message",
"Changes will be affected after restart.");
message.ShowDialog();
I cannot find a way to detect if use has clicked the button or not. Or what key is pressed by the user. If the user has clicked the OK Button I want to do something. Or if the use has pressed some key I want to do some other thing ? How can I listen to the above dialog ?
You can use setOnKeyPressed on your OK_Message class just like you used inside your class since it is extending the JFXDialog.
message.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
System.out.println(event.getCode().getName());
}
});
For listening which button is clicked why not create customEventListenerfor your OK_Message class?
In your OK_Message.java file create an interface outside of the class like;
interface OnEventListener {
void onButtonCliked(String id);
}
and add a new method to your OK_Message class as
public void setOnEventListener(OnEventListener listener) {
mOnEventListener = listener;
}
First
Lets change your getButton() method to this one to add functionality of creating multiple buttons with different IDs and texts.
private JFXButton getButton(String id,String text){
JFXButton btn = new JFXButton(text);
btn.setId(id);
btn.setButtonType(JFXButton.ButtonType.FLAT);
btn.setPrefWidth(50);
btn.setTextFill(Color.WHITE);
btn.setOnAction((ActionEvent event) -> {
mOnEventListener.onButtonCliked(btn.getId()); //Here we are firing the event
CloseDialog();
});
btn.setStyle("-fx-background-color:red");
return btn;
}
In getDialogContent() lets create more than one button.
Content.setActions(getButton("Button1","OK"),getButton("Button2","CANCEL"));
Now you can use listeners after declaring new OK_Message objects
OK_Message message =
new OK_Message((StackPane)stackpane,
"Message",
"Changes will be affected after restart.");
message.ShowDialog();
message.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
System.out.println(event.getCode().getName());
}
});
message.setOnEventListener(new OnEventListener() {
#Override
public void onButtonCliked(String id) {
System.out.println(id);
}
});
Determine what do to according to the id observed by onButtonClicked() function.

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;
}
}
}

Resources