TableView modify style per cell only [duplicate] - javafx

This question already has an answer here:
How to get row value inside updateItem() of CellFactory
(1 answer)
Closed 4 years ago.
Assume a:
TableView<ResultType, String> table = new TableView<> ();
table.setItems(myItems);
In TableColumn we have the setCellValueFactory method which very nicely gives you access to the ResultType object of the respective cell value. So you can use it to extract the values like that:
aColumnFromTableView.setCellValueFactory(data -> new SimpleStringProperty(data.getValue));
Now each cell from aColumnFromTableView will be populated with a value from all ResultType objects which are set as items for the table.
The question is: can we also change the cell's style in a similar way? I had a look at the setCellFactory method, but it does not seem as friendly as setCellValueFactory though (= it does not provide me the respective ResultType).
Here's what you can do with setCellFactory:
aColumnFromTableView.setCellValueFactory(data -> ???? ); // data is actually aColumnFromTableView itself??
So I am wondering of a way to set the cell style individually similar to what I described with "setCellValueFactory". I hope it exists.
Note: I also tried
aColumnFromTableView.setCellValueFactory(data -> {
aColumnFromTableView.setStyle("my style");
return new SimpleStringProperty(data.getValue);
})
But that sets it for the entire column and not individually.
Thanks!!!!

If you want to customize the style of the TableCell based on the value of the cell you'll need to use a cellFactory and return your own TableCell.
For instance, if you wanted a TableCell<?, Double> that displayed the number in red if it was negative you could do:
column.setCellFactory(col -> new TableCell<>() {
#Override
protected void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setText(item.toString());
if (item < 0.0) {
setTextFill(Color.RED); // or use setStyle(String)
} else {
setTextFill(Color.BLACK); // or use setStyle(String)
}
}
}
});
When creating a custom TableCell you'll more than likely want to override the updateItem(Object,boolean) method. It's important you override it correctly, however, if you want it to work right. Read the javadoc for information:
The updateItem method should not be called by developers, but it is the best method for developers to override to allow for them to customise the visuals of the cell. To clarify, developers should never call this method in their code (they should leave it up to the UI control, such as the ListView control) to call this method. However, the purpose of having the updateItem method is so that developers, when specifying custom cell factories (again, like the ListView cell factory), the updateItem method can be overridden to allow for complete customisation of the cell.
It is very important that subclasses of Cell override the updateItem method properly, as failure to do so will lead to issues such as blank cells or cells with unexpected content appearing within them. Here is an example of how to properly override the updateItem method:
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setText(item.toString());
}
}
Note in this code sample two important points:
We call the super.updateItem(T, boolean) method. If this is not done, the item and empty properties are not correctly set, and you are likely to end up with graphical issues.
We test for the empty condition, and if true, we set the text and graphic properties to null. If we do not do this, it is almost guaranteed that end users will see graphical artifacts in cells unexpectedly.
Instead of setting properties or calling setStyle you could use things like PseudoClass states to make it easier to style from an external CSS stylesheet.
import javafx.css.PseudoClass;
import javafx.scene.control.TableCell;
public class CustomCell<S> extends TableCell<S, Double> {
private static final PseudoClass POSITIVE = PseudoClass.getPseudoClass("positive");
private static final PseudoClass NEGATIVE = PseudoClass.getPseudoClass("negative");
public CustomCell() {
getStyleClass().add("custom-cell");
}
#Override
protected void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
pseudoClassStateChanged(POSITIVE, false);
pseudoClassStateChanged(NEGATIVE, false);
} else {
setText(item.toString()); // you might want to format the number for display
pseudoClassStateChanged(POSITIVE, item >= 0.0);
pseudoClassStateChanged(NEGATIVE, item < 0.0);
}
}
}
Then use:
column.setCellFactory(col -> new CustomCell<>());
And in a stylesheet:
.custom-cell:positive {
-fx-text-fill: black;
}
.custom-cell:negative {
-fx-text-fill: red;
}

Related

JavaFX TreeTableView set custom node in row cell

