TreeTableView : setting a row not editable - java

I want to have control over the styling of some rows of a TreeTableView based on the level in the tree. I used setRowFactory and apply a styling if this row is part of the first level children of the root of the Table. The styling works fine, but I also want to disable clicking on the checkbox for those rows. I am able to setDisable(true) but that also disables the expanding of the TreeItem and SetEditable(false) does not seem to have any effect.
EDIT: What I understand is that the Table must be set editable, then the columns are by default editable. But if I set TreeTableRow.setEditable(true); or TreeTableRow.setEditable(false); I never see any effect. The description seems of setEditable seems exactly what I want but I am unable to use it that way.
void javafx.scene.control.Cell.setEditable(boolean arg0)
setEditable
public final void setEditable(boolean value)
Allows for certain cells to not be able to be edited. This is useful incases >where, say, a List has 'header rows' - it does not make sense forthe header rows >to be editable, so they should have editable set tofalse.
Parameters:value - A boolean representing whether the cell is editable or not.If >true, the cell is editable, and if it is false, the cell can notbe edited.
Main:
public class TreeTableViewRowStyle extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
// create the treeTableView and colums
TreeTableView<Person> ttv = new TreeTableView<Person>();
TreeTableColumn<Person, String> colName = new TreeTableColumn<>("Name");
TreeTableColumn<Person, Boolean> colSelected = new TreeTableColumn<>("Selected");
colName.setPrefWidth(100);
ttv.getColumns().add(colName);
ttv.getColumns().add(colSelected);
ttv.setShowRoot(false);
ttv.setEditable(true);
// set the columns
colName.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
colSelected.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(colSelected));
colSelected.setCellValueFactory(new TreeItemPropertyValueFactory<>("selected"));
ttv.setRowFactory(table-> {
return new TreeTableRow<Person>(){
#Override
public void updateItem(Person pers, boolean empty) {
super.updateItem(pers, empty);
boolean isTopLevel = table.getRoot().getChildren().contains(treeItemProperty().get());
if (!isEmpty()) {
if(isTopLevel){
setStyle("-fx-background-color:lightgrey;");
setEditable(false); //THIS DOES NOT SEEM TO WORK AS I WANT
//setDisable(true); //this would disable the checkbox but also the expanding of the tree
}else{
setStyle("-fx-background-color:white;");
}
}
}
};
});
// creating treeItems to populate the treetableview
TreeItem<Person> rootTreeItem = new TreeItem<Person>();
TreeItem<Person> parent1 = new TreeItem<Person>(new Person("Parent 1"));
TreeItem<Person> parent2 = new TreeItem<Person>(new Person("Parent 1"));
parent1.getChildren().add(new TreeItem<Person>(new Person("Child 1")));
parent2.getChildren().add(new TreeItem<Person>(new Person("Child 2")));
rootTreeItem.getChildren().addAll(parent1,parent2);
ttv.setRoot(rootTreeItem);
// build and show the window
Group root = new Group();
root.getChildren().add(ttv);
stage.setScene(new Scene(root, 300, 300));
stage.show();
}
}
Model Person :
public class Person {
private StringProperty name;
private BooleanProperty selected;
public Person(String name) {
this.name = new SimpleStringProperty(name);
selected = new SimpleBooleanProperty(false);
}
public StringProperty nameProperty() {
return name;
}
public BooleanProperty selectedProperty() {
return selected;
}
public void setName(String name){
this.name.set(name);
}
public void setSelected(boolean selected){
this.selected.set(selected);
}
}

