Forum Stats

  • 3,838,710 Users
  • 2,262,394 Discussions
  • 7,900,739 Comments

Discussions

Callback - TableRow should be striked through after "check" the checkBox of a CheckBoxTableCell

jrmind
jrmind Member Posts: 2
edited Apr 21, 2017 4:55AM in JavaFX 2.0 and Later

Hello,

my situation and idea is as follows. I have a TableView with three columns(Name, Duration, Complete). The complete column contains CheckBoxes. My plan is to strike through the content in the cell "BLA" of column name if complete CheckBox was checked in the same tableRow. I've tried it with a RowFactory, but the problem is: if i klick on the row of the table the call method of the RowFactory triggers. But if I click on the CheckBox the callback of the completionCol triggers. I have no chance to set a new css class to the cell of name.

pastedImage_0.png

Code:

TableColumn<TaskRow, Boolean> completionCol = new TableColumn<>("Complete");
completionCol.setEditable(true);
completionCol.setCellValueFactory(new PropertyValueFactory("complete"));
completionCol.setCellFactory(CheckBoxTableCell.forTableColumn(

   new Callback<Integer, ObservableValue<Boolean>>() {

   @Override
   public ObservableValue<Boolean> call(Integer param) {

   logik to save state of the task from uncomplete to complete...

  return tasksTable.getItems().get(param).completeProperty();
   }

  })

);

TableColumn<TaskRow, String> taskNameCol = new TableColumn<>("Name");
taskNameCol.setCellValueFactory(new PropertyValueFactory("taskName"));

tasksTable.getColumns().setAll(taskNameCol, durationCol, completionCol);

tasksTable.setEditable(true);

I was not able to find a solution by myself. Can someone hlep me out here?

jrmindjsmith

