Cancel edit in javafx tableview - java

I am fairly new in JavaFX. I have a table having multiple columns and a refresh button in each row. I am trying to set the old value in the edited cells whenever I click Refresh button. One idea is Passing the old value through a global variable to refresh button and set it. I can get the old value But how can I set that old value? here is my code
String old=null;
public void initialize(URL arg0, ResourceBundle arg1) {
colName.setCellValueFactory(new PropertyValueFactory<ModelBrBuilding,String>("BranchName"));
colName.setCellFactory(TextFieldTableCell.forTableColumn());
colName.setOnEditCommit(
new EventHandler<CellEditEvent<ModelBrBuilding, String>>() {
#Override
public void handle(CellEditEvent<ModelBrBuilding, String> t) {
old= ((ModelBrBuilding) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).getBranchName();
((ModelBrBuilding) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setBranchName(t.getNewValue());
}
}
);
colAction.setCellFactory(col -> {
Button RefreshButton = new Button("Refresh");
hbox.getChildren().add(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(RefreshButton);
}
}
};
RefreshButton.setOnAction(event->{
//here I need to set the old value
});
return cell ;
});
Can any one give me idea how can I do that?

Finally, I have found my answer which is working
RefreshButton.setOnAction(event -> {
ModelBrBuilding buildin = new ModelBrBuilding();
int i = tableBuilding.getSelectionModel().getSelectedIndex();
buildin.setBranchName(old);
tableBuilding.getItems().set(i, buildin);
});

Related

Remove Datepicker rendering from a TableView cell (JavaFX8)