I want to create a treetableview which can show for every tree item a sub tree with a row which contains custom nodes (in this case a button).
In the end i want to make it look like that.
In order to achieve that i probably need a custom row factory.
//set custom row factory
treeTableView.setRowFactory(new Callback<TreeTableView<Struct>, TreeTableRow<Struct>>() {
#Override
public TreeTableRow<Struct> call(TreeTableView<Struct> param) {
return new TreeTableRow<Struct>(){
#Override
protected void updateItem(Struct item, boolean empty) {
super.updateItem(item, empty);
//check if row has values
if (!empty) {
//override the row cell graphics if boolean is set
if (item.isCustomRow()) {
setGraphic(new StackPane(new Button("Button")));
}
} else {
setGraphic(null);
}
}
};
}
});
My idea was to override the updateItem() method and use the setGraphic() method to display the button. To indicate whether the tree item shows my own graphic node or the default, i added a boolean to my tableview structure class (Struct).
//Struct contains treetable data
class Struct {
...
//the boolean defines if the row graphics should be overridden
Boolean customRow;
...
public Boolean isCustomRow() {
return customRow;
}}
The result which i got can you see here. I looks like the empty column cells inside the row blocks my own node.
I suspect that i also need a custom TreeTableRowSkin but i didnt find any examples how to implement this. Is there anyone who can help me to solve this?
The full code example can be found here.

Removing JavaFX TreeCell formating added with PseudoClass

I am using a PseudoClass to change the formating of a JavaFx TreeCell depending on a condition given by the data in the tree cell. The following code works:
public class EmfTreeCellImpl extends TreeCell<EmfTreeNode> {
PseudoClass AMBIGUOUS_FEATURE = PseudoClass.getPseudoClass("ambiguous-feature");
public EmfTreeCellImpl() {
}
#Override
protected void updateItem(EmfTreeNode item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
return;
}
setText(item.getLabel());
setEditable(false);
if (item instanceof EmfTreeNode.SingleAttributeNode) {
EmfTreeNode.SingleAttributeNode san = (EmfTreeNode.SingleAttributeNode) item;
//pseudoClassStateChanged(AMBIGUOUS_FEATURE, san.isAmbiguous());
pseudoClassStateChanged(AMBIGUOUS_FEATURE, san.isAmbiguous());
}
}
}
The cells get highlighted correctly. The issue is that the highlight is "persisted" in the specific tree cell in the GUI. That is, if I collapse or expand other tree branches the highlight stays "in place" highlighting other cells that do not meet the condition. I thought the setGraphic(null) would remove the formatting.
To solve this I added an extra line to the updateItem to clear the formatting:
if (empty || item == null) {
setText(null);
setGraphic(null);
pseudoClassStateChanged(AMBIGUOUS_FEATURE, false);
return;
}
But somehow it feels I should only be either removing the format from the ones that have been formatted or using a more "global" clear.
As stated in the comments, the solution proposed in the answer itself is perfectly valid, however a more concise way of handling it may be making use of the built-in filled pseudo-class of Cell which is applied automatically to any non-empty cell:
:filled:ambiguous-feature {
/* css-styling of non-empty ambiguous cells */
}

JavaFX : color individual TreeItems using css

