I have a TableView with a on column contains a article and an other column with a price. I would like to change the row's css of a index row when I click in a button.
I have this :
articleTable.setRowFactory(param -> new TableRow<LigneTicket>() {
#Override
protected void updateItem(LigneTicket paramT, boolean empty) {
super.updateItem(paramT, empty);
if (!isEmpty() && paramT != null && paramT.getArticle().isArticleAnnuler()) {
getStyleClass().add("articleCanceled");
articleTable.refresh();
}
}
});
But this code work on change of my tableView, not just on the click in a button, and it's not working in a index row choose.
Help please,
Thanks
Try use one hack after your event:
for ( Column col : articleTable.getColumns() ) {
col.setVisible( false );
col.setVisible( true );
}
Note that there is not a 1:1 relation between table items and table rows.
TableRows only exist for visible items and may be reassigned a value. You therefore need to take care properly removing the style class.
Furthermore instead of using a style class to mark the rows, I recommend using a pseudoclass, which allows easier adding/removing.
You could store the data about the state of the item in the item class itself or store it in a suitable external datastructre.
The following example adds/removes the removed pseudoclass to/from table rows when the value associated with the item in a ObservableMap changes.
The buttons allow assigning or clearing the pseudoclass from the selected rows.
You could do a similar thing with indices instead of items too.
#Override
public void start(Stage primaryStage) {
TableView<Item> tableView = new TableView<>(FXCollections.observableArrayList(
new Item("a"),
new Item("b"),
new Item("c"),
new Item("d"),
new Item("e"),
new Item("f")
));
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
ObservableMap<Item, Boolean> removed = FXCollections.observableHashMap();
PseudoClass removedPseudoClass = PseudoClass.getPseudoClass("removed");
tableView.setRowFactory(tv -> {
TableRow<Item> result = new TableRow<>();
ObjectBinding<Boolean> binding = Bindings.valueAt(removed, result.itemProperty());
binding.addListener((observable, oldValue, newValue) -> result.pseudoClassStateChanged(removedPseudoClass, newValue != null && newValue));
return result;
});
TableColumn<Item, String> column = new TableColumn<>("value");
column.setCellValueFactory(td -> td.getValue().valueProperty());
tableView.getColumns().add(column);
Button btn = new Button("remove");
Button btn2 = new Button("add");
btn.setOnAction(evt -> {
for (Item item : tableView.getSelectionModel().getSelectedItems()) {
removed.put(item, Boolean.TRUE);
}
});
btn2.setOnAction(evt -> {
for (Item item : tableView.getSelectionModel().getSelectedItems()) {
removed.remove(item);
}
});
Scene scene = new Scene(new VBox(10, tableView, btn, btn2));
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
public class Item {
public Item() {
}
public Item(String value) {
this.value.set(value);
}
private final StringProperty value = new SimpleStringProperty();
public String getValue() {
return value.get();
}
public void setValue(String val) {
value.set(val);
}
public StringProperty valueProperty() {
return value;
}
}
style.css
.table-row-cell:filled {
-fx-background-color: lime;
}
.table-row-cell:filled:selected {
-fx-background-color: -fx-selection-bar;
}
.table-row-cell:filled:removed {
-fx-background-color: orange;
}
.table-row-cell:filled:removed:selected {
-fx-background-color: -fx-selection-bar;
}
Related
I have a problem with JavaFx.
I have a table view, which shows the Data I want.
Now I change the Data in the TableView.
My changeListener works, so far so good.
Now I want the rows of the table with changes in it to appear in a different color.
But I just can't find a way to get the specific row.
I tried to google the solution but all I can find is how to get a selected row.
But there won't be any user input. The data just refreshes.
Can you help me?
Probably I was just to stupid to find the right keywords.
I think of something like:
tableview.getRow(indexOfChangedRow).setStyle
The TableRow maintains a property called Item, which holds the data for the row. You need a RowFactory that binds the background to some value in your Table data. Generally, your Table data is going to have fields composed of ObservableValues, like Properties. So you end up with a Property (the Table data Model), which is composed of Properties. This means that you'll need a ChangeListener to manually reset the Binding on the Background property of the TableRow whenever the Item property is changed.
Here's an example:
public class TableBackground extends Application {
private ObservableList<TableModel> tableData = FXCollections.observableArrayList();
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(createContent()));
primaryStage.show();
}
private Parent createContent() {
BorderPane borderPane = new BorderPane();
borderPane.setCenter(createTable());
borderPane.setBottom(createAddBox());
return borderPane;
}
private Node createTable() {
TableView<TableModel> tableView = new TableView<>();
TableColumn<TableModel, String> firstColumn = new TableColumn<>("Field 1");
firstColumn.setCellValueFactory(p -> p.getValue().field1Property());
tableView.getColumns().add(firstColumn);
TableColumn<TableModel, String> secondColumn = new TableColumn<>("Field 2");
secondColumn.setCellValueFactory(p -> p.getValue().field2Property());
tableView.getColumns().add(secondColumn);
tableView.setItems(tableData);
Background redBackground = new Background(new BackgroundFill(Color.RED, null, null));
Background blueBackground = new Background(new BackgroundFill(Color.CORNFLOWERBLUE, null, null));
tableView.setRowFactory(t -> {
TableRow<TableModel> row = new TableRow<TableModel>();
row.itemProperty().addListener((ob, oldValue, newValue) -> {
row.backgroundProperty().bind(Bindings.createObjectBinding(() -> newValue.isNewRow() ? redBackground : blueBackground,
newValue.newRowProperty()));
});
return row;
});
return tableView;
}
private Node createAddBox() {
TextField textField1 = new TextField();
TextField textField2 = new TextField();
Button button = new Button("Add Row");
button.setOnAction(evt -> {
tableData.forEach(dataItem -> dataItem.setNewRow(false));
tableData.add(new TableModel(textField1.getText(), textField2.getText()));
});
return new HBox(10, textField1, textField2, button);
}
}
And the TableModel would look like this:
public class TableModel {
private StringProperty field1 = new SimpleStringProperty("");
private StringProperty field2 = new SimpleStringProperty("");
private BooleanProperty newRow = new SimpleBooleanProperty(true);
public TableModel(String field1Data, String field2Data) {
field1.set(field1Data);
field2.set(field2Data);
setNewRow(true);
}
public String getField1() {
return field1.get();
}
public StringProperty field1Property() {
return field1;
}
public void setField1(String field1) {
this.field1.set(field1);
}
public String getField2() {
return field2.get();
}
public StringProperty field2Property() {
return field2;
}
public void setField2(String field2) {
this.field2.set(field2);
}
public boolean isNewRow() {
return newRow.get();
}
public BooleanProperty newRowProperty() {
return newRow;
}
public void setNewRow(boolean newRow) {
this.newRow.set(newRow);
}
}
The important part is the RowFactory:
Background redBackground = new Background(new BackgroundFill(Color.RED, null, null));
Background blueBackground = new Background(new BackgroundFill(Color.CORNFLOWERBLUE, null, null));
tableView.setRowFactory(t -> {
TableRow<TableModel> row = new TableRow<TableModel>();
row.itemProperty().addListener((ob, oldValue, newValue) -> {
row.backgroundProperty().bind(Bindings.createObjectBinding(() -> newValue.isNewRow() ? redBackground : blueBackground,
newValue.newRowProperty()));
});
return row;
});
It's a plain vanilla row except that it has a ChangeListener on row.itemProperty() which puts a Binding on row.backgroundProperty() based on value of new items TableModel.newRowProperty(). If it's new, then the background is red, otherwise it's blue.
The Button OnAction event, sets all of the existing rows newRowProperty() to false, and then adds a new row with newRowProperty() set to true. This means that all of the existing rows will turn blue, and the new row will be red.
how to do the Check-Box enable disable in a Table-Column In JavaFX if i select one check-box i need to disable the remaining check-Boxes .
if i select one check-box i need to disable the remaining check-Boxes in the table view, in JavaFx.
colSelect.setCellFactory(new Callback<TableColumn<LogVoiceBroadCast, Boolean>, TableCell<LogVoiceBroadCast, Boolean>>()
{
#Override
public TableCell<LogVoiceBroadCast, Boolean> call(TableColumn<LogVoiceBroadCast, Boolean> param) {
final TableCell<LogVoiceBroadCast, Boolean> cell = new TableCell<LogVoiceBroadCast, Boolean>() {
#Override
public void updateItem(Boolean value, boolean empty) {
super.updateItem(value, empty);
if (!empty || value != null) {
CheckBox checkBox = new CheckBox();
checkBox.setSelected(false);
checkBox.addEventHandler(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (!checkBox.isSelected()) {
LogVoiceBroadCast item = (LogVoiceBroadCast) getTableRow().getItem();
voiceFiles.add(item);
} else {
LogVoiceBroadCast item = (LogVoiceBroadCast) getTableRow().getItem();
voiceFiles.remove(item);
}
}
});
final HBox hbox = new HBox(checkBox);
hbox.setAlignment(Pos.CENTER);
hbox.setSpacing(12);
setGraphic(hbox);
} else {
setGraphic(null);
}
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
You shouldn't use a for input events to trigger listen to changes in the CheckBox state. Instead add a listener to the CheckBox.selected property.
Furthermore you need to implement a way of restoring the CheckBox state, since different TableCells may be used for the "checked row". Use some observable data structure to store this kind of information and use a listener to update the state of the CheckBoxes:
TableView<Integer> table = new TableView<>();
for (int i = 0; i < 100; i++) {
table.getItems().add(i);
}
TableColumn<Integer, Number> column = new TableColumn<>();
column.setCellValueFactory(cd -> new SimpleIntegerProperty(cd.getValue()));
// property to store the checked row item or null, if no row is checked
final ObjectProperty<Integer> selectedValue = new SimpleObjectProperty<>();
TableColumn<Integer, Void> checkBoxColumn = new TableColumn<>();
checkBoxColumn.setCellFactory(col -> new TableCell<Integer, Void>() {
private final CheckBox checkBox = new CheckBox();
private final ChangeListener<Boolean> selectionListener = (o, oldValue, newValue) -> {
if (newValue) {
// use the row item as selected item, when checkbox is selected
selectedValue.set((Integer) getTableRow().getItem());
} else {
// clear selected item, when checkbox is unselected
selectedValue.set(null);
}
};
private final ChangeListener<Integer> listener = (o, oldValue, newValue) -> {
if (!isEmpty()) {
// update state without triggering the listener
checkBox.selectedProperty().removeListener(selectionListener);
if (newValue == null) {
checkBox.setSelected(false);
checkBox.setDisable(false);
} else {
boolean match = newValue.equals(getTableView().getItems().get(getIndex()));
checkBox.setSelected(match);
checkBox.setDisable(!match);
}
checkBox.selectedProperty().addListener(selectionListener);
}
};
{
// listener should not prevent garbage collection of cell
selectedValue.addListener(new WeakChangeListener<>(listener));
checkBox.selectedProperty().addListener(selectionListener);
setAlignment(Pos.CENTER);
}
#Override
protected void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(checkBox);
listener.changed(null, null, selectedValue.get());
}
}
});
table.getColumns().addAll(column, checkBoxColumn);
I want to add checkBox to tableCell but i dont know what to do to make checkBox visible when i use .setCellFactory
I want to achive this with cellFactory function ---> checkBoxVisible
when i want to make use of cellFactory checkBox are not visible
----> wrong effect
window1.setCellFactory(new BooleanColorCellFactory());
window2.setCellFactory(new BooleanColorCellFactory());
This is BooleanColorCellFactory Class
#Override
public TableCell<Server, CheckBox> call(TableColumn<Server, CheckBox> param) {
return new TableCell<Server, CheckBox>(){
#Override
protected void updateItem(CheckBox item, boolean empty) {
super.updateItem(item, empty);
// if(!empty) {
// setVisible(true);
// setEditable(true);
// getChildren().add(item);
// setText(item.toString());
// if(item.isSelected())
// setStyle(" -fx-background-color: Green");
// else
// setStyle(" -fx-background-color: RED");
}
// }
};
}
}
I have tried some things but nothing was working.
What i need to add in BooleanColorCellFactory to make this work ?
UPDATE:
So i was playing around and i manage to get step closer to solution by adding this into BooleanColorCellFactory Class
if(!getChildren().contains(item))
getChildren().add(item);
but it is buggy and dont looks well and its added after i start scrolling(which is weird behavior for me)--> click
You shouldn't put a Node inside the item class unless you really need to. Furthermore never access the children of a Control directly unless you're writing a Skin for this Control.
You should instead add a BooleanProperty to the Server class:
private final BooleanProperty window1 = new SimpleBooleanProperty();
public boolean isWindow1() {
return window1.get();
}
public void setWindow1(boolean value) {
window1.set(value);
}
public BooleanProperty window1Property() {
return window1;
}
TableColumn<Server, Boolean> window1;
Callback<TableColumn<Server, Boolean>, TableCell<Server, Boolean>> factory = new BooleanColorCellFactory();
// cellValueFactory returns property
window1.setCellValueFactory(new PropertyValueFactory<>("window1"));
window1.setCellFactory(factory);
...
window2.setCellFactory(factory);
#Override
public TableCell<Server, Boolean> call(TableColumn<Server, Boolean> param) {
return new TableCell<Server, Boolean>(){
private final CheckBox checkBox = new CheckBox();
{
checkBox.selectedProperty().addListener((o, oldValue, newValue) -> {
// save new value in row item
WritableValue<Boolean> property = (WritableValue<Boolean>) getTableColumn().getCellObservableValue​(getIndex());
property.setValue(newValue);
});
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
setStyle(null);
} else {
setGraphic(checkBox);
checkBox.setSelected(item);
setStyle(item ? "-fx-background-color: Green" : "-fx-background-color: RED");
}
}
};
}
Procede accordingly for window2
As the title states, I'm trying to enable/disable a button within a table row based upon a boolean within that table row's data. Here's my code so far:
col.setCellFactory(new Callback<TableColumn<ExampleRow, String>, TableCell<ExampleRow, String>>() {
#Override
public TableCell call(final TableColumn<ExampleRow, String> param){
final Button btn = new Button("Save");
final TableCell<ExampleRow, String> cell = new TableCell<ExampleRow, String>(){
#Override
public void updateItem(String item, boolean empty){
super.updateItem(item, empty);
if(empty){
setGraphic(null);
setText(null);
} else {
btn.setPrefWidth(col.getWidth());
btn.setPadding(Insets.EMPTY);
btn.setOnAction(event -> {
});
setGraphic(btn);
setText(null);
}
}
};
ExampleRow row = (ExampleRow)cell.getTableRow().getItem(); //NPE here
btn.setDisable(!row.hasChanged());
return cell;
}
});
Unfortunately my code breaks on the fifth from the bottom line. If I exclude that line and change the line below to btn.setDisable(true) it works wonderfully. What can I do to disable this button based upon the data in which the button resides?
You aren't using the item anyways, so you could just make it a Boolean and use the value of the changed property. This allows you to enable/disable the button in the updateItem method:
Example:
public static class Item {
private final BooleanProperty changed = new SimpleBooleanProperty();
public final boolean isChanged() {
return this.changed.get();
}
public final void setChanged(boolean value) {
this.changed.set(value);
}
public final BooleanProperty changedProperty() {
return this.changed;
}
}
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView();
table.getItems().addAll(new Item(), new Item(), new Item());
TableColumn<Item, Boolean> column = new TableColumn<>();
column.setCellValueFactory(cd -> cd.getValue().changedProperty());
column.setCellFactory(col -> new TableCell<Item, Boolean>() {
final Button btn = new Button("Save");
{
btn.setOnAction(evt -> {
Item item = (Item) getTableRow().getItem();
item.setChanged(false);
});
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
btn.setDisable(!item);
setGraphic(btn);
}
}
});
table.getColumns().add(column);
Button btn = new Button("change");
btn.setOnAction((ActionEvent event) -> {
Item item = table.getSelectionModel().getSelectedItem();
if (item != null) {
item.setChanged(true);
}
});
VBox root = new VBox(btn, table);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
BTW: TableView uses the cellFactory to create the cells. The item, table and tableRow properties are updated later. Therefore retrieving any of those values in the cellFactory's call method itself makes no sense, since none of those values have been assigned at that time.
So I have code that works if the user selects a different row than the one currently selected
table.getSelectionModel().selectedItemProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue == null) {
updateDetails(oldValue);
return;
}
updateDetails(newValue);
});
}
However, I want this to work if the user clicks on the same value as well - basically, there's a part of the code that modifies an image shown but that image doesn't update itself unless I click on another row then go back to the row I was previously on. I would like to be able to update the row I'm on simply by clicking on it (which would call updateDetails) but can't seem to figure this out...
Create a custom rowFactory and add a mouse listener to it.
Example
This displays the old value property of the last item clicked and the new item clicked as text of the Label.
TableView<Item> tv = new TableView<>(FXCollections.observableArrayList(new Item("foo"), new Item("bar"), new Item("42")));
Label label = new Label();
TableColumn<Item, String> valueColumn = new TableColumn<>("value");
valueColumn.setCellValueFactory(d -> d.getValue().valueProperty());
tv.getColumns().add(valueColumn);
EventHandler<MouseEvent> eventHandler = new EventHandler<MouseEvent>() {
private Item lastItem;
#Override
public void handle(MouseEvent event) {
if (event.getButton() == MouseButton.PRIMARY) {
TableRow<Item> source = (TableRow<Item>) event.getSource();
if (!source.isEmpty()) {
label.setText(MessageFormat.format("old: {0}; new: {1}", lastItem == null ? null : lastItem.getValue(), (lastItem = source.getItem()).getValue()));
}
}
}
};
tv.setRowFactory(t -> {
TableRow<Item> row = new TableRow();
row.setOnMouseClicked(eventHandler);
return row;
});
public class Item {
public Item() {
}
public Item(String value) {
this.value.set(value);
}
private final StringProperty value = new SimpleStringProperty();
public String getValue() {
return value.get();
}
public void setValue(String val) {
value.set(val);
}
public StringProperty valueProperty() {
return value;
}
}