I have a TableView with 2 columns “Date” (LocalDate) and “FX” (Double). I have enabled the cell editing and following an example I found here (http://physalix.com/javafx8-render-a-datepicker-cell-in-a-tableview/) I have created a custom CellFactory that displays a DatePicker for the cells of column “Date”. This solution though renders the DatePciker immediately, so I changed my code to show the DatePicker only when the user double clicks on any of the (non-empty) Date cells. So far so good…
How do I “go back” and remove the DatePicker rendering from the cell after the user has changed the date or cancelled the input? See the pictures as reference. Pic 1 is the initial state of the list. Pic 2 is after double click. How do I go back to Pic 1 status? Let me know if you need to see my specific code.
Reference pictures
This is the code that checks for the double click and then creates the CellFactory
fxTable.getSelectionModel().setCellSelectionEnabled(true);
fxTable.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() == 2) {
TablePosition pos = fxTable.getSelectionModel().getSelectedCells().get(0);
int col = pos.getColumn();
if (col == 0) {
//The code below creates the DatePicker in the cell using the DatePickerCell class that I created following the example in the code I found
tblDateFX.setCellFactory(new Callback<TableColumn<Map.Entry<LocalDate, Double>, String>, TableCell<Map.Entry<LocalDate, Double>, String>>() {
#Override
public TableCell<Map.Entry<LocalDate, Double>, String> call(TableColumn<Map.Entry<LocalDate, Double>, String> param) {
ObservableMap<LocalDate, Double> items = FXCollections.observableMap(myBasket.getEnrtriesCur(curName));
DatePickerCell datePick = new DatePickerCell(items);
return datePick;
}
});
}
}
}
});
This is the DatePickerCell Class
public class DatePickerCell<S, T> extends TableCell<Map.Entry<LocalDate,Double>, String> {
private DatePicker datePicker;
private ObservableMap<LocalDate,Double> curEntries;
public DatePickerCell(ObservableMap<LocalDate,Double> curEntries) {
super();
this.curEntries = curEntries;
if (datePicker == null) {
createDatePicker();
}
setGraphic(datePicker);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
Platform.runLater(new Runnable() {
#Override
public void run() {
datePicker.requestFocus();
}
});
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (null == this.datePicker) {
System.out.println("datePicker is NULL");
}
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
setContentDisplay(ContentDisplay.TEXT_ONLY);
} else {
datePicker.setValue(LocalDate.parse(item,df));
setGraphic(this.datePicker);
setText(item);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
}
#Override
public void startEdit() {
super.startEdit();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
setGraphic(null);
}
private void createDatePicker() {
this.datePicker = new DatePicker();
datePicker.setEditable(true);
datePicker.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
setGraphic(datePicker);
setText(df.format(datePicker.getValue()));
}
});
datePicker.setOnAction(new EventHandler() {
public void handle(Event t) {
LocalDate date = datePicker.getValue();
int index = getIndex();
commitEdit(df.format(date));
if (null != getCurEntries()) {
System.out.println("Modify value");
}
}
});
setAlignment(Pos.CENTER);
}
Have you tried the function setOnEditCommit to do reverse of your code?
column.setOnEditCommit((TableColumn.CellEditEvent<MyObject, Date> t) -> {
//modify the rendering of you cell to normal
});
After some research I found out that the default rendering of a cell in a TableView is a label. So I tweaked the DatePickerCell class to render a label in the "updateItem" method and render the DatePicker only when the label is clicked (meaning that the user wants to edit the date in the cell).
In terms of "going back" I added a listener for "ESC keypressed" on the DatePicker so when that key is pressed (during the edit) a label is rendered and the edit is therefore cancelled. That works quite well!
I'm still trying to figure out how to do the same when the user tries to cancel the edit by clicking somewhere else on the screen.
--
So here's my stab at the DatePickerEdit class.
This is doing what I need. Renders the cells normally at first, only when the user clicks on the date cell the datepicker is rendered. If the user clicks away from the cell, the cell goes back to its initial rendering (same happens when "ESC" is pressed whilst editing or indeed a new date is picked).
Note that I am passing to the class the Observable list that contains the values shown in the TableView. In this way I can update the value in the list directly in the class. Not sure if this is a good practice or not, this was a "forced solution" though. Originally I used the "setOnEditCommit" method for the TableColumn but after some testing I noticed that this event is not always called after the cell is updated (i.e. the commitEdit() method is called for the cell). Not sure if this is a bug or there's something wrong in my code. For sure it does not always happen. On multiple runs, I would say that 1 out of 3 showed this bugged behaviour.
Here's the code, not sure if it's a "good" code or not. I would appreciate any advice in merit.
public class DatePickerCell<S, T> extends TableCell<FX, String> {
private DatePicker datePicker;
private Label lbl;
private ObservableList<FX> currencies;
public DatePickerCell(ObservableList<FX> list) {
super();
lbl=new Label();
this.currencies=list;
if (datePicker == null) {
createDatePicker();
}
}
#Override
public void updateItem(String item, boolean empty) {
// This section here manages the graphic rendering of each cell
// As I don't want to generate the datepicker graphics immediately I just render a label
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
createLabel(item);
}
}
#Override
public void startEdit() {
super.startEdit();
}
#Override
public void cancelEdit() {
super.cancelEdit();
}
private void createDatePicker() {
this.datePicker = new DatePicker();
datePicker.setEditable(true);
// when the user clicks on the label the DatePicker graphics is generated
lbl.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
datePicker.setValue(LocalDate.parse(lbl.getText(),df));
setGraphic(datePicker);
setText(lbl.getText());
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
datePicker.requestFocus();
}
});
// This listener manages the "lost focus" on the picker
datePicker.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
// This combination of OldValue NewValue is generated whenever there is a click outside the DatePicker "graphic area"
// i.e. the calendar (when open), the text filed, the calendar icon OR when a NEW date is selected in the calendar.
// This last case generates the "OnAction" event as well that is managed below.
if (oldValue && !newValue) {
createLabel(df.format(datePicker.getValue()));
}
}
});
// This is generated when a NEW date is picked
// it simply commits the new date and changes the graphics back to a label
datePicker.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
LocalDate date = datePicker.getValue();
int index=getIndex();
if (date!=null) {
commitEdit(df.format(date));
getCurrencies().get(index).setDate(date);
createLabel(df.format(date));
}
}
});
// added this listener in case the user wants to cancel pressing "ESC"
// when this happens the label graphics is rendered
datePicker.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
LocalDate date = datePicker.getValue();
if (event.getCode()== KeyCode.ESCAPE) {
createLabel(df.format(date));
}
}
});
setAlignment(Pos.CENTER_LEFT);
}
private void createLabel(String item) {
lbl.setMinWidth(getWidth());
setGraphic(lbl);
lbl.setText(item);
}
public ObservableList<FX> getCurrencies() {
return currencies;
}
}

"Cannot find symbol constructor, EventHandler does not take parameters" when setting the action for a button in JavaFX

