position of TableColumn in Scene - javafx

I have a TableView with several TableColumns and I want to place a Node below a certain TableColumn. How do I get the exact position (x,y-coordinates) of the TableColumn so I can bind the translate properties of my node?
Here is a snippet of how I placed a button on the top right corner of my TableView:
button.translateXProperty().unbind();
button.translateXProperty().bind(tableView.widthProperty().divide(2.0).subtract(button.getWidth() / 2.0 + 2.0) + tableView.localToScene(0.0, 0.0).getX());
This works fine, but obviously only for the TableView. The TableColumns don't have those translate properties or the localToScene methods, so I can't directly get the position to which I would like to bind my Node.
My current solution (which doesn't really work that well) is to do the following:
I read out the position of my TableView in the Scene (PointA) and then go through the list of all columns (tableView.getColumns()) and check if each of them is visible, and if so, add their width to the X-value of PointA. I do this until I find the actual column that I want to place the Node below.
Now the problem is, that I can't really just bind the Nodes position to this point, because when I change the order of the columns, or make one of them invisible, my column changes position on the screen. I would have to add a listener to the column order and visibility...
Is there any more efficient way to do what I want? :D

I generally dislike using lookups, but you can retrieve the label that is used to display the column header using the lookup .table-view .column-header .label and then bind your button's layout properties using the bounds of that label.
Example:
import java.util.Optional;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class TableColumnLocationExample extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Person> table = new TableView<>();
table.getColumns().add(column("First Name", Person::firstNameProperty, 120));
table.getColumns().add(column("Last Name", Person::lastNameProperty, 120));
table.getColumns().add(column("Email", Person::emailProperty, 250));
table.getItems().addAll(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
Pane root = new Pane(table);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
for (TableColumn<Person, ?> col : table.getColumns()) {
Optional<Label> header = findLabelForTableColumnHeader(col.getText(), root);
header.ifPresent(label -> {
Button button = new Button(col.getText());
button.prefWidthProperty().bind(Bindings.createDoubleBinding(() ->
label.getBoundsInLocal().getWidth(), label.boundsInLocalProperty()));
button.minWidthProperty().bind(button.prefWidthProperty());
button.maxWidthProperty().bind(button.prefWidthProperty());
button.layoutXProperty().bind(Bindings.createDoubleBinding(() ->
label.getLocalToSceneTransform().transform(label.getBoundsInLocal()).getMinX(),
label.boundsInLocalProperty(), label.localToSceneTransformProperty()));
button.layoutYProperty().bind(Bindings.createDoubleBinding(() ->
table.getBoundsInParent().getMaxY() ,table.boundsInParentProperty()));
root.getChildren().add(button);
});
}
}
private Optional<Label> findLabelForTableColumnHeader(String text, Parent root) {
return root.lookupAll(".table-view .column-header .label")
.stream()
.map(Label.class::cast)
.filter(label -> label.getText().equals(text))
.findAny(); // assumes all columns have unique text...
}
private <S,T> TableColumn<S,T> column(String title, Function<S,ObservableValue<T>> property, double width) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
col.setPrefWidth(width);
return col ;
}
public static class Person {
private StringProperty firstName = new SimpleStringProperty();
private StringProperty lastName = new SimpleStringProperty();
private StringProperty email = new SimpleStringProperty();
public Person(String firstName, String lastName, String email) {
setFirstName(firstName);
setLastName(lastName);
setEmail(email);
}
public final StringProperty firstNameProperty() {
return this.firstName;
}
public final String getFirstName() {
return this.firstNameProperty().get();
}
public final void setFirstName(final String firstName) {
this.firstNameProperty().set(firstName);
}
public final StringProperty lastNameProperty() {
return this.lastName;
}
public final String getLastName() {
return this.lastNameProperty().get();
}
public final void setLastName(final String lastName) {
this.lastNameProperty().set(lastName);
}
public final StringProperty emailProperty() {
return this.email;
}
public final String getEmail() {
return this.emailProperty().get();
}
public final void setEmail(final String email) {
this.emailProperty().set(email);
}
}
public static void main(String[] args) {
launch(args);
}
}

If you are allowed to use non-public api, you might consider to access the TableColumnHeader via its skin, provided it's of type TableViewSkinBase: it has api to access the TableRowHeader which is the container of all TableColumnHeaders and has api to find the header for any column it contains.
Code snippet (the width/location binding is copied from James' example, just to the header instead of the label)
private void buttonsPerHeader(TableView<Person> table, Pane root) {
if (!(table.getSkin() instanceof TableViewSkinBase)) return;
TableViewSkinBase skin = (TableViewSkinBase) table.getSkin();
TableHeaderRow headerRow = skin.getTableHeaderRow();
for (TableColumn col : table.getColumns()) {
TableColumnHeader header = headerRow.getColumnHeaderFor(col);
Button button = new Button(col.getText());
button.prefWidthProperty().bind(Bindings.createDoubleBinding(() ->
header.getBoundsInLocal().getWidth(), header.boundsInLocalProperty()));
button.minWidthProperty().bind(button.prefWidthProperty());
button.maxWidthProperty().bind(button.prefWidthProperty());
button.layoutXProperty().bind(Bindings.createDoubleBinding(() ->
header.getLocalToSceneTransform().transform(header.getBoundsInLocal()).getMinX(),
header.boundsInLocalProperty(), header.localToSceneTransformProperty()));
button.layoutYProperty().bind(Bindings.createDoubleBinding(() ->
table.getBoundsInParent().getMaxY() ,table.boundsInParentProperty()));
root.getChildren().add(button);
}
}

Related

JavaFX tableview Enable and Disable row selection

As a part of my project i want display a tableview which should be disabled for some time(on editting time nb:not table editting).So that i got a working code which will disable the tableview.This is the working code,
table.setSelectionModel(null);
So my problem is after editing process is over when click a button i want to enable it back,but unfortunately i could'nt find any alternative code for that.Any one please suggest me the code which will enable the row selection.Any answer will appreciable.
You can retrieve the default selection model when you create the table:
TableView<T> table = new TableView<>();
TableViewSelectionModel<T> defaultSelectionModel = table.getSelectionModel();
where T is the type for your table. (Of course, if you are using FXML, just put the second line in the controller's initialize() method.)
Then to disable row selection do
table.setSelectionModel(null);
and to enable it again
table.setSelectionModel(defaultSelectionModel);
Here is a SSCCE:
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TableView.TableViewSelectionModel;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TableWithDisabledSelection extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Person> table = new TableView<>();
TableViewSelectionModel<Person> defaultSelectionModel = table.getSelectionModel();
table.getColumns().add(column("First Name", Person::firstNameProperty));
table.getColumns().add(column("Last Name", Person::lastNameProperty));
table.getColumns().add(column("Email", Person::emailProperty));
table.getItems().addAll(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
CheckBox enableSelection = new CheckBox("Enable selection");
enableSelection.setSelected(true);
enableSelection.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
table.setSelectionModel(defaultSelectionModel);
} else {
table.setSelectionModel(null);
}
});
BorderPane root = new BorderPane(table);
BorderPane.setAlignment(enableSelection, Pos.CENTER);
BorderPane.setMargin(enableSelection, new Insets(5));
root.setBottom(enableSelection);
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.show();
}
private <S,T> TableColumn<S,T> column(String title, Function<S,Property<T>> prop) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> prop.apply(cellData.getValue()));
return col ;
}
public static void main(String[] args) {
launch(args);
}
public static class Person {
private final StringProperty firstName = new SimpleStringProperty();
private final StringProperty lastName = new SimpleStringProperty();
private final StringProperty email = new SimpleStringProperty();
public Person(String firstName, String lastName, String email) {
setFirstName(firstName);
setLastName(lastName);
setEmail(email);
}
public final StringProperty firstNameProperty() {
return this.firstName;
}
public final String getFirstName() {
return this.firstNameProperty().get();
}
public final void setFirstName(final String firstName) {
this.firstNameProperty().set(firstName);
}
public final StringProperty lastNameProperty() {
return this.lastName;
}
public final String getLastName() {
return this.lastNameProperty().get();
}
public final void setLastName(final String lastName) {
this.lastNameProperty().set(lastName);
}
public final StringProperty emailProperty() {
return this.email;
}
public final String getEmail() {
return this.emailProperty().get();
}
public final void setEmail(final String email) {
this.emailProperty().set(email);
}
}
}
I handled this situation with rowfactory like this;
tableView.setRowFactory(param -> new TableRow<Model>()
{
#Override
protected void updateItem(Model item, boolean empty)
{
super.updateItem(item, empty);
if (!empty)
{
disableProperty().bind(item.getFocusable().not());
}
}
});
so, you can bind the disableproperty with suitable property of your tableview model.

How to change selection behavior of TableView?

I've set multiple selection mode to my TableView and I want multiple rows to be selected with Lclick, not Ctrl + Lclick. Is there a simple way to do this.
I tried table.setOnMouseClicked() with null implementation but it doesn't prevents target row to be selected and previously selected row to be unselected, either setOnMousePressed() or setOnMouseReleased().
I really don't want to re-implement TableView.TableViewSelectionModel. There should be a layer between click and calling TableView.TableViewSelectionModel.clearAndSelect()
UPD
I've just found a few questions with the similar problem but not exactly the same. Those guys wanted to drag and select multiple, when I want to select one-by-one, but without keyboard.
In general, changing behavior for JavaFX UI controls is difficult (or impossible), and generally I'd recommend just accepting the default behaviors (even if they're not what your users might really want).
In this case, I think you can make this work by adding an event filter to the table rows, implementing the desired selection behavior and consuming the event (to prevent the default behavior getting invoked).
Here's an example:
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class MultipleSelectTable extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Person> table = new TableView<>();
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
table.setRowFactory(tv -> {
TableRow<Person> row = new TableRow<>();
row.addEventFilter(MouseEvent.MOUSE_PRESSED, e-> {
if (! row.isEmpty() && e.getClickCount() == 1) {
Person person = row.getItem() ;
if (table.getSelectionModel().getSelectedItems().contains(person)) {
int index = row.getIndex() ;
table.getSelectionModel().clearSelection(index);
} else {
table.getSelectionModel().select(person);
}
e.consume();
}
});
return row ;
});
table.getColumns().add(column("First Name", Person::firstNameProperty));
table.getColumns().add(column("Last Name", Person::lastNameProperty));
table.getColumns().add(column("Email", Person::emailProperty));
table.getItems().addAll(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
BorderPane root = new BorderPane(table);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private static <S,T> TableColumn<S,T> column(String text, Function<S,ObservableValue<T>> property) {
TableColumn<S,T> col = new TableColumn<>(text);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
col.setPrefWidth(200);
return col ;
}
private static class Person {
private final StringProperty firstName = new SimpleStringProperty();
private final StringProperty lastName = new SimpleStringProperty();
private final StringProperty email = new SimpleStringProperty();
public Person(String firstName, String lastName, String email) {
setFirstName(firstName);
setLastName(lastName);
setEmail(email);
}
public final StringProperty firstNameProperty() {
return this.firstName;
}
public final java.lang.String getFirstName() {
return this.firstNameProperty().get();
}
public final void setFirstName(final java.lang.String firstName) {
this.firstNameProperty().set(firstName);
}
public final StringProperty lastNameProperty() {
return this.lastName;
}
public final java.lang.String getLastName() {
return this.lastNameProperty().get();
}
public final void setLastName(final java.lang.String lastName) {
this.lastNameProperty().set(lastName);
}
public final StringProperty emailProperty() {
return this.email;
}
public final java.lang.String getEmail() {
return this.emailProperty().get();
}
public final void setEmail(final java.lang.String email) {
this.emailProperty().set(email);
}
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX TableColumn: is there a way to generate a column header event?

I solved the basic problem I was looking at by setting a comparator on the entire table, but what I was initially trying to do was find a way to "click" the header to generate the sorting event.
I'd still like to know how to do this, as I currently do not know of a method to proc sorting methods of the columns, only the table itself.
Call getSortOrder() on the TableView: that returns a list of TableColumns representing the order by which rows are sorted:
An empty sortOrder list means that no sorting is being applied on the
TableView. If the sortOrder list has one TableColumn within it, the
TableView will be sorted using the sortType and comparator properties
of this TableColumn (assuming TableColumn.sortable is true). If the
sortOrder list contains multiple TableColumn instances, then the
TableView is firstly sorted based on the properties of the first
TableColumn. If two elements are considered equal, then the second
TableColumn in the list is used to determine ordering. This repeats
until the results from all TableColumn comparators are considered, if
necessary.
Then just add to, remove from, set, clear, etc the list as you need.
SSCCE:
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TableViewProgrammaticSort extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Person> table = new TableView<>();
TableColumn<Person, String> firstNameCol = column("First Name", Person::firstNameProperty);
TableColumn<Person, String> lastNameCol = column("Last Name", Person::lastNameProperty);
TableColumn<Person, String> emailCol = column("Email", Person::emailProperty);
table.getColumns().add(firstNameCol);
table.getColumns().add(lastNameCol);
table.getColumns().add(emailCol);
table.getItems().addAll(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
ComboBox<TableColumn<Person, ?>> sortCombo = new ComboBox<>();
sortCombo.getItems().add(firstNameCol);
sortCombo.getItems().add(lastNameCol);
sortCombo.getItems().add(emailCol);
sortCombo.setCellFactory(lv -> new ColumnListCell());
sortCombo.valueProperty().addListener((obs, oldColumn, newColumn) -> {
table.getSortOrder().clear();
if (newColumn != null) {
table.getSortOrder().add(newColumn);
}
});
sortCombo.setButtonCell(new ColumnListCell());
BorderPane root = new BorderPane(table, sortCombo, null, null, null);
BorderPane.setMargin(table, new Insets(10));
BorderPane.setMargin(sortCombo, new Insets(10));
BorderPane.setAlignment(sortCombo, Pos.CENTER);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private static class ColumnListCell extends ListCell<TableColumn<Person, ?>> {
#Override
public void updateItem(TableColumn<Person, ?> column, boolean empty) {
super.updateItem(column, empty);
if (empty) {
setText(null);
} else {
setText(column.getText());
}
}
}
private static <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
return col ;
}
public static class Person {
private final StringProperty firstName = new SimpleStringProperty(this, "firstName");
private final StringProperty lastName = new SimpleStringProperty(this, "lastName");
private final StringProperty email = new SimpleStringProperty(this, "email");
public Person(String firstName, String lastName, String email) {
this.firstName.set(firstName);
this.lastName.set(lastName);
this.email.set(email);
}
public final StringProperty firstNameProperty() {
return this.firstName;
}
public final java.lang.String getFirstName() {
return this.firstNameProperty().get();
}
public final void setFirstName(final java.lang.String firstName) {
this.firstNameProperty().set(firstName);
}
public final StringProperty lastNameProperty() {
return this.lastName;
}
public final java.lang.String getLastName() {
return this.lastNameProperty().get();
}
public final void setLastName(final java.lang.String lastName) {
this.lastNameProperty().set(lastName);
}
public final StringProperty emailProperty() {
return this.email;
}
public final java.lang.String getEmail() {
return this.emailProperty().get();
}
public final void setEmail(final java.lang.String email) {
this.emailProperty().set(email);
}
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX how to highlight TableColumn instead of TableRow when mouse is hovered to column

I have tried to find a solution to highlight table column when mouse is on top of that column, but I have not been able to find a solution. Is someone already found some solution for that? I have already tried to take a look to css and components with ScenicView, but it is not helping very much.
If you're using Java 8, you can do this quite nicely using a CSS pseudoclass. The performance of CSS pseudoclasses is very good (anecdotally, at least, it is the fastest way to change the style of JavaFX controls), and since the table view only creates a small number of table cells, even for very large data sets, this should perform very well.
Define an external css class that sets the style of a table cell with a custom pseudoclass applied as follows:
.table-cell:column-hover {
-fx-background-color: -fx-cell-focus-inner-border, -fx-selection-bar ;
-fx-background-insets: 0, 0 0 1 0 ;
-fx-background: -fx-selection-bar ;
}
(The -fx-background-color here actually changes the background color: the -fx-background is just a trick to ensure the text fill stays a suitable color relative to the background.)
Now, for each column, define a boolean property. Create a cell factory for each column that sets the boolean property to true when the mouse moves over a cell, and sets it to false when the mouse moves off. Have each cell observe the boolean property and set the pseudoclass state for the cell when it changes.
Here's an example, using the usual Person table from the standard tutorial. The interesting code is in the createCol(...) method:
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.css.PseudoClass;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TableViewTest extends Application {
private static final PseudoClass COLUMN_HOVER_PSEUDO_CLASS = PseudoClass.getPseudoClass("column-hover");
#Override
public void start(Stage primaryStage) {
TableView<Person> table = new TableView<>();
TableColumn<Person, String> firstNameCol = createCol("First Name", Person::firstNameProperty, 150);
TableColumn<Person, String> lastNameCol = createCol("Last Name", Person::lastNameProperty, 150);
TableColumn<Person, String> emailCol = createCol("Email", Person::emailProperty, 200);
table.getItems().addAll(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
table.getColumns().add(firstNameCol);
table.getColumns().add(lastNameCol);
table.getColumns().add(emailCol);
VBox root = new VBox(15, table);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 800, 600);
scene.getStylesheets().add("table-column-hover.css");
primaryStage.setScene(scene);
primaryStage.show();
}
private TableColumn<Person, String> createCol(String title,
Function<Person, ObservableValue<String>> mapper, double size) {
TableColumn<Person, String> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> mapper.apply(cellData.getValue()));
// Is the column being hovered over with the mouse?
BooleanProperty columnHover = new SimpleBooleanProperty();
col.setCellFactory(column -> {
// basic table cell:
TableCell<Person, String> cell = new TableCell<Person, String>() ;
cell.textProperty().bind(cell.itemProperty());
// when the mouse hovers over the cell, set the columnHover to indicate
// the mouse is over the column:
cell.hoverProperty().addListener((obs, wasHovered, isNowHovered) -> {
columnHover.set(isNowHovered);
});
// update the column-hover pseudoclass state for this cell when the column is hovered over
// note this will activate the pseudoclass when the mouse is over any cell in this column
columnHover.addListener((obs, columnWasHovered, columnIsNowHovered) ->
cell.pseudoClassStateChanged(COLUMN_HOVER_PSEUDO_CLASS, columnIsNowHovered)
);
return cell ;
});
col.setPrefWidth(size);
return col ;
}
public class Person {
private final StringProperty firstName = new SimpleStringProperty(this, "firstName");
private final StringProperty lastName = new SimpleStringProperty(this, "lastName");
private final StringProperty email = new SimpleStringProperty(this, "email");
public Person(String firstName, String lastName, String email) {
this.firstName.set(firstName);
this.lastName.set(lastName);
this.email.set(email);
}
public final StringProperty firstNameProperty() {
return this.firstName;
}
public final java.lang.String getFirstName() {
return this.firstNameProperty().get();
}
public final void setFirstName(final java.lang.String firstName) {
this.firstNameProperty().set(firstName);
}
public final StringProperty lastNameProperty() {
return this.lastName;
}
public final java.lang.String getLastName() {
return this.lastNameProperty().get();
}
public final void setLastName(final java.lang.String lastName) {
this.lastNameProperty().set(lastName);
}
public final StringProperty emailProperty() {
return this.email;
}
public final java.lang.String getEmail() {
return this.emailProperty().get();
}
public final void setEmail(final java.lang.String email) {
this.emailProperty().set(email);
}
}
public static void main(String[] args) {
launch(args);
}
}
I guess that is only possible with many "hacks".
The TableColumn only exists in the data model of the TableView, the view part that is rendered based on the model gets rendered in rows, not in columns. So each TableCell has a table row as parent - not a table column.
If it were implemented the other way round, the table gets rendered in columns we could easily achieve this.
Now if you really want to do this, you would have to do the following:
Write a custom TableCell and register it as the CellRenderer for all columns.
If your custom cell gets (un-)hovered, retrieve its column and notify all other cells that are in the same column.
But: This will slow your application down if the table grows larger and / or the computer where the application is running is slow.

Expand table row on mouse click

I found these examples:
http://www.jeasyui.com/tutorial/datagrid/datagrid21.php\
Can a table row expand and close?
Basically I want to create a JavaFX table which I can expand in order to see more data. Is there any similar example written in JavaFX?
EDIT
So, after reworking the problem with tableView specifics, I (sort of) quickly hacked together this example. Keep in mind, I didn't use the animation mentioned in the original answer, although it would be easy enough to adapt, and I didn't replicate the provided example exactly at all, since I honestly, didn't have time. But this gives the basic accordion feel, where you would just need to spend time messing around with various width and height properties of different fields to achieve something that was exactly that. (in the handler you might want to even insert a row where the first column has a huge width and a nested table view to achieve sort of exactly what they were doing). again, this is with 1 column, and it shows the basics of adding a bit of added information on expansion, you could take this as far as you want:
fileChooserExample.java:
package filechooserexample;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.event.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.*;
import javafx.util.Callback;
public class FileChooserExample extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(final Stage stage) {
stage.setTitle("People");
// stage.getIcons().add(new Image("http://icons.iconarchive.com/icons/icons-land/vista-people/72/Historical-Viking-Female-icon.png")); // icon license: Linkware (Backlink to http://www.icons-land.com required)
// create a table.
final TableView<Person> table = new TableView<>(
FXCollections.observableArrayList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Brown")
)
);
// define the table columns.
TableColumn<Person, Boolean> actionCol = new TableColumn<>("Action");
actionCol.setSortable(false);
actionCol.setPrefWidth(1000);
// define a simple boolean cell value for the action column so that the column will only be shown for non-empty rows.
actionCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, Boolean>, ObservableValue<Boolean>>() {
#Override public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Person, Boolean> features) {
return new SimpleBooleanProperty(features.getValue() != null);
}
});
// create a cell value factory with an add button for each row in the table.
actionCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
#Override public TableCell<Person, Boolean> call(TableColumn<Person, Boolean> personBooleanTableColumn) {
return new AddPersonCell(stage, table);
}
});
table.getColumns().setAll(actionCol);
table.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
stage.setScene(new Scene(table));
stage.show();
}
/** A table cell containing a button for adding a new person. */
private class AddPersonCell extends TableCell<Person, Boolean> {
// a button for adding a new person.
final Button addButton = new Button("Add");
// pads and centers the add button in the cell.
final VBox paddedButton = new VBox();
final HBox mainHolder = new HBox();
// records the y pos of the last button press so that the add person dialog can be shown next to the cell.
final DoubleProperty buttonY = new SimpleDoubleProperty();
/**
* AddPersonCell constructor
* #param stage the stage in which the table is placed.
* #param table the table to which a new person can be added.
*/
AddPersonCell(final Stage stage, final TableView table) {
paddedButton.setPadding(new Insets(3));
paddedButton.getChildren().add(addButton);
mainHolder.getChildren().add(paddedButton);
addButton.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
buttonY.set(mouseEvent.getScreenY());
if (getTableRow().getPrefHeight() == 100){
getTableRow().setPrefHeight(35);
paddedButton.getChildren().remove(1);
getTableRow().autosize();
}
else{
getTableRow().setPrefHeight(100);
Label myLabel = new Label();
myLabel.setText("This is new label text!");
myLabel.setTextFill(Color.BLACK);
paddedButton.getChildren().add(myLabel);
getTableRow().autosize();
}
}
});
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
table.getSelectionModel().select(getTableRow().getIndex());
}
});
}
/** places an add button in the row only if the row is not empty. */
#Override protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(paddedButton);
}
}
}
}
Person.java:
package filechooserexample;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private StringProperty firstName;
private StringProperty lastName;
public Person(String firstName, String lastName) {
setFirstName(firstName);
setLastName(lastName);
}
public final void setFirstName(String value) { firstNameProperty().set(value); }
public final void setLastName(String value) { lastNameProperty().set(value); }
public String getFirstName() { return firstNameProperty().get(); }
public String getLastName() { return lastNameProperty().get(); }
public StringProperty firstNameProperty() {
if (firstName == null) firstName = new SimpleStringProperty(this, "firstName");
return firstName;
}
public StringProperty lastNameProperty() {
if (lastName == null) lastName = new SimpleStringProperty(this, "lastName");
return lastName;
}
}
Again, pardon the seemingly hackery of adding the various buttons with the named columns that do nothing, It just got super busy here so I borrowed the main table structure from :
original SO table dynamic row addition question
Who did a wonderful job of adding additional rows to a table.
again, if this is not at all what you need let me know, and I'll try to help as best I can.

Resources