The base problem is that none of the editable (nor the pseudo-editable like CheckBoxXX) Tree/Table cells respect the editability of the row they are contained in. Which I consider a bug.
To overcome, you have to extend the (pseudo) editable cells and make them respect the row's editable. The exact implementation is different for pseudo- vs. real editing cells. Below are in-line examples, for frequent usage you would make them top-level and re-use.
CheckBoxTreeTableCell: subclass and override updateItem to re-bind its disabled property like
colSelected.setCellFactory(c -> {
TreeTableCell cell = new CheckBoxTreeTableCell() {
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (getGraphic() != null) {
getGraphic().disableProperty().bind(Bindings
.not(
getTreeTableView().editableProperty()
.and(getTableColumn().editableProperty())
.and(editableProperty())
.and(getTreeTableRow().editableProperty())
));
}
}
};
return cell;
});
For a real editing cell, f.i. TextFieldTreeTableCell: override startEdit and return without calling super if the row isn't editable
colName.setCellFactory(c -> {
TreeTableCell cell = new TextFieldTreeTableCell() {
#Override
public void startEdit() {
if (getTreeTableRow() != null && !getTreeTableRow().isEditable()) return;
super.startEdit();
}
};
return cell;
});
Now you can toggle the row's editability as you do, changed the logic a bit to guarantee full cleanup in all cases:
ttv.setRowFactory(table-> {
return new TreeTableRow<Person>(){
#Override
public void updateItem(Person pers, boolean empty) {
super.updateItem(pers, empty);
// tbd: check for nulls!
boolean isTopLevel = table.getRoot().getChildren().contains(treeItemProperty().get());
if (!isEmpty() && isTopLevel) {
// if(isTopLevel){
setStyle("-fx-background-color:lightgrey;");
setEditable(false);
}else{
setEditable(true);
setStyle("-fx-background-color:white;");
}
}
};
});

Instead of creating a custom TreeTableCell subclass you can use the following utility method that basically installs a new cell-factory on a column that delegates to the original cell-factory but adds the row-editability binding whenever a cell is created.
public <S, T> void bindCellToRowEditability(TreeTableColumn<S, T> treeTableColumn) {
// Keep a handle on the original cell-factory.
Callback<TreeTableColumn<S, T>, TreeTableCell<S, T>> callback = treeTableColumn.getCellFactory();
// Install a new cell-factory that performs the delegation.
treeTableColumn.setCellFactory(column -> {
TreeTableCell<S, T> cell = callback.call(column);
// Add a listener so that we pick up when a new row is set for the cell.
cell.tableRowProperty().addListener((observable, oldRow, newRow) -> {
// If the new row is non-null, we proceed.
if (newRow != null) {
// We get the cell and row editable-properties.
BooleanProperty cellEditableProperty = cell.editableProperty();
BooleanProperty rowEditableProperty = newRow.editableProperty();
// Bind the cell's editable-property with its row's property.
cellEditableProperty.bind(rowEditableProperty);
}
});
return cell;
});
}
You can then set this for all columns of your TreeTableView as:
List<TreeTableColumn<S, ?>> columns = treeTableView.getColumns();
columns.forEach(this::bindCellToRowEditability);
You still need the custom TreeTableRow that checks whether it is top-level or not so that the editable value is correctly set for the row itself. However, setting the editable value on the row will now ensure that all cells in that row correctly reflects the row's editable-property.

If you want disable a specific Cell then handle the disable logic in the CellFactory rather than in RowFactory. The static method forTreeTableColumn(..) is a convinient method for quick use. But that is not the only way. You can still create your own factory for CheckBoxTreeTableCell.
So instead of
colSelected.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(colSelected));
set the cellfactory as below, and this should work for you.
colSelected.setCellFactory(new Callback<TreeTableColumn<Person, Boolean>, TreeTableCell<Person, Boolean>>() {
#Override
public TreeTableCell<Person, Boolean> call(TreeTableColumn<Person, Boolean> column) {
return new CheckBoxTreeTableCell<Person, Boolean>() {
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
boolean isTopLevel = column.getTreeTableView().getRoot().getChildren().contains(getTreeTableRow().getTreeItem());
setEditable(!isTopLevel);
}
};
}
});

Related

Create a listener logic for a custom combobox

