16

Does anyone know how to add a tooltip to a column header in a TableView ?

There are many places where are explained how to add the tooltip to data cells, but I didn't find a way to add the tooltip to header.

Using the tool ScenicView, I can see that the headers are Labels inside a TableColumnHeader object, but It seems that It is not a public object.

Any suggestions ?

Roberto
  • 8,586
  • 3
  • 42
  • 53

3 Answers3

26
    TableColumn<Person, String> firstNameCol = new TableColumn<>();
    Label firstNameLabel = new Label("First Name");
    firstNameLabel.setTooltip(new Tooltip("This column shows the first name"));
    firstNameCol.setGraphic(firstNameLabel);
James_D
  • 201,275
  • 16
  • 291
  • 322
  • It works ! I need to remove the TableColumn text property, but I get what I need, thanks for the quick answer. – Roberto Apr 22 '14 at 16:21
  • 4
    Note that if you have `tableMenuButtonVisible` set to true and use this solution, the menu shown by that button won't contain any text as it uses the text of the table columns. – Grochni Jun 24 '15 at 13:33
  • Thanks James_D! I found that if you wrap your code in a `Platform.runLater(() -> {}` you can just `.lookup()` the header and its existing label. Thanks for helping me figure it all out! – Brad Turek Apr 13 '17 at 04:03
  • It seems like this removed the auto column width that was present, is there a way to restore it? – Jeffmagma May 24 '23 at 00:59
10

This is an extended answer to James_D. (I don't have the reputation to comment):

To make the label connect with textProperty of the column and just hide the original text, so it does not mess up the rest of the table functionality:

nameLabel.textProperty().bindBidirectional(textProperty());
nameLabel.getStyleClass().add("column-header-label");
nameLabel.setMaxWidth(Double.MAX_VALUE); //Makes it take up the full width of the table column header and tooltip is shown more easily.

css:

.table-view .column-header .label{
    -fx-content-display: graphic-only;
}
.table-view .column-header .label .column-header-label{
    -fx-content-display: text-only;
}
Jesper Pedersen
  • 111
  • 1
  • 4
  • 1
    +1 for the `setMaxWidth()`, the tooltips are shown much more naturally for the user (when hovering any part of the header, not necessarily the exact text part per se) – Joffrey Oct 18 '16 at 12:47
  • I found that `label.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);` works a treat; the tooltip appears in the whole square, not just on the same line as the text! – Brad Turek Apr 12 '17 at 22:16
  • The style classes may be simplified by using a nested Label selector (that way no id has to be set). e.i.: _grahpics-only selector_: `.column-header Label` _text-only selector_: `.column-header Label Label` – n247s Mar 25 '19 at 21:38
5

The solution

Alternatively, you can look up the label that's already there and just give it a tool tip.

In order to .lookup() an element the table needs to be rendered already. To be sure that your code runs after the table is rendered, just wrap your code in a Platform.runlater().

// We need to do this after the table has been rendered (we can't look up elements until then)
Platform.runLater(() -> {
    // Prepare a tooltip
    Tooltip tooltip = new Tooltip("This is a super cool control; here's how to work it...");
    tooltip.setWrapText(true);
    tooltip.setMaxWidth(200);

    // Get column's column header
    TableColumnHeader header = (TableColumnHeader) historyTable.lookup("#" + column.getId());

    // Get column header's (untooltipped) label
    Label label = (Label) header.lookup(".label");

    // Give the label a tooltip
    label.setTooltip(tooltip);

    // Makes the tooltip display, no matter where the mouse is inside the column header.
    label.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
}

The solution at scale

If you want to do this for your whole set of columns, you might put them all in a LinkedHashMap (this will just help you organizationally by keeping your columns associated with their messages):

// We'll use this to associate columns with messages for right now.
LinkedHashMap<TableColumn<Person, String>, String> tableColumns = new LinkedHashMap<>();

// Each column gets a helpful message
tableColumns.put(numberOfBoxesColumn, "The total number of boxes that have arrived");
tableColumns.put(backordersColumn, "Use these columns as a measure of how urgently the Purchase Order needs to be processed.");
/*... put each column in along with it's message */

... then use a for-each loop to loop through and give each column a tooltip ...

// Make a tooltip out of each message. Give each column('s label) it's tooltip.
for (Map.Entry<TableColumn<Person, String>, String> pair : tableColumns.entrySet()) {
    
    TableColumn<Person, String> column;
    String message;

    // Get the column and message
    column = pair.getKey();
    message = pair.getValue();

    // Prepare a tooltip
    Tooltip tooltip = new Tooltip(message);
    tooltip.setWrapText(true);
    tooltip.setMaxWidth(200);

    // Get column's column header
    TableColumnHeader header = (TableColumnHeader) historyTable.lookup("#" + column.getId());

    // Get column header's (untooltipped) label
    Label label = (Label) header.lookup(".label");

    // Give the label a tooltip
    label.setTooltip(tooltip);

    // Makes the tooltip display, no matter where the mouse is inside the column header.
    label.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
}

NOTE: I tried the combination of Jesper and James' solutions before coming up with this one. Sadly, when I did, the CSS caused all of my labels to disappear when I look at my .fxml layout in SceneBuilder. And we just can't have that, can we? (okay maybe I just couldn't have that).

Where're mah labels!

Community
  • 1
  • 1
Brad Turek
  • 2,472
  • 3
  • 30
  • 56