I am creating a program in JavaFX which lists tasks from a data base and displays a button for each row with allows a user to register the task as claimed in the database. I used the code on this link to help me with the buttons for each row: https://gist.github.com/jewelsea/3081826.
However, after changing the code to fit my program, i am getting an error in relation to setting the action of the cellButton variable. I have also added the class that calls this method, just in case.
CANNOT FIND SYMBOL CONSTRUCTOR, EVENTHANDLER DOES NOT TAKE PARAMETERS..
& METHOD DOES NOT OVERRIDE OR IMPLEMENT A METHOD FROM A SUPERTYPE (I am guessing this error is as a result of the first two errors).
//Define the button cell
private class ButtonCell extends TableCell<task, Boolean> {
final Button cellButton = new Button("Claim");
ButtonCell(){
//Action when the button is pressed
cellButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
// get Selected Item
task currentPerson = (task) ButtonCell.this.getTableView().getItems().get(ButtonCell.this.getIndex());
//remove selected item from the table list
newMan.claimTask(currentPerson.getTaskID());
}
});
}
//Display button if the row is not empty
#Override
protected void updateItem(Boolean t, boolean empty) {
super.updateItem(t, empty);
if(!empty){
setGraphic(cellButton);
}
}
}
x
private TableView createTasksTable() {
TableView tableView = new TableView();
TableColumn<task,String> firstNameCol = new TableColumn<>("Task");
firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<task, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<task, String> p) {
// p.getValue() returns the Person instance for a particular TableView row
return new SimpleStringProperty(p.getValue().getName());
}
});
//Insert Button
TableColumn col_action = new TableColumn<>("Action");
tableView.getColumns().add(col_action);
col_action.setCellValueFactory(
new Callback<TableColumn.CellDataFeatures<task, Boolean>,
ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<task, Boolean> p) {
return new SimpleBooleanProperty(p.getValue() != null);
}
});
//Adding the Button to the cell
col_action.setCellFactory(
new Callback<TableColumn<task, Boolean>, TableCell<task, Boolean>>() {
#Override
public TableCell<task, Boolean> call(TableColumn<task, Boolean> p) {
return new ButtonCell();
} });
tableView.getColumns().addAll(firstNameCol);
return tableView;
}
You have probably imported the wrong EventHandler. Make sure you have
import javafx.event.EventHandler ;
and not something from awt.

JavaFX TableColumn text wrapping

I am experiencing an issue when resizing a TableView which contains text items that wrap around TableCell items. Upon resizing, hidden values are resized but the visible items do not re-calculate the text wrapping.
The tweets in the red box were hidden during the resize and had their text wrapping adjusted as expected. Tweets above the box were visible during the resize phase and still have the old wrapping.
Below is my code for the resize phase.
fxSearchResultsTableTweet.setCellFactory(new Callback<TableColumn<Status, String>, TableCell<Status, String>>() {
#Override
public TableCell<Status, String> call(TableColumn<Status, String> arg0) {
return new TableCell<Status, String>() {
private Text text;
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!isEmpty()) {
text = new Text(item.toString());
text.setWrappingWidth(fxSearchResultsTableTweet.getWidth());
this.setWrapText(true);
setGraphic(text);
}
}
};
}
});
}
Any help would be greatly appreciated.
This is closer, but not great:
textCol.setCellFactory(new Callback<TableColumn<Status, String>, TableCell<String, String>>() {
#Override
public TableCell<Status, String> call(
TableColumn<Status, String> param) {
TableCell<Status, String> cell = new TableCell<>();
Text text = new Text();
cell.setGraphic(text);
cell.setPrefHeight(Control.USE_COMPUTED_SIZE);
text.wrappingWidthProperty().bind(cell.widthProperty());
text.textProperty().bind(cell.itemProperty());
return cell ;
}
});
In 2.2 this displays the wrong height when you add new items to the table, then on resize the cells are sized correctly. In 8 it's almost perfect, just seems to fail after the first item is added (at least in my mock-up).
As noted in the comments,
textCol.setCellFactory(tc -> {
TableCell<Status, String> cell = new TableCell<>();
Text text = new Text();
cell.setGraphic(text);
cell.setPrefHeight(Control.USE_COMPUTED_SIZE);
text.wrappingWidthProperty().bind(textCol.widthProperty());
text.textProperty().bind(cell.itemProperty());
return cell ;
});
appears to work better.
Just add cell factory on each table of column.
It should add before adding your data to table view.
It is worked fine for me.
yourTableColumn.setCellFactory(param -> {
return new TableCell<YourDataClass, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setStyle("");
} else {
Text text = new Text(item);
text.setStyle("-fx-text-alignment:justify;");
text.wrappingWidthProperty().bind(getTableColumn().widthProperty().subtract(35));
setGraphic(text);
}
}
};
});

