0

In my JavaFX TableView, I am trying to retrieve TableCells from a selected row to mark them with custom colors. Simply changing the colors of the entire row does not work in this case, as I use different color shadings in each cell depending on the value of each cell

The example below shows two approaches I tried I to solve the problem

1) Use a listener to retrieve cells in the selected row. Printing the row index and content already works However, I could not find how to retrieve a TableCell from table.getSelectionModel().

2) Try a dirty workaround to add the TableCells to a global data structure in the columnCellFactory. However, the TableCells do not get added to the tableCells ArrayList for some reason.

To obtain a short example, the imports and the Classes defining the EditingCell (custom TableCell) and CellEditEvent were omitted.

package TableViewColExample;

public class TableViewExample extends Application {

  private  Callback<TableColumn, TableCell> columnCellFactory ;
  final TableView<String[]> table = new TableView<String[]>();
  ObservableSet<Integer> selectedRowIndexes = FXCollections.observableSet();  
  ObservableSet<String> selectedRows = FXCollections.observableSet();  

  ArrayList<ArrayList<EditingCell>> tableColumns = new         ArrayList<ArrayList<EditingCell>>();

  @Override
  public void start(Stage stage) {
  String[][] dat = new String[][]{  
    {"C1","C2","C3"},{"a","b","c"},{"d","e","f"},{"g","i","h"}};                                                                      

  ObservableList<String []> data = FXCollections.observableArrayList();
  data.addAll(Arrays.asList(dat));
  data.remove(0);
  table.setItems(data); 

  for (int i = 0; i < dat[0].length; i++) {
  TableColumn tc = new TableColumn(dat[0][i]);
  final int colNo = i;

  tc.setCellValueFactory(new Callback<CellDataFeatures<String[], String>, ObservableValue<String>>() {
    public ObservableValue<String> call(CellDataFeatures<String[], String> p) {
           return new SimpleStringProperty((p.getValue()[colNo]));
       }    
   });

   ArrayList<EditingCell> tableCells = new ArrayList<EditingCell>();    

   columnCellFactory =
       new Callback<TableColumn, TableCell>() {
           public TableCell call(TableColumn p) {

             EditingCell tcell = new EditingCell();

             //For some reason, the EditingCell is never added to the list
             tableCells.add(tcell);
             return tcell;
           }
       };

   tc.setCellFactory(columnCellFactory);

   tableColumns.add(tableCells);

   //The printed value here is 0, which means that the Factory does not add the Editing Cell to the List
   System.out.println(" Column rows "+tableCells.size());

   table.getColumns().add(tc); 
   } 

   //Output: TableColumns 3, TableRows 0
   System.out.println("TableColumns "+ tableColumns.size() + " Table rows "+tableColumns.get(0).size());

   table.setItems(data); 
   table.getSelectionModel().getSelectedCells().addListener((Change<? extends TablePosition> change) -> {  
     selectedRows.clear();  
            table.getSelectionModel().getSelectedCells().stream().map(TablePosition::getRow).f  orEach(row -> {        
        selectedRowIndexes.add(row);  
        System.out.println(selectedRowIndexes.toString());
      });  

      table.getSelectionModel().getSelectedItems().forEach(row -> {  
        selectedRows.add(Arrays.toString(row));  
        System.out.println(selectedRows.toString());
      });               
    });  

    stage.setScene(new Scene(table));
    stage.show();
  }

  public static void main(String[] args) {
    launch(args);
  }
}
scs
  • 567
  • 6
  • 22
  • 1
    Don't do it like this. There is (deliberately) not enough implementation information provided about how the cell mechanism works (under what circumstances new cells are created, etc) for you to reliably be able to get references to the "live" cells and update them this way (and you run the risk of memory leaks, as you might hold references to cells that are not referenced anywhere else). You can manage the colors, even dynamically, with a combination of the cell (or maybe row) factories and CSS, but you need to explain more fully what you are trying to achieve. – James_D Nov 09 '15 at 12:36
  • I am trying to change the color shading of cells in a selected rows or columns after selecting the columns and then pressing a button. The new color shading should be calculated from the previous colors in the cells. – scs Nov 10 '15 at 09:23
  • Currently I am using a workaround that feeds color - specific strings to UpdateItem() in a TableCell and stores the previous value of each cell. It is sufficient for my application but not good enough to post as an answer. if (item.equals("---red---")){ String value = this.prevValue; super.updateItem(item, empty); setStyle("-fx-background-color: red"); this.setText(value); this.setItem(value); setGraphic(graphic); Event event = new CellEditEvent(value); this.fireEvent(event); } – scs Nov 10 '15 at 13:07
  • That just sounds like you have the wrong type for your column: i.e. you should be storing actual colors in the properties, instead of `String`s. In general, you need to think about the data first, and get that right, and then the cell presentation as being a view of the data. If you get the data right, the cell factories should more or less write themselves. – James_D Nov 10 '15 at 13:27
  • The idea behind my data structure is to have the data and in addition the selection of marked rows / columns. The color of each cell depends on its value and group selection. Therefore, I would prefer to just store the selection of rows and columns. – scs Nov 10 '15 at 14:22
  • Sorry, I guess I don't really understand what you are trying to do here. It sounds too much as though you are trying to use the view (e.g. the table cells) to access data for the application, which is an anti-pattern. But maybe I don't really follow clearly. Either you need to create a [MCVE] showing what you are doing (your current example doesn't compile) or maybe someone else can answer. But currently this just doesn't make sense to me. – James_D Nov 10 '15 at 15:17
  • See if [this example](https://gist.github.com/james-d/0d4c1ce6b54394834816) helps at all. It updates the selection color of the cells in the selected row according to their value, using only a cell factory and css. – James_D Nov 10 '15 at 15:42
  • In the meantime, I created an example that marks a row when a button is clicked. Unfortunately, the example is at least 4 pages long as it includes a class for overriding the TableCell. I would prefer to wait until I am more proficient in JavaFX before posting an answer. Thank you for your help. – scs Nov 10 '15 at 15:52
  • 1
    [My example](https://gist.github.com/james-d/0d4c1ce6b54394834816) is only ~150 lines (including cell factory), and as far as I can tell does what you are asking for without any hacks with caching table cells. [Examples already exist](http://stackoverflow.com/questions/20350099/programmatically-change-the-tableview-row-appearance/20475137#20475137) of highlighting rows on pressing a button, not sure if yours adds anything to that. – James_D Nov 10 '15 at 15:56

0 Answers0