I am fairly new in JavaFX. I have a table with multiple columns and two buttons (btnBuilding , btnBSearch) outside the table. In the table, I have a column colAction where I want to have some buttons based on the button clicked outside the table. Suppose if I click btnBuilding I want to have 2 button Save and Refresh in my colAction column and Whenever I click btnBSearch I want to have 2 button Edit and Add in my colAction column. Inside the initialize() I tried like below
colAction.setCellFactory(col -> {
Button SaveButton = new Button("Save");
Button AddButton = new Button("Add");
Button RefreshButton = new Button("Refresh");
Button EditButton = new Button("Edit");
HBox hbox = new HBox(5);
if(btnBSearch.isFocused())
hbox.getChildren().addAll(AddButton,EditButton);
else if(btnBuilding.isFocused())
hbox.getChildren().addAll(SaveButton,RefreshButton);
TableCell<ModelBrBuilding, ModelBrBuilding> cell = new TableCell<ModelBrBuilding, ModelBrBuilding>() {
#Override
//Updating with the number of row
public void updateItem(ModelBrBuilding building, boolean empty) {
super.updateItem(building, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(hbox);
}
}
};
EditButton.setOnAction((ActionEvent event)->{
});
RefreshButton.setOnAction(event->{
});
SaveButton.setOnAction((ActionEvent event) -> {
});
AddButton.setOnAction(event -> {
});
return cell ;
});
But the problem is whatever button I click I am always getting Add and Edit in my action column. How can I add different button in my column based on the button (resides outside the table) I click?
The cellFactory runs only once for each cell. You need to make sure the cell is updated the button outside of the table is clicked.
You could do this by creating a property that contains a factory for the graphics and listen to it.
public interface GraphicFactory<T> {
Node createGraphic();
void updateGraphic(Node graphic, T item);
}
public class ReplacableGraphicTableCell<S, T> extends TableCell<S, T> {
private final ChangeListener<GraphicFactory<T>> factoryListener = (o, oldValue, newValue) -> {
if (newValue == null || isEmpty()) {
setGraphic(null);
} else {
Node n = newValue.createGraphic();
newValue.updateGraphic(n, getItem());
setGraphic(n);
}
};
private final ObservableValue<GraphicFactory<T>> factory;
private ReplacableGraphicTableCell(ObservableValue<GraphicFactory<T>> factory) {
this.factory = factory;
factory.addListener(factoryListener);
}
public static <E, F> Callback<TableColumn<E, F>, TableCell<E, F>> forTableColumn(ObservableValue<GraphicFactory<F>> factory) {
if (factory == null) {
throw new IllegalArgumentException();
}
return column -> new ReplacableGraphicTableCell(factory);
}
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
GraphicFactory<T> fact = factory.getValue();
if (fact == null) {
setGraphic(null);
} else {
Node graphic = getGraphic();
if (graphic == null) {
graphic = fact.createGraphic();
setGraphic(graphic);
}
fact.updateGraphic(graphic, item);
}
}
}
}
final ObjectProperty<GraphicFactory<Item>> graphicFactory = new SimpleObjectProperty<>();
TableColumn<Item, Item> column = new TableColumn<>();
column.setCellValueFactory(cd -> new SimpleObjectProperty<>(cd.getValue()));
column.setCellFactory(ReplacableGraphicTableCell.forTableColumn(graphicFactory));
ToggleGroup tg = new ToggleGroup();
tg.selectedToggleProperty().addListener((o, oldValue, newValue) -> {
GraphicFactory<Item> factory = null;
if (newValue != null) {
factory = (GraphicFactory<Item>) newValue.getUserData();
}
graphicFactory.set(factory);
});
RadioButton rb = new RadioButton("Add/Edit");
rb.setUserData(new GraphicFactory<Item>() {
#Override
public Node createGraphic() {
Button add = new Button("Add");
Button edit = new Button("Edit");
HBox hbox = new HBox(add, edit);
add.setOnAction(evt -> {
System.out.println("Add " + hbox.getUserData());
});
edit.setOnAction(evt -> {
System.out.println("Edit " + hbox.getUserData());
});
return hbox;
}
#Override
public void updateGraphic(Node graphic, Item item) {
graphic.setUserData(item);
}
});
rb.setToggleGroup(tg);
RadioButton rb2 = new RadioButton("Save/Refresh");
rb2.setUserData(new GraphicFactory<Item>() {
#Override
public Node createGraphic() {
Button save = new Button("Save");
Button refresh = new Button("Refresh");
HBox hbox = new HBox(save, refresh);
save.setOnAction(evt -> {
System.out.println("Save " + hbox.getUserData());
});
refresh.setOnAction(evt -> {
System.out.println("Refresh " + hbox.getUserData());
});
return hbox;
}
#Override
public void updateGraphic(Node graphic, Item item) {
graphic.setUserData(item);
}
});
rb2.setToggleGroup(tg);
It will not work this way. To begin with, you need to process the btnBuilding and btnBSearch buttons. Which of the buttons is pressed must reflect in the table you are using. For this purpose, one feature can be created propert to reflect which of the two buttons is pressed.
BooleanProperty showSearch = new SimpleBooleanProperty(false);
...
btnBuilding.setOnAction(e -> showSearch.setValue(false));
btnBSearch.setOnAction(e -> showSearch.setValue(true));
Then, you link the colAction column to the value of the property.
colAction.setCellValueFactory(cdf -> showSearch);
In this situation, you can create CellFactory to create the dynamic content cell
colAction.setCellFactory(col -> {
return new TableCell<String, Boolean>() {
Button SaveButton = new Button("Save");
Button AddButton = new Button("Add");
Button RefreshButton = new Button("Refresh");
Button EditButton = new Button("Edit");
HBox hboxBuilding = new HBox(5);
HBox hboxSearch = new HBox(5);
{
hboxBuilding.getChildren().addAll(AddButton,EditButton);
hboxSearch.getChildren().addAll(SaveButton,RefreshButton);
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
}
else {
setGraphic(item ? hboxBuilding : hboxSearch);
}
}
};
});
Related
I want to have a context menu on a TreeView item.
I am expecting that the ActionEvent of the event handler gives me information about which TreeView item is clicked, but I just find that getSource and getTarget return a Menu Item. How can I find out which TreeView Item has been clicked? I can have multiple treevIews in separate Tabs.
FileContextMenu cm = new FileContextMenu(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
System.out.println("Open File");
//MenuItem mi = (MenuItem)e.getSource();
EventTarget et = e.getTarget();
//File editorFile = new File(mi.getId());
System.out.println(et);
//mainWindowController.openEditor(editorFile);
}
}, new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
System.out.println("Create Project From Template");
}
});
which calls this:
public class FileContextMenu extends ContextMenu
{
public FileContextMenu(EventHandler<ActionEvent> ehOpenFile,
EventHandler<ActionEvent> ehProjectFromTemplate)
{
MenuItem item1 = new MenuItem("Open File");
item1.setOnAction(ehOpenFile);
MenuItem item2 = new MenuItem("Create Project From Template");
item2.setOnAction(ehProjectFromTemplate);
this.getItems().addAll(item1, item2);
}
}
I am attaching the menu here:
private void addFilesTab(FXMLLoader loader, String sRoot, ContextMenu cm) throws IOException
{
AnchorPane fileView = loader.load();
FileViewController fileViewController = loader.getController();
FileShort fsRoot = new FileShort(sRoot);
if(fsRoot.exists()) {
fileViewController.setRootFolder(fsRoot);
fileViewController.setContextMenu(cm);
ObservableList<Tab> tabs = navigationTabPane.getTabs();
tabs.add(new Tab(sRoot));
// Connect the FileView with last tab of the Navigation TabPane.
tabs.get(tabs.size()-1).setContent(fileView);
}
}
which calls this:
public void setContextMenu(ContextMenu cm)
{
fileTreeView.setContextMenu(cm);
}
I now try to use a cellfactory, but I don't understand how to use the p parameter to find a cells value . My code for this is:
this.fileTreeView.setCellFactory(new Callback<TreeView<FileShort>,TreeCell<FileShort>>(){
#Override
public TreeCell<FileShort> call(TreeView<FileShort> p) {
TreeCell<FileShort> cell = new TreeCell<FileShort>();
cell.setContextMenu(cm);
return cell;
}
});
You have to create a different context menu for each cell:
this.fileTreeView.setCellFactory(new Callback<TreeView<FileShort>,TreeCell<FileShort>>(){
#Override
public TreeCell<FileShort> call(TreeView<FileShort> p) {
TreeCell<FileShort> cell = new TreeCell<FileShort>() {
#Override
protected void updateItem(FileShort file, boolean empty) {
super.updateItem(file, empty);
if (empty) {
setText(null);
} else {
// maybe use a more appropriate string for display here
// e.g. if you were using a regular java.io.File you would
// likely want file.getName()
setText(file.toString());
}
}
};
ContextMenu cm = createContextMenu(cell);
cell.setContextMenu(cm);
return cell;
}
});
private ContextMenu createContextMenu(TreeCell<FileShort> cell) {
ContextMenu cm = new ContextMenu();
MenuItem openItem = new MenuItem("Open File");
openItem.setOnAction(event -> {
FileShort file = cell.getItem();
if (file != null) {
// open the file...
}
});
cm.getItems().add(openItem);
// other menu items...
return cm ;
}
I have JFXTreeTableView which consist of 5 columnsx In that first 2 columns have Delete & Edit Buttons for each cell. After populating table
I want first columns should disable on save Button click.
If above case is not possible then delete Buttons inside first column's cells should be disabled on Save button click.
I did like this but dont know how to disable column or buttons inside cells.
Controller Class
public class FinanceActionsController implements Initializable {
#FXML
private JFXTreeTableView<InvoiceItems> tblInvoiceItemsView;
private JFXButton btnSave;
#FXML
private HBox hbBottonBtnBar;
ObservableList<InvoiceItems> invoiceItems = FXCollections.observableArrayList();
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
tableStructure();
btnSave.setOnAction((ActionEvent event) -> {
if (invoiceItems.isEmpty()) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Please add Atleast one Invoice Item");
alert.showAndWait();
} else {
onClickBtnSaveInvoice();
disableAndAddControlsOnSave();
//tblInvoiceItemsView.setDisable(true);
}
});
}
private void tableStructure() {
JFXTreeTableColumn<InvoiceItems, Boolean> delItem = new JFXTreeTableColumn<>("Delete");
JFXTreeTableColumn<InvoiceItems, String> editItem = new JFXTreeTableColumn<>("Edit");
JFXTreeTableColumn<InvoiceItems, String> billItem = new JFXTreeTableColumn<>("Billable Head");
delItem.setCellValueFactory((TreeTableColumn.CellDataFeatures<InvoiceItems, Boolean> param) -> param.getValue().getValue().getBtnFlag());
delItem.setCellFactory(new Callback<TreeTableColumn<InvoiceItems, Boolean>, TreeTableCell<InvoiceItems, Boolean>>() {
#Override
public TreeTableCell<InvoiceItems, Boolean> call(TreeTableColumn<InvoiceItems, Boolean> param) {
final TreeTableCell<InvoiceItems, Boolean> cell = new TreeTableCell<InvoiceItems, Boolean>() {
MaterialIconView del = new MaterialIconView(MaterialIcon.DELETE_FOREVER, "1.5em");
final JFXButton btnDel = new JFXButton("", del);
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
btnDel.disableProperty().bind(txtN.disableProperty());
del.setFill(Color.RED);
btnDel.setButtonType(JFXButton.ButtonType.RAISED);
btnDel.setOnAction(event -> {
});
setGraphic(btnDel);
setText(null);
}
}
};
return cell;
}
});
billItem.setCellValueFactory((TreeTableColumn.CellDataFeatures<InvoiceItems, String> param) -> param.getValue().getValue().getBillItemDesc());
final TreeItem<InvoiceItems> root = new RecursiveTreeItem<>(invoiceItems, RecursiveTreeObject::getChildren);
tblInvoiceItemsView.getColumns().setAll(delItem, editItem, billItem);
tblInvoiceItemsView.setRoot(root);
tblInvoiceItemsView.setShowRoot(false);
}
Class InvoiceItems -
class InvoiceItems extends RecursiveTreeObject<InvoiceItems> {
StringProperty billItemDesc;
BooleanProperty btnFlag;
public InvoiceItems(String billItemDesc) {
this.billItemDesc = new SimpleStringProperty(billItemDesc);
}
public StringProperty getBillItemDesc() {
return billItemDesc;
}
public BooleanProperty getBtnFlag() {
return btnFlag;
}
public void setBtnFlag(Boolean btnFlag) {
this.btnFlag = new SimpleBooleanProperty(btnFlag);
}
}
I have tried to pass InvoiceItems setBtnFlag as True in Observable list to work in setCellFactory's updateItem method but not working. Please help any help will be appreciable, Thank You.
I have started making a program in JavaFX where i have ListView with added functionality like editing, adding new items and deleting.I have managed to do most of the work but there is still a little more to do. So far i have added the deleting and editing functionality successfully but i have got a small problem with the adding functionality. I can still add new items to the ListView but i can't make it possible to edit the newly added item, because i need the ListCell the new item represents to start editing it. So my question is how can i get the ListCell of the new item in the ListView.
For this purpose here is my code:
private TextField textField;
ListCell<String> cell = this;
int i = 0;
boolean manually_selected = false;
public LanguageListCell(ListView<String> languages)
{
ContextMenu contextMenu = new ContextMenu();
cell.setEditable(true);
MenuItem editItem = new MenuItem();
editItem.textProperty().bind(Bindings.format("Edit \"%s\"", cell.itemProperty()));
editItem.setOnAction(event -> {
// The LanguageListCell class i want to put here...
cell.startEdit();
});
MenuItem addItem = new MenuItem("Add language");
addItem.setOnAction(new EventHandler<ActionEvent> () {
#Override
public void handle(ActionEvent ev)
{
i++;
String lang = "New Language " + i;
languages.getItems().add(lang);
if(i == 10)
{
addItem.setDisable(true);
}
languages.getSelectionModel().clearSelection();
languages.getSelectionModel().select(languages.getItems().size() - 1);
cell.setItem(languages.getSelectionModel().getSelectedItem());
manually_selected = true;
}
});
MenuItem deleteItem = new MenuItem();
deleteItem.textProperty().bind(Bindings.format("Delete \"%s\"", cell.itemProperty()));
deleteItem.setOnAction(new EventHandler<ActionEvent> () {
#Override
public void handle(ActionEvent ev)
{
if(languages.getSelectionModel().getSelectedItems().size() - 1 > 0)
{
if(i > 0)
{
i = (languages.getItems().size() - languages.getSelectionModel().getSelectedItems().size()) - 1;
}
/*for(String lang: languages.getSelectionModel().getSelectedItems())
{
languages.getItems().remove(lang);
}*/
ArrayList<String> delete_data = new ArrayList<String>(languages.getSelectionModel().getSelectedItems());
languages.getItems().removeAll(delete_data);
}
languages.getItems().remove(cell.getItem());
if(i > 0) i = 0;
}
});
contextMenu.setOnShowing(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent e) {
if(languages.getSelectionModel().getSelectedItems().size() - 1 > 0)
{
editItem.setDisable(true);
addItem.setDisable(true);
}
}
});
contextMenu.getItems().addAll(addItem, editItem, deleteItem);
cell.textProperty().bind(cell.itemProperty());
cell.emptyProperty().addListener((obs, wasEmpty, isNowEmpty) -> {
if (isNowEmpty) {
cell.setContextMenu(null);
} else {
cell.setContextMenu(contextMenu);
}
});
}
I have a table that lists objects of type Bot which have a name and isOn properties that I want to list:
private SimpleStringProperty name;
private boolean isOn;
The boolean isOn, I want to be read from a checkbox and also editable from that checkbox
So far, I have been able to add a checkbox to a column in my table for each row but it is purely visual (i.e. it is not tied to the Bot's isOn member).
How can I make the checkbox read and write from and to this member of Bot?
Here is my code dealing with the Table altogether:
ObservableList<Bot> bots = FXCollections.observableArrayList();
#FXML
private TableView<Bot> botTable;
#FXML
private TableColumn<Bot, String> nameColumn;
#FXML
private TableColumn<Bot, Boolean> statusColumn;
public void initialize(URL location, ResourceBundle resources){
nameColumn.setCellValueFactory(new PropertyValueFactory<Bot, String>("name"));
statusColumn.setCellValueFactory(new PropertyValueFactory<Bot, Boolean>("on"));
statusColumn.setSortable(false);
statusColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Bot, Boolean>, ObservableValue<Boolean>>(){
#Override public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Bot, Boolean> features) {
return new SimpleBooleanProperty(features.getValue() != null);
}
});
// create a cell value factory with an add button for each row in the table.
statusColumn.setCellFactory(new Callback<TableColumn<Bot, Boolean>, TableCell<Bot, Boolean>>() {
#Override public TableCell<Bot, Boolean> call(TableColumn<Bot, Boolean> personBooleanTableColumn) {
return new AddBotCell(/*stage, botTable*/);
}
});
botTable.setItems(bots);
}
/** A table cell containing a button for adding a new person. */
private class AddBotCell extends TableCell<Bot, Boolean> {
// a checkbox for adding a new bot.
final CheckBox checkbox = new CheckBox();
// pads and centers the add button in the cell.
final StackPane paddedCheckBox = new StackPane();
AddBotCell(/*final Stage stage, final TableView table*/) {
paddedCheckBox.setPadding(new Insets(3));
paddedCheckBox.getChildren().add(checkbox);
checkbox.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
}
});
}
/** places an add checkbox in the row only if the row is not empty. */
#Override protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(checkbox);
}
}
}
You need to remove the checkbox, if the cell becomes empty. Furthermore you need to update the value when the user interacts with the CheckBox. This is better done from a listener to the selected property:
private class AddBotCell extends TableCell<Bot, Boolean> {
// a button for adding a new person.
final CheckBox checkbox = new CheckBox();
// pads and centers the add button in the cell.
final StackPane paddedCheckBox = new StackPane();
// records the y pos of the last button press so that the add person dialog can be shown next to the cell.
final DoubleProperty buttonY = new SimpleDoubleProperty();
private boolean updating = false;
AddBotCell(/*final Stage stage, final TableView table*/) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
paddedCheckBox.setPadding(new Insets(3));
paddedCheckBox.getChildren().add(checkbox);
checkbox.selectedProperty().addListener((o, oldValue, newValue) -> {
if (!updating) {
updating = true;
((Bot)getTableRow().getItem()).setIsOn(newValue);
updating = false;
}
});
}
/** places an add button in the row only if the row is not empty. */
#Override protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
setGraphic(paddedCheckBox);
updating = true;
checkbox.setSelected(item);
updating = false;
}
}
}
Also your cellValueFactory should use the value of the property.
statusColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Bot, Boolean>, ObservableValue<Boolean>>(){
#Override public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Bot, Boolean> features) {
return new SimpleBooleanProperty(features.getValue().isIsOn());
}
});
I want to add checkbox and textfield to one property of PropertySheet (ControlsFX library). Is it possible or no? So, i just need to add some GUI elements together to one PropertyEditor, for example checkbox + button, checkbox + label, checkbox + textfield and etc. Is it possible to override PropertyEditor to do it?
you could also wrap multiples nodes inside a single parent .[See Here
Solved by myself. I tried to add checkbox + combobox to HBox. Code below, it works.
public static final <T> PropertyEditor<?> createCheckBoxLinkEditor(PropertySheet.Item property,
final Collection<T> choices) {
ComboBox<T> comboBox = new ComboBox<T>();
comboBox.setCellFactory((ListView<T> p) -> new ListCell<T>() {
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
} else if (item instanceof Class) {
setText(((Class) item).getSimpleName());
} else {
setText(item.toString());
}
}
});
HBox hbox = new HBox(5);
CheckBox checkBox = new CheckBox();
hbox.getChildren().add(checkBox);
hbox.getChildren().add(comboBox);
//hbox.getA
//comboBox.setConverter(value);
return new AbstractPropertyEditor<T, HBox>(property, hbox) {
{
comboBox.setItems(FXCollections.observableArrayList(choices));
//new AutoCompleteComboBoxListener(comboBox);
new SelectKeyComboBoxListener(comboBox);
}
#Override
protected ObservableValue<T> getObservableValue() {
return comboBox.getSelectionModel().selectedItemProperty();
}
#Override
public void setValue(T value) {
comboBox.getSelectionModel().select(value);
}
};
}