Best Answer

  • bouye-JavaNet
    bouye-JavaNet Member Posts: 394 Silver Badge
    edited Apr 19, 2017 9:10PM Answer ✓

    Some way of doing it: changing the row renderer.

    import java.util.Optional;import java.util.stream.IntStream;import javafx.application.Application;import javafx.application.Platform;import javafx.beans.property.BooleanProperty;import javafx.beans.property.SimpleBooleanProperty;import javafx.beans.property.SimpleStringProperty;import javafx.beans.property.StringProperty;import javafx.beans.value.ChangeListener;import javafx.concurrent.ScheduledService;import javafx.concurrent.Task;import javafx.geometry.Insets;import javafx.scene.Scene;import javafx.scene.control.TableColumn;import javafx.scene.control.TableRow;import javafx.scene.control.TableView;import javafx.scene.control.cell.CheckBoxTableCell;import javafx.scene.control.cell.PropertyValueFactory;import javafx.scene.layout.StackPane;import javafx.scene.paint.Color;import javafx.scene.shape.Line;import javafx.stage.Stage;import javafx.util.Duration;import org.scenicview.ScenicView;public class Main extends Application {    public static final class TaskRow {        private final StringProperty taskName = new SimpleStringProperty();        public final String getTaskName() {            return taskName.get();        }        public final void setTaskName(String value) {            taskName.set(value);        }        public final StringProperty taskNameProperty() {            return taskName;        }        private final BooleanProperty complete = new SimpleBooleanProperty();        public final boolean isComplete() {            return complete.get();        }        public final void setComplete(boolean value) {            complete.set(value);        }        public final BooleanProperty completeProperty() {            return complete;        }    }    @Override    public void start(final Stage primaryStage) throws Exception {        final TableColumn<TaskRow, String> taskNameCol = new TableColumn<>("Name");        final TableColumn<TaskRow, String> durationCol = new TableColumn<>("Duration");        final TableColumn<TaskRow, Boolean> completionCol = new TableColumn<>("Complete");        completionCol.setEditable(true);        completionCol.setCellValueFactory(new PropertyValueFactory("complete"));        completionCol.setCellFactory(CheckBoxTableCell.forTableColumn(completionCol));        taskNameCol.setCellValueFactory(new PropertyValueFactory("taskName"));        final TableView<TaskRow> tasksTable = new TableView<>();        tasksTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);        tasksTable.getColumns()                .setAll(taskNameCol, durationCol, completionCol);        tasksTable.setRowFactory(tableView -> new TableRow<TaskRow>() {            private TaskRow oldItem;            private Line line = new Line();            {                getChildren().add(line);                line.setId("strikeThrough");                line.getStyleClass().add("strike-through");                line.setStroke(Color.GRAY);                line.setStrokeWidth(0.8);                line.setVisible(false);                line.setMouseTransparent(true);            }            @Override            protected void layoutChildren() {                super.layoutChildren();                if (!getChildren().contains(line)) {                    getChildren().add(line);                }                line.toFront();                final double width = getWidth();                final double height = getHeight();                final Insets insets = getInsets();                //                 final double areaX = insets.getLeft();                final double areaY = insets.getTop();                final double areaW = Math.max(0, width - (insets.getLeft() + insets.getRight()));                final double areaH = Math.max(0, height - (insets.getTop() + insets.getBottom()));                //                final double x1 = areaX;                final double y1 = areaY + areaH / 2;                final double x2 = areaX + areaW;                final double y2 = y1;                line.setStartX(x1);                line.setStartY(y1);                line.setEndX(x2);                line.setEndY(y2);            }            @Override            protected void updateItem(final TaskRow item, boolean empty) {                Optional.ofNullable(oldItem).ifPresent(this::uninstallItem);                if (!empty) {                    Optional.ofNullable(item).ifPresent(this::installItem);                    oldItem = item;                }            }            private final ChangeListener<Boolean> completeListener = (observable, oldValue, newValue) -> {                installStyle(newValue);            };            private void installStyle(boolean completed) {                final String style = completed ? "-fx-background-color: red;" : null;                setStyle(style);                line.setVisible(completed);            }            private void uninstallItem(final TaskRow item) {                item.completeProperty().removeListener(completeListener);                installStyle(false);            }            private void installItem(final TaskRow item) {                item.completeProperty().addListener(completeListener);                installStyle(item.isComplete());            }        });        tasksTable.setEditable(true);        final StackPane root = new StackPane(tasksTable);        final Scene scene = new Scene(root);        primaryStage.setScene(scene);        primaryStage.show();        final int maxTaskNumber = 10;        final int taskNumber = (int) Math.max(1, maxTaskNumber * Math.random());        final TaskRow[] model = IntStream.range(0, taskNumber)                .mapToObj(index -> {                    final String taskName = String.format("Task %d", index + 1);                    final TaskRow task = new TaskRow();                    task.setTaskName(taskName);                    return task;                })                .toArray(TaskRow[]::new);        tasksTable.getItems().setAll(model);        service = new ScheduledService<Void>() {            @Override            protected Task<Void> createTask() {                return new Task<Void>() {                    @Override                    protected Void call() throws Exception {                        Platform.runLater(() -> {                            final int index = (int) (taskNumber * Math.random());                            final TaskRow task = model[index];                            task.setComplete(!task.isComplete());                        });                        return null;                    }                };            }        };        service.setDelay(Duration.seconds(5));        service.setPeriod(Duration.seconds(2));        service.start();        ScenicView.show(scene);    }    private ScheduledService<Void> service;}

    You can also create special cell renderer for each column that use the same principle and either insert a line (as I did) or try to use the -fx-strikethrough selector (this does not work using the row render but might work with a particular cell renderer).

    you may also attempt to attach a CSS pseudoclass to the row and achieve the strikethrough through CSS something like

    .table-row:complete {  -fx-background-color: red;}.table-row:complete .text {  -fx-strikethrough: true;}
    jrmindjrmindjsmith

Answers

  • bouye-JavaNet
    bouye-JavaNet Member Posts: 394 Silver Badge
    edited Apr 19, 2017 9:10PM Answer ✓

    Some way of doing it: changing the row renderer.

    import java.util.Optional;import java.util.stream.IntStream;import javafx.application.Application;import javafx.application.Platform;import javafx.beans.property.BooleanProperty;import javafx.beans.property.SimpleBooleanProperty;import javafx.beans.property.SimpleStringProperty;import javafx.beans.property.StringProperty;import javafx.beans.value.ChangeListener;import javafx.concurrent.ScheduledService;import javafx.concurrent.Task;import javafx.geometry.Insets;import javafx.scene.Scene;import javafx.scene.control.TableColumn;import javafx.scene.control.TableRow;import javafx.scene.control.TableView;import javafx.scene.control.cell.CheckBoxTableCell;import javafx.scene.control.cell.PropertyValueFactory;import javafx.scene.layout.StackPane;import javafx.scene.paint.Color;import javafx.scene.shape.Line;import javafx.stage.Stage;import javafx.util.Duration;import org.scenicview.ScenicView;public class Main extends Application {    public static final class TaskRow {        private final StringProperty taskName = new SimpleStringProperty();        public final String getTaskName() {            return taskName.get();        }        public final void setTaskName(String value) {            taskName.set(value);        }        public final StringProperty taskNameProperty() {            return taskName;        }        private final BooleanProperty complete = new SimpleBooleanProperty();        public final boolean isComplete() {            return complete.get();        }        public final void setComplete(boolean value) {            complete.set(value);        }        public final BooleanProperty completeProperty() {            return complete;        }    }    @Override    public void start(final Stage primaryStage) throws Exception {        final TableColumn<TaskRow, String> taskNameCol = new TableColumn<>("Name");        final TableColumn<TaskRow, String> durationCol = new TableColumn<>("Duration");        final TableColumn<TaskRow, Boolean> completionCol = new TableColumn<>("Complete");        completionCol.setEditable(true);        completionCol.setCellValueFactory(new PropertyValueFactory("complete"));        completionCol.setCellFactory(CheckBoxTableCell.forTableColumn(completionCol));        taskNameCol.setCellValueFactory(new PropertyValueFactory("taskName"));        final TableView<TaskRow> tasksTable = new TableView<>();        tasksTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);        tasksTable.getColumns()                .setAll(taskNameCol, durationCol, completionCol);        tasksTable.setRowFactory(tableView -> new TableRow<TaskRow>() {            private TaskRow oldItem;            private Line line = new Line();            {                getChildren().add(line);                line.setId("strikeThrough");                line.getStyleClass().add("strike-through");                line.setStroke(Color.GRAY);                line.setStrokeWidth(0.8);                line.setVisible(false);                line.setMouseTransparent(true);            }            @Override            protected void layoutChildren() {                super.layoutChildren();                if (!getChildren().contains(line)) {                    getChildren().add(line);                }                line.toFront();                final double width = getWidth();                final double height = getHeight();                final Insets insets = getInsets();                //                 final double areaX = insets.getLeft();                final double areaY = insets.getTop();                final double areaW = Math.max(0, width - (insets.getLeft() + insets.getRight()));                final double areaH = Math.max(0, height - (insets.getTop() + insets.getBottom()));                //                final double x1 = areaX;                final double y1 = areaY + areaH / 2;                final double x2 = areaX + areaW;                final double y2 = y1;                line.setStartX(x1);                line.setStartY(y1);                line.setEndX(x2);                line.setEndY(y2);            }            @Override            protected void updateItem(final TaskRow item, boolean empty) {                Optional.ofNullable(oldItem).ifPresent(this::uninstallItem);                if (!empty) {                    Optional.ofNullable(item).ifPresent(this::installItem);                    oldItem = item;                }            }            private final ChangeListener<Boolean> completeListener = (observable, oldValue, newValue) -> {                installStyle(newValue);            };            private void installStyle(boolean completed) {                final String style = completed ? "-fx-background-color: red;" : null;                setStyle(style);                line.setVisible(completed);            }            private void uninstallItem(final TaskRow item) {                item.completeProperty().removeListener(completeListener);                installStyle(false);            }            private void installItem(final TaskRow item) {                item.completeProperty().addListener(completeListener);                installStyle(item.isComplete());            }        });        tasksTable.setEditable(true);        final StackPane root = new StackPane(tasksTable);        final Scene scene = new Scene(root);        primaryStage.setScene(scene);        primaryStage.show();        final int maxTaskNumber = 10;        final int taskNumber = (int) Math.max(1, maxTaskNumber * Math.random());        final TaskRow[] model = IntStream.range(0, taskNumber)                .mapToObj(index -> {                    final String taskName = String.format("Task %d", index + 1);                    final TaskRow task = new TaskRow();                    task.setTaskName(taskName);                    return task;                })                .toArray(TaskRow[]::new);        tasksTable.getItems().setAll(model);        service = new ScheduledService<Void>() {            @Override            protected Task<Void> createTask() {                return new Task<Void>() {                    @Override                    protected Void call() throws Exception {                        Platform.runLater(() -> {                            final int index = (int) (taskNumber * Math.random());                            final TaskRow task = model[index];                            task.setComplete(!task.isComplete());                        });                        return null;                    }                };            }        };        service.setDelay(Duration.seconds(5));        service.setPeriod(Duration.seconds(2));        service.start();        ScenicView.show(scene);    }    private ScheduledService<Void> service;}

    You can also create special cell renderer for each column that use the same principle and either insert a line (as I did) or try to use the -fx-strikethrough selector (this does not work using the row render but might work with a particular cell renderer).

    you may also attempt to attach a CSS pseudoclass to the row and achieve the strikethrough through CSS something like

    .table-row:complete {  -fx-background-color: red;}.table-row:complete .text {  -fx-strikethrough: true;}
    jrmindjrmindjsmith
  • jrmind
    jrmind Member Posts: 2
    edited Apr 21, 2017 4:51AM

    @bouye-JavaNet, thanks alot! The answer is very helpfull! I could implement it in my application and it works fine. I was never thinking about the approach with a Line. I have some problems to understand the Cell and RowFactories and how to customize them correctly. Need more praxis in javaFX.

This discussion has been closed.