JavaFX, how to freeze the position of some columns in TableView
Asked Answered
D

5

13

The idea is: on a TableView of N columns to have the first M columns always visible even when you use the horizontal scroller.

The only thing near my requirement is this Binding two tableviews together such that they scroll in sync. The idea to put side by side two tables is not the best by my point of view because

1) The sort of the column is partially indipendent between the two tables: if you use the same observableList the rows are sorted in both tables but it is not possible the sort on multiple columns where at least one column is not on the same table
2) There is no syncronous scroll with the mouse wheel or with the arrows keys

I know that, probably, I can cope with problems like these using EventHandlers and Listeners but I am hoping it is possible to use only one table.

So, the question is: are there any configurable properties on TableView or TableColumns to have the behaviour I am looking for?

Dragrope answered 17/7, 2013 at 6:56 Comment(0)
M
2

So, the question is: are there any configurable properties on TableView or TableColumns to have the behaviour I am looking for?

Apparently not, but there is a feature request for this behavior on the JavaFX Jira (you'll need to register to view it):

https://javafx-jira.kenai.com/browse/RT-19454

I suggest you vote for it. :^)

Monoplegia answered 28/8, 2013 at 20:36 Comment(0)
M
4

I am able to freeze columns in javafx table view. We need to create our custom table column class where in we will have two methods setFixed and isFixed which will be used to make some column as fixed.

Apart from this you need to create your own

  1. TableViewskin

  2. TableHeaderRow - basically in this class you need to override the getRootHeader() method

  3. NestedTableColumnHeader - In this class override layoutChildren() method and add new method to layout the fixedColumns
  4. VirtualFlow
  5. TableView - override createDefaultSkin() , add new booleanProperty showColumnHeaderand one ObservableArrayList for fixedTableColumn
  6. TableRow - Override createDefaultSkin()
  7. TableRowSkinBase - override layoutChildren() method to handle fixed columns.
Madi answered 30/5, 2016 at 6:24 Comment(0)
U
3

There is currently no such feature. In fact, even manipulating the scroll bar and responding to scrollbar events are problematic. Two options I can think of:

Option #1 - Multiple Table

Create a new layout which contains the two tables and two scroll bars like in the FXML snippet below:

<BorderPane prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
  <bottom>
    <ScrollBar fx:id="hScroll" />
  </bottom>
  <center>
    <HBox fx:id="varTable" prefHeight="100.0" prefWidth="200.0">
      <children>
        <TableView fx:id="fixedTable" prefHeight="200.0" prefWidth="200.0">
          <columns>
            <TableColumn prefWidth="75.0" text="Column X" />
            <TableColumn prefWidth="75.0" text="Column X" />
          </columns>
        </TableView>
        <TableView prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS">
          <columns>
            <TableColumn prefWidth="75.0" text="Column X" />
            <TableColumn prefWidth="75.0" text="Column X" />
          </columns>
        </TableView>
      </children>
    </HBox>
  </center>
  <right>
    <ScrollBar fx:id="vScroll" orientation="VERTICAL" />
  </right>
</BorderPane>

Note the second tabe has HBox.hgrow="ALWAYS" set.

Write a function to locate the scroll bar in a TableView:

private static final ScrollBar getScrollBar(final TableView<?> tableView) {
  for (final VirtualFlow virtualFlow: Nodes.filter(Nodes.descendents(tableView, 2), VirtualFlow.class)) {
     for (final Node subNode: virtualFlow.getChildrenUnmodifiable()) {
        if (subNode instanceof ScrollBar && ((ScrollBar)subNode).getOrientation() == Orientation.VERTICAL) {
           return (ScrollBar)subNode;
        }
     }
  }

  return null;

}

Use this to locate the two vertical scroll bars and bind their properties (like min, max, value etc.) to your own vertical scroll bar and then hide the original scroll bars. You will also need to set managed=false so that they do not take up space in the layout.

Locate and hide the horizontal scroll bars and bind the properties of the 'moving' table horizontal scroll bar to your own horizontal scroll bar.

We are succesfully using this technique to link two tables with a single scroll bar while waiting for this Jira to be fixed.

Option #2 - Write your own TableViewSkin Download the JavaFx source to see what they do with the skin and then you can either write a complete custom skin or write a skin that wraps two regular skins and implement in a similar way to Option #1 above

Underglaze answered 29/8, 2013 at 12:36 Comment(0)
M
2

So, the question is: are there any configurable properties on TableView or TableColumns to have the behaviour I am looking for?

Apparently not, but there is a feature request for this behavior on the JavaFX Jira (you'll need to register to view it):

https://javafx-jira.kenai.com/browse/RT-19454

I suggest you vote for it. :^)

Monoplegia answered 28/8, 2013 at 20:36 Comment(0)
C
1

JavaFX doesn't have this functionality. Check out ControlsFX's SpreadsheetView.

Cyclic answered 18/1, 2015 at 5:49 Comment(1)
ControlsFX now has a decent implementation on Bitbucket. They've migrated 'SpreadsheetView' functionality to a legit TableView subclass.Cyclic
E
0

Code worked with javafx 11

import javafx.application.Platform;
import javafx.scene.AccessibleAttribute;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TableCell;
import javafx.scene.layout.Region;

public class LockedTableCell<T, S> extends TableCell<T, S> {
        
    {

        Platform.runLater(() -> {

            try {
                ScrollBar scrollBar = (ScrollBar) getTableView()
                            .queryAccessibleAttribute(AccessibleAttribute.HORIZONTAL_SCROLLBAR);
                // set fx:id of TableColumn and get region of column header by #id
                Region headerNode = (Region) getTableView().lookup("#" + getTableColumn().getId());
                scrollBar.valueProperty().addListener((ob, o, n) -> {
                    double doubleValue = n.doubleValue();
                    
                    // move header and cell with translateX & bring it front
                    headerNode.setTranslateX(doubleValue);
                    headerNode.toFront();

                    this.setTranslateX(doubleValue);
                    this.toFront();

                });
            } catch (Exception e) {
                e.printStackTrace();
            }
            
        });

    }

}

Using it (with first M columns)

tableColumnInstance1.setCellFactory(param -> new LockedTableCell<>() {...});
tableColumnInstance2.setCellFactory(param -> new LockedTableCell<>() {...});
...
tableColumnInstanceM.setCellFactory(param -> new LockedTableCell<>() {...});



Evertor answered 16/9, 2021 at 15:10 Comment(2)
Please don't post identical answers (neither as direct copies nor as links) to multiple questions. Post one good answer, then vote/flag to close the other questions as duplicates. If the question is not a duplicate, tailor your answers to the question. In particular, this question is about freezing multiple columns, so you might add an example of how to apply your solution to more than one column :)Triolein
it does. using setCellFactory(...) on N columns in tableview. btw, thanks you, i will tailor my answer about multiple columns.Evertor

© 2022 - 2024 — McMap. All rights reserved.