I want to be able to color individual Tree Item of treeView based on some condition.
This answer seems good but I am unable to implement it.
https://stackoverflow.com/a/10931896/6653207
I am unable to understand how to use setCellFactory method to format individual TreeItems.
I have a class
public class Bag {
public String caption,assignment="";
Boolean eval;
public Set<Vertex> Nodes = new HashSet<Vertex>();
public Vector<Bag> ChildBags = new Vector<Bag>();
#Override
public String toString()
{
return assignment+ " " +caption;
}
}
Here's my css file:
.true{
-fx-text-fill:#33cc00 ;
}
.assignment{
-fx-text-fill: #0033cc
}
So I want to color to green the caption ( the toString() method returns ) of all those nodes whose eval property is true.
and assignment string which toString() method returns for all the nodes should be blue.
How can I do so?
Thanks.
By overriding the updateItem method of the TreeCell, you can adjust the TreeCell's properties based on the value of the TreeItem the cell contains.
In the following example a pseudoclass is assigned to all cells that contain a value with the prefix "child" and all empty cells get a black background.
TreeView<String> treeView = ...
PseudoClass childPseudoClass = PseudoClass.getPseudoClass("child");
treeView.setCellFactory(tv -> new TreeCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
// update for empty cell / cell containing null
pseudoClassStateChanged(childPseudoClass, false);
setText("");
setStyle("-fx-background-color: black;");
} else {
// update for filled cell
pseudoClassStateChanged(childPseudoClass, item.startsWith("child"));
setText(item);
setStyle(null);
}
}
});
CSS Stylesheet
.tree-cell:child {
-fx-background-color: red;
}
The updateItem method is called by the TreeView every time the value changes, e.g. if a new TreeItem is associated with the cell or the value property of a TreeItem is modified.
You could also use the factory to add listeners to the TreeCell, before it's returned, in case you prefer this and e.g. want to change the cell based on the treeItem property.
EDIT: To apply different colors to the text, you need to use different Nodes for the text parts.
treeView.setCellFactory(tv -> new TreeCell<Bag>() {
private final Text assignment;
private final Text caption;
private final Node graphic;
{
assignment = new Text();
caption = new Text();
assignment.getStyleClass().add("assignment");
graphic = new HBox(4, assignment, caption);
setGraphic(graphic);
}
#Override
protected void updateItem(Bag item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
setGraphic(graphic);
assignment.setText(item.assignment);
caption.setText(item.caption);
caption.getStyleClass().remove("true");
if (item.eval) {
caption.getStyleClass().add("true");
}
}
}
});
To color the text you need to use the -fx-fill property instead of the -fx-text-fill property.

How to add a custom css style for specific item in a combo/choice box in JavaFx?

I am using javaFx, and I want to mimic this control. Specificly the link in the bottom of the menu !!
is it possible to do it with css only or without creating a custom control?
There is no need to implement a custom control, but you do need a cell factory to do this. The basic idea is to add a style class to the cell if it requires it, and remove it if not. So you can do, for example:
final String cellStyleClass = "my-combo-box-cell" ;
ComboBox<String> combo = new ComboBox<>();
combo.setCellFactory(listView -> new ListCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty) ;
getStyleClass().remove(cellStyleClass);
if (empty) {
setText(null);
} else {
setText(item);
if (/* needs style class */) {
getStyleClass().add(cellStyleClass);
}
}
}
});
The test to see if you need to add the style class can obviously reference the item, etc.
Now in your CSS file you can style the cell as you need it:
.my-combo-box-cell {
/* specific styles here... */
}

ChoiceBox inside a TableView's cell only through custom Callback

I have been using a custom cell factory to display a ChoiceBox inside a TableView's TableColumn's cell, i.e.
someColumn.setCellFactory(new DayCellFactory());
with DayCellFactory implementing Callback (with the call and updateItem methods), and the ChoiceBox's (and thus the cell's) value being bound to the value of a property. The ChoiceBox shows the potential values of the enum Day.
class DayCellFactory implements Callback<TableColumn<DayPeriod, Day>, TableCell<DayPeriod, Day>> {
private final static ObservableList<Day> list = FXCollections.observableArrayList();
static {
for (Day day : Day.values()) {
list.add(day);
}
}
#Override
public TableCell<DayPeriod, Day> call (TableColumn<DayPeriod, Day> column) {
return new TableCell<DayPeriod, Day>() {
#Override
public void updateItem (Day item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
ObservableValue<Day> obsVal = getTableColumn().getCellObservableValue(getIndex());
ObjectProperty<Day> objProp = (ObjectProperty<Day>) obsVal;
ChoiceBox<Day> choice = new ChoiceBox<>(list);
choice.valueProperty().bindBidirectional(objProp);
setGraphic(choice);
}
}
};
}
}
I thought I could reduce the code by simply using
someColumn.setCellFactory(ChoiceBoxTableCell.forTableColumn(list));
(with the list defined and filled as required) but the resulting cell looks (and behaves) like a simple label showing the property's toString() value - yes, I did click there, but no ChoiceBox appears. And removing this line makes no difference. Removing the cell value factory results in no value being displayed.
I guess that there is no binding taking place with this simple approach, but this can probably be taken care of as well. It just seems strange that there is no ChoiceBox appearing.

Resources