Context Menu visibility in TableView JavaFX

I am creating TableView in JavaFX. In which I want to show Context Menu in right click of mouse in tableView. So I am adding an EventHandler on table as given below :
TableView tableView=new TableView();
EventHandler event = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
if (me.getButton() == MouseButton.SECONDARY) {
tableView.getContextMenu().show(tableView, me.getSceneX(), me.getSceneY());
}
}
};
tableView.addEventHandler(MouseEvent.MOUSE_CLICKED, event);
But my problem is that Context Menu is visible wherever I right click on any part of table.
I want to do that Context Menu should be only visible if I clicked on any rows in TableView.
i.e. How would I get row number in TableView at specific point, So that my Context Menu should be only visible, if I clicked on any row of TableView.
The best solution I found was to check if the y coordinate is outside of the bounds of the column header and then to explicitly show the menu.
ContextMenu visibleMenu = null;
tableView.setOnMouseClicked((MouseEvent e) -> {
if (visibleMenu !=null) {
visibleMenu.hide();
visibleMenu = null;
}
if (e.getButton()==MouseButton.SECONDARY) {
double columnHeaderHeight = tableView.lookup(".column-header-background").getBoundsInLocal().getHeight();
if (e.getY()>columnHeaderHeight) {
visibleMenu = getContextMenu(); // build on the fly or use a prebuild menu
visibleMenu.show(tableView, e.getScreenX(), e.getScreenY());
} else {
// you could show a header specific context menu here
}
}
});
The added benefit is that you can build the context menu on the fly with context sensitive items (that for example only appear if a certain type of cell is selected), or just reuse a prebuild contextmenu as setContextMenu does, up to you.
Add context menu to the specific cells using CellFactory not to the whole table.
E.g. using Table from Oracle tutorial:
TableColumn firstNameCol = new TableColumn();
firstNameCol.setText("First");
firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
firstNameCol.setCellFactory(new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(final TableColumn param) {
final TableCell cell = new TableCell() {
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
if (isEditing()) {
setText(null);
} else {
setText(getItem().toString());
setGraphic(null);
}
}
}
};
// This way I will have context menu only for specific column
cell.setContextMenu(ContextMenuBuilder.create().items(MenuItemBuilder.create().text("menu").build()).build());
return cell;
}
});
may be the older question. There is a solution, like getting the target of the mouse event of the table and check for instance for class TableCellSkin and display the context menu as,
table.addEventHandler(MouseEvent.MOUSE_CLICKED,
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
if (e.getButton() == MouseButton.SECONDARY
&& !isRowEmpty) {
EventTarget target = e.getTarget();
if (target instanceof TableCellSkin
|| ((Node) target).getParent() instanceof TableCellSkin) {
// do your stuff. Context menu will be displayed by default
} else {
// hide the context menu when click event is outside table row
table.getContextMenu().hide();
}
}
}
});
#FXML
void tableContextMenuRequested(ContextMenuEvent event) {
if (tableview.getSelectionModel().getSelectedItems().size() == 0) {
tableContextMenu.hide();
}
}

JavaFX tableview with colorpicker editor