I am trying to create a listener logic for a custom combo box that I have created that contains items with check boxes.
I was not able to proceed as I am not getting an idea on how to do it.
MainApplication.java
public class MainApplication extends Application{
#Override
public void start(Stage stage) {
Scene scene = new Scene(new VBox(), 450, 250);
ComboBox<ComboBoxItemWrap<Person>> cb = new ComboBox<>();
#SuppressWarnings("unchecked")
ObservableList<ComboBoxItemWrap<Person>> options = FXCollections.observableArrayList(
new ComboBoxItemWrap<>(new Person("A", "12-Aug-1994")),
new ComboBoxItemWrap<>(new Person("B", "13-Aug-1994")),
new ComboBoxItemWrap<>(new Person("C", "14-Aug-1994"))
);
cb.setCellFactory( c -> {
ListCell<ComboBoxItemWrap<Person>> cell = new ListCell<ComboBoxItemWrap<Person>>(){
#Override
protected void updateItem(ComboBoxItemWrap<Person> item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
final CheckBox cb = new CheckBox(item.toString());
cb.selectedProperty().bind(item.checkProperty());
setGraphic(cb);
}
}
};
cell.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {
cell.getItem().checkProperty().set(!cell.getItem().checkProperty().get());
StringBuilder sb = new StringBuilder();
cb.getItems().filtered( f-> f!=null).filtered( f-> f.getCheck()).forEach( p -> {
sb.append("; "+p.getItem());
});
final String string = sb.toString();
cb.setPromptText(string.substring(Integer.min(2, string.length())));
});
return cell;
});
cb.setItems(options);
VBox root = (VBox) scene.getRoot();
Button bt = new Button("test");
bt.setOnAction(event -> {
cb.getItems().filtered( f -> f.getCheck()).forEach( item -> System.out.println(item.getItem()));
});
root.getChildren().addAll(cb, bt);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
ComboBoxItemWrap.java
public class ComboBoxItemWrap<T> {
private BooleanProperty check = new SimpleBooleanProperty(false);
private ObjectProperty<T> item = new SimpleObjectProperty<>();
ComboBoxItemWrap() {
}
ComboBoxItemWrap(T item) {
this.item.set(item);
}
ComboBoxItemWrap(T item, Boolean check) {
this.item.set(item);
this.check.set(check);
}
public BooleanProperty checkProperty() {
return check;
}
public Boolean getCheck() {
return check.getValue();
}
public void setCheck(Boolean value) {
check.set(value);
}
public ObjectProperty<T> itemProperty() {
return item;
}
public T getItem() {
return item.getValue();
}
public void setItem(T value) {
item.setValue(value);
}
#Override
public String toString() {
return item.getValue().toString();
}
}
Person.java
public class Person {
private StringProperty name = new SimpleStringProperty();
private StringProperty birthday = new SimpleStringProperty();
public Person() {
}
public Person(String name, String birthday) {
setNameValue(name);
setBirthdayValue(birthday);
}
public StringProperty getNameProperty() {
return name;
}
public String getNameValue() {
return name.getValue();
}
public void setNameValue(String value) {
name.setValue(value);
}
public StringProperty getBirthdayProperty() {
return birthday;
}
public String getBirthdayValue() {
return birthday.getValue();
}
public void setBirthdayValue(String value) {
birthday.setValue(value);
}
#Override
public String toString() {
return getNameValue()+" ("+getBirthdayValue()+")";
}
}
In the output application, a list of items with check boxes will get populated. On selection of any number of entries in the list, the entry name gets populated on the combo box itself separated by a ';'. Now I want my back end code to listen and identify the entries that have been selected in order to perform further operations.
You may not need to reinvent the wheel. Consider using ControlsFX CheckComboBox.
That being said there are several problems in the code:
You never update the property on a selection of the CheckBox. This can be easily fixed by using bidirectional bindings.
Since the ComboBox popup is closed, the CheckBox is no longer armed at the time the MOUSE_RELEASED event is triggered. this is a prerequesite for the selected state of the CheckBox changing though. Modifying the skin allows you to change this behaviour.
You use ObservableList.filtered to create FilteredLists that you throw away immediately afterwards. You also create a filtered list of a filtered list in the MOUSE_RELEASED event filter. This is not wrong per se, but you're creating an expensive object there without the need to do so: simply get a stream there. This is a much more lightweight way to filter a list, if the result is only needed once. Use filtered/FilteredList only if you need an ObservableList that contains elements from another ObservableList and that is automatically updated.
Also note that there is a way to make an ObservableList trigger update changes on a change of a property: Use the observableArrayList method taking an extractor as parameter.
This is how you could rewrite your code to make it work:
VBox root = new VBox();
Scene scene = new Scene(root, 450, 250);
ComboBox<ComboBoxItemWrap<Person>> cb = new ComboBox<>();
ObservableList<ComboBoxItemWrap<Person>> options = FXCollections.observableArrayList(item -> new Observable[] {item.checkProperty()});
options.addAll(
new ComboBoxItemWrap<>(new Person("A", "12-Aug-1994")),
new ComboBoxItemWrap<>(new Person("B", "13-Aug-1994")),
new ComboBoxItemWrap<>(new Person("C", "14-Aug-1994")));
cb.setCellFactory(c -> new ListCell<ComboBoxItemWrap<Person>>() {
private final CheckBox cb = new CheckBox();
#Override
protected void updateItem(ComboBoxItemWrap<Person> item, boolean empty) {
ComboBoxItemWrap<Person> oldItem = getItem();
if (oldItem != null) {
// remove old binding
cb.selectedProperty().unbindBidirectional(oldItem.checkProperty());
}
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
cb.selectedProperty().bindBidirectional(item.checkProperty());
cb.setText(item.toString());
setGraphic(cb);
}
}
});
// make sure popup remains open
ComboBoxListViewSkin<ComboBoxItemWrap<Person>> skin = new ComboBoxListViewSkin<>(cb);
skin.setHideOnClick(false);
cb.setSkin(skin);
cb.setItems(options);
cb.promptTextProperty().bind(Bindings.createStringBinding(() ->
options.stream().filter(ComboBoxItemWrap::getCheck).map(Object::toString).collect(Collectors.joining("; ")), options));
Note that if you want the popup to be closed after (de)selecting a checkbox, you could simply add a event filter for MOUSE_RELEASED for the checkbox that calls cb.arm() instead of modifying the skin.

