The setStyle
method will set an inline style on a Node
; this style is in the form of a CSS rule. This is what you do with the bold case:
if (d.getAction().equals(Donation.NEW_DONATION)) {
setStyle("-fx-font-weight: bold;");
}
To add a CSS class to the list of classes for a node, get the list of the node's CSS classes with getStyleClass()
, and manipulate it.
You have to be a little careful here, as the list can contain multiple copies of the same value, and additionally you have no control over how many times updateItem()
is called and with which Donation
s as a parameter. The best option is to remove all instances of the class delete-row
and add one back in under the correct conditions:
@Override
public void updateItem(Donation d, boolean empty) {
super.updateItem(d, empty) ;
getStyleClass().removeAll(Collections.singleton("delete-row"));
if (d == null) {
setStyle("");
} else if (d.getAction().equals(Donation.DELETE_DONATION)) {
setStyle("");
getStyleClass().add("delete-row");
} else if (d.getAction().equals(Donation.NEW_DONATION)) {
setStyle("-fx-font-weight: bold;");
} else {
setStyle("");
}
}
Another option is to use a CSS pseudoclass instead:
@Override
public void updateItem(Donation d, boolean empty) {
super.updateItem(d, empty) ;
PseudoClass delete = PseudoClass.getPseudoClass("delete-row");
pseudoClassStateChanged(delete, d != null && d.getAction().equals(Donation.DELETE_DONATION));
if (d != null && d.getAction().equals(Donation.NEW_DONATION)) {
setStyle("-fx-font-weight: bold;");
} else {
setStyle("");
}
}
with
.table-row-cell:delete-row .text {
-fx-strikethrough: true;
}
I would probably refactor the NEW_DONATION
style as a pseudoclass as well in this scenario, for consistency.
Here's a complete example using pseudoclasses. Note that I changed the CSS for bold (as I understand it, using font-weight
depends on the system having a bold font for the currently-selected font; using something generic (sans-serif
) with a -fx-font
rule is more robust.)
Donation.java
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Donation {
public enum Action { NEW_DONATION, DELETE_DONATION, NO_ACTION }
private final StringProperty name = new SimpleStringProperty() ;
private final ObjectProperty<Action> action = new SimpleObjectProperty<>() ;
public Donation(String name, Action action) {
setName(name);
setAction(action);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
public final ObjectProperty<Action> actionProperty() {
return this.action;
}
public final Action getAction() {
return this.actionProperty().get();
}
public final void setAction(final Action action) {
this.actionProperty().set(action);
}
}
App.java
import java.util.Random;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage stage) {
TableView<Donation> table = new TableView<>();
table.setRowFactory(tv -> {
TableRow<Donation> row = new TableRow<>() {
@Override
protected void updateItem(Donation donation, boolean empty) {
super.updateItem(donation, empty);
PseudoClass add = PseudoClass.getPseudoClass("add-row");
pseudoClassStateChanged(add,
donation != null && donation.getAction() == Donation.Action.NEW_DONATION);
PseudoClass delete = PseudoClass.getPseudoClass("delete-row");
pseudoClassStateChanged(delete,
donation != null && donation.getAction() == Donation.Action.DELETE_DONATION);
}
};
return row ;
});
Random rng = new Random();
for (int i = 1 ; i <= 40 ; i++) {
table.getItems().add(new Donation("Donation "+i, Donation.Action.values()[rng.nextInt(3)]));
}
table.getColumns().add(column("Donation", Donation::nameProperty));
table.getColumns().add(column("Action", Donation::actionProperty));
BorderPane root = new BorderPane(table);
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
private static <S,T> TableColumn<S,T> column(String name, Function<S, Property<T>> prop) {
TableColumn<S,T> col = new TableColumn<>(name);
col.setCellValueFactory(data -> prop.apply(data.getValue()));
return col ;
}
public static void main(String[] args) {
launch();
}
}
style.css:
.table-row-cell:delete-row .text {
-fx-strikethrough: true;
}
.table-row-cell:add-row {
/* -fx-font-weight: bold; */
-fx-font: bold 1em sans-serif ;
}

Update:
If the property determining the style of the table row is not being observed by one of the columns (e.g. in the above example, the "action" column is not present), you need to arrange for the row to observe that property itself. This is a little tricky, as the row is reused for different table items, so you need to add and remove the listener from the correct property when that happens. This looks like:
table.setRowFactory(tv -> {
TableRow<Donation> row = new TableRow<>() {
// Listener that updates style when the actionProperty() changes
private final ChangeListener<Donation.Action> listener =
(obs, oldAction, newAction) -> updateStyle();
{
// make sure listener above is registered
// with the correct actionProperty()
itemProperty().addListener((obs, oldDonation, newDonation) -> {
if (oldDonation != null) {
oldDonation.actionProperty().removeListener(listener);
}
if (newDonation != null) {
newDonation.actionProperty().addListener(listener);
}
});
}
@Override
protected void updateItem(Donation donation, boolean empty) {
super.updateItem(donation, empty);
updateStyle();
}
private void updateStyle() {
Donation donation = getItem();
PseudoClass add = PseudoClass.getPseudoClass("add-row");
pseudoClassStateChanged(add, donation != null && donation.getAction() == Donation.Action.NEW_DONATION);
PseudoClass delete = PseudoClass.getPseudoClass("delete-row");
pseudoClassStateChanged(delete, donation != null && donation.getAction() == Donation.Action.DELETE_DONATION);
}
};
return row ;
});