I have a TableView that uses a ColorPicker to (display/edit) colors in a cell.
The table display the ColorPicker in the desired field, but edits aren't working.
TableColumn<SeriesPreferences, Color> c2 = new TableColumn<SeriesPreferences, Color>("Color");
c2.setCellValueFactory(new PropertyValueFactory<SeriesPreferences, Color>("color"));
c2.setCellFactory(new Callback<TableColumn<SeriesPreferences, Color>,
TableCell<SeriesPreferences, Color>>()
{
#Override
public TableCell<SeriesPreferences, Color>
call(final TableColumn<SeriesPreferences, Color> param)
{
TableCell<SeriesPreferences, Color> cell =
new TableCell<SeriesPreferences, Color>()
{
#Override
public void updateItem(Color c, boolean empty)
{
if(c != null)
{
final ColorPicker cp = new ColorPicker();
cp.setValue(c);
setGraphic(cp);
cp.setOnAction(new EventHandler<javafx.event.ActionEvent>()
{
public void
handle(javafx.event.ActionEvent t)
{
getTableView().edit(getTableRow().getIndex(), param);
commitEdit(cp.getValue());
}
});
}
}
};
return cell;
}
});
c2.setOnEditCommit(new EventHandler<CellEditEvent<SeriesPreferences, Color>>()
{
#Override
public void handle(CellEditEvent<SeriesPreferences, Color> t)
{
((SeriesPreferences) t.getTableView().getItems().get(t.getTablePosition().
getRow())).setColor(t.getNewValue());
}
});
The edit event handler isn't being called when i change the color in the color picker, any ideas?
There's no need to access the JavaFX POJO (or JavaFX Bean) directly if its properties are correctly bound to the table and also it isn't necessary to call anything other than commitEdit.
The answer from Max Beikirch is misleading, because it causes the color picker (and with it the color) to disappear when the table is not in edit mode. It's a workaround to put the table into edit mode, but a bad one. So do this before showing the color picker popup when click on the button:
Write your cell with a color picker like this:
public class ColorTableCell<T> extends TableCell<T, Color> {
private final ColorPicker colorPicker;
public ColorTableCell(TableColumn<T, Color> column) {
this.colorPicker = new ColorPicker();
this.colorPicker.editableProperty().bind(column.editableProperty());
this.colorPicker.disableProperty().bind(column.editableProperty().not());
this.colorPicker.setOnShowing(event -> {
final TableView<T> tableView = getTableView();
tableView.getSelectionModel().select(getTableRow().getIndex());
tableView.edit(tableView.getSelectionModel().getSelectedIndex(), column);
});
this.colorPicker.valueProperty().addListener((observable, oldValue, newValue) -> {
if(isEditing()) {
commitEdit(newValue);
}
});
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
protected void updateItem(Color item, boolean empty) {
super.updateItem(item, empty);
setText(null);
if(empty) {
setGraphic(null);
} else {
this.colorPicker.setValue(item);
this.setGraphic(this.colorPicker);
}
}
}
If you're on Java 7, replace the lambdas with anonymous inner classes, but it should work as well. Full blog post is here.
I had the same problem for CheckBoxTableCell and DatePickerTableCell and ColorPickerTableCells :-(
I deal it like that: on the events of the controls I get back the POJO objects in use by the "((Inputs)getTableView().getItems().get(getTableRow().getIndex()" and I update similary like is it done in the OnEditCommit method...
So for me it's look like this (update the color):
((Inputs) getTableView().getItems().get(
getTableRow().getIndex())
).setColor(cp.getValue());
Here is example with ColorPickerCell
:
public class ColorPickerTableCell<Inputs> extends TableCell<Inputs, Color>{
private ColorPicker cp;
public ColorPickerTableCell(){
cp = new ColorPicker();
cp.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
commitEdit(cp.getValue());
updateItem(cp.getValue(), isEmpty());
((Inputs) getTableView().getItems().get(
getTableRow().getIndex())
).setColor(cp.getValue());
}
});
setGraphic(cp);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setEditable(true);
}
#Override
protected void updateItem(Color item, boolean empty) {
super.updateItem(item, empty);
cp.setVisible(!empty);
this.setItem(item);
cp.setValue(item);
}
}
With this simple JavaFX's POJO:
public ObjectProperty<Color> color = new SimpleObjectProperty<Color>();
this.color = new SimpleObjectProperty(color);
public ObjectProperty<Color> colorProperty() {
return color;
}
public void setColor(Color color2) {
color.set(color2);
}
I do not know if it's a good way to achive that but it worked for me... Note that the JavaFX's POJO is only accessible within an "ActionEvent" request (combobox, datepicker, colorpicker, etc..)
Regards,
Well, I investigated that topic a bit as I have had the same problem. I am afraid to say that JavaFX is just unusable.
I took a look at how others implemented their cells and the key was that were all using something that is representable by a string.
Now, it's the way it always is with Java: Do it the Java-way or be left alone in the rain. The docs for JavaFX are extremely bad at the moment, so I had to try until it works.
So: To trigger the editCommit-event, you have to call setContentDisplay(ContentDisplay. TEXT_ONLY) in updateItem(). That works well if want to display your data as string, but fails completely in cases like these, where a colorpicker just does the job.
Alternatively, it might be possible to fire the event manually. But how do you get the table-position? I don't know.
It like Michael Simons said in the comment on the OP. You need to be in edit mode. When creating your own custom cells you can trigger edit mode manually by calling startEdit(); from inside the TableCell.
for example using the focusProperty of your control:
cp.focusedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
startEdit();
}
});

Categories

Resources