How can I add a checkbox to a table that reads and writes the object property that it represents in JavaFX

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

Multiple scene nodes in PropertySheet Editor JavaFX

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

Why super.updateItem(item, empty) Called in listview.cellFacoty method in javafx?

Code:
mpcListView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> param){
return new XCell();
}
});
public class XCell extends ListCell<String>{
HBox hbox = new HBox();
Label label = new Label();
Button button = new Button("",getImage());
String lastItem;
public XCell(){
super();
hbox.setSpacing(120);
hbox.getChildren().addAll(label,button);
button.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event){
mpcListView.getItems().remove(lastItem);
}
});
}
#Override
protected void updateItem(String item, boolean empty){
super.updateItem(item, empty);
if (empty){
lastItem = null;
setGraphic(null);
}else{
lastItem = item;
label.setText(item);
setGraphic(hbox);
}
}
}
Why super.updateItem(item, empty) is called?
ListCell updateItem(...) implementation is really important as it calls Cell updateItem(...) implementation which checks if it is empty or not and calls the correct method: setItem(...) or setEmpty(...).
If you don't call super.updateItem(...) then Cell.updateItem(...) is not called and setItem(...) is not called then... nothing is drawn or the value is not updated! ListCell just adds some check before using Cell updateItem implementation, so you get two choices:
You can call super.updateItem(...) in your custom ListCell implementation
You can call setItem(...) and setEmpty(...) in your updateItem implementation and "by-pass" ListCell implementation on checks and edits stuff
Note that ListCell it is not the "base" implementation used by a ListView. Refer to the class TextFieldListCell instead, it is a great example of how it actually works, get the sources from mercurial and read, it is always the best way.
For instance, TextFieldListCell uses super.updateItem(...) to call Cell.updateItem implementation to check if it is empty or not (by using setItem or setEmpty) and then uses javafx.scene.control.cell.CellUtils.updateItem(...). This method gets the item setted in the current cell and uses after that a converter on the item to show a String in a Label.
The default implementation of ListCell does some standard housekeeping. For example, it registers a mouse listener that updates the ListView's selection model appropriately.
The super.updateItem(...) call invokes the superclass implementation of the method. If you omit it, selection won't work, and probably a bunch of other functionality won't work either.
In your code, the lastItem field is redundant. There's already an item property defined in the ListCell class: another job the default implementation of updateItem(...) does is to update this property. So you can omit that field and just call getItem() when you need to get the item.
It's pretty easy to forget to call super.updateItem(...). For this reason I often use an alternative approach:
mpcListView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> param){
return createXCell();
}
});
public ListCell<String> createXCell() {
final ListCell<String> cell = new ListCell<String>();
final HBox hbox = new HBox();
final Label label = new Label();
final Button button = new Button("",getImage());
hbox.setSpacing(120);
hbox.getChildren().addAll(label,button);
button.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event){
mpcListView.getItems().remove(cell.getItem());
}
});
cell.itemProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> obs, String oldValue, String newValue) {
if (newValue == null) {
cell.setText(null);
cell.setGraphic(null);
} else {
cell.setText(newValue);
cell.setGraphic(hbox);
}
}
});
return cell ;
}
Note that I don't subclass ListCell at all here: I just use the default implementation and update the text and graphic using a listener on the itemProperty. This is more in the spirit of "Prefer aggregation over inheritance".

Unable to show the Tool Tip on the JavaFX TableView cell value

In my JavaFX TableView I have one TableColumn on which I have set Cell Factory to render ProgressBar and for other TableColumns I have set Cell Factory to show ToolTip. Like the image below. Second Column is showing Progress Bar and other 3 Columns are render to show Tool tip, that has simple string values to show.
I was getting issue in which the TableView was not displaying/showing updated values in the table i.e UI is not validating/refreshing/painting the TableView elements. If I clicked on ColumnHeader to sort any column then only I can see the TableView updating. Manually sort the table column to refresh the table content is not making sense so I have searched and found solution to show/hide the Table Columns for updating the Table View.
To resolved the issue I have written a code below to solve the TableView Updating/Refreshing issue but due to this code now ToolTip are not getting visible.
Code to Update Table View after each specific interval
class TableProgressBarUpdator implements Runnable {
TableView table;
public TableProgressBarUpdator(TableView fxtable) {
table = fxtable;
}
public void start() {
new Thread(this).start();
}
public void run() {
while (keepUpdating) {
try {
updateProgressbar();
Thread.sleep(1000);
} catch (Exception e) {
LogHandler.doErrorLogging("Error while updating tables cell", e);
}
}
LogHandler.doDebugLogging("Table process repainting is completed.");
}
private void updateProgressbar() throws Exception {
Platform.runLater(new Runnable() {
#Override
public void run() {
((TableColumn) table.getColumns().get(0)).setVisible(false);
((TableColumn) table.getColumns().get(0)).setVisible(true);
}
});
}
}
Start Updating Table View
public void startUpdatingTableProgress() {
keepUpdating = true;
TableProgressBarUpdator tpu = new TableProgressBarUpdator(table);
tpu.start();
}
Stop Updating Table View
public void stopUpdatingTableProgress() {
keepUpdating = false;
}
Adding more code that is showing render classes to show Progress bar and display Tool Tip.
Code to show the Progress Bar Table View.
public static class ProgressBarTableCell<S, T> extends TableCell<S, T> {
private final ProgressBar progressBar;
private ObservableValue<T> ov;
public ProgressBarTableCell() {
this.progressBar = new ProgressBar();
progressBar.setPrefHeight(23);
setAlignment(Pos.CENTER);
}
#Override
public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
setGraphic(null);
setText(null);
} else {
if (item.toString().equalsIgnoreCase("Processing")) {
Platform.runLater(new Runnable() {
#Override
public void run() {
if (getGraphic() == null) {
setGraphic(progressBar);
progressBar.setProgress(-1);
} else {
ProgressBar objpProgressBar = (ProgressBar) getGraphic();
objpProgressBar.setProgress(-1);
}
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
});
} else {
Platform.runLater(new Runnable() {
#Override
public void run() {
if (getGraphic() == null) {
setGraphic(progressBar);
progressBar.setProgress(0);
} else {
ProgressBar objpProgressBar = (ProgressBar) getGraphic();
objpProgressBar.setProgress(0);
}
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
});
}
}
}
}
Code to Show the Tool Tip
public class ToolTip extends TableCell {
#Override
protected void updateItem(Object object, boolean selected) {
if (object == null) {
setGraphic(null);
setText(null);
}else{
setText(object.toString());
setTooltip(new Tooltip(object.toString()));
}
}
}
Issue -
If I comment-out these two lines from TableProgressBarUpdator Class then I am able to see Tool Tip for each cell values in 1st, 3rd and 4th column but now Table View contents are not updating/refreshing and when I UN-comment these lines I am unable to see the Tool Tip.
((TableColumn) table.getColumns().get(0)).setVisible(false);
((TableColumn) table.getColumns().get(0)).setVisible(true);
In all due to these two lines my Tool Tip Render is not working and If I remove these two lines then Table View Content are not Refreshing/Updating.
You don't need to update TableView manually. may be there are problem in your class associated with that TableView's column.
You have to create class as given below :
public static class Test{
private StringProperty name;
private Test() {
name = new SimpleStringProperty();
}
public Test(String name) {
this.name = new SimpleStringProperty(name);
}
public void setName(String name) {
this.name.set(name);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
}
Are you sure you need the Platform.runLater() call within your ProgressBarTableCell? I would expect it to already be in the Application thread. That could cause the progress bar update to be placed at the end of the queue in the Application thread, after the scheduled table update.
Is the value for your TableCell wrapped in an ObservableProperty (looks like you should have an SimpleStringProperty)? If you did, the table should recognize that it needs a refresh, and you shouldn't have to resort to toggling the column visibility as a hack to force table refreshing.

Categories