JavaFX

JavaFX TreeTableView Example

This is a JavaFX TreeTableView Example. The TreeTableView control combines the features of the TableView and TreeView controls. It displays a TreeView inside a TableView.

A TreeView is used to view hierarchical data. A TableView is used to view tabular data. A TreeTableView is used to view hierarchical data in a tabular form.

The following table shows an overview of the whole article:

The following examples use Java SE 8 and JavaFX 2.2.

1. Introduction

TreeTableView inherits from Control, not from TreeView or TableView. TreeTableView reuses most of the code used for TreeView and TableView. Most of the classes in the API are inherited from a common abstract base class for all three controls.

For example, the TableColumn and TreeTableColumn classes are used to define columns in TableView and TreeTableView, respectively, and both are inherited from the TableColumnBase class.

TreeTableView API looks huge as it combines the APIs for both TreeView and TableView. However, if you are familiar with TreeView and TableView APIs, the TreeTableView API will look familiar to you.

TreeTableView supports the following features:

  • You can add multiple columns.
  • You can have nested columns.
  • You can resize columns at runtime.
  • You can reorder columns at runtime.
  • You can sort data on a single or multiple columns.
  • You can add a context menu for columns.
  • You can set a cell value factory for a column to populate its cells.
  • You can set a cell factory for a column to customize its cells rendering.
  • You can edit data in cells.

TreeItems provide the model in a TreeView. Each node in the TreeView derives its data from the corresponding TreeItem. Recall that you can visualize each node in a TreeView as a row with only one column.

An ObservableList provides the model in a TableView. Each item in the observable list provides data for a row in the TableView. A TableView can have multiple columns.

TreeTableView also uses a model for its data. Because it is a combination of TreeView and TableView, it has to decide which type of model it uses. It uses the model based on TreeView. That is, each row in a TreeTableView is defined by a TreeItem in a TreeView. TreeTableView supports multiple columns. Data for columns in a row are derived from the TreeItem for that row.

2. Creating a TreeTableView

2.1 The Code

Person.java

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class Person
{
	// Declaring the attributes page 424
	private String firstName;
	private String lastName;
	private LocalDate birthDate;

	// An enum for age categories
	public enum AgeCategory
	{
		BABY,
		CHILD,
		TEEN,
		ADULT,
		SENIOR,
		UNKNOWN
	};

	public Person(String firstName, String lastName, LocalDate birthDate)
	{
		this.firstName = firstName;
		this.lastName = lastName;
		this.birthDate = birthDate;
	}

	public String getFirstName()
	{
		return firstName;
	}

	public void setFirstName(String firstName)
	{
		this.firstName = firstName;
	}

	public String getLastName()
	{
		return lastName;
	}

	public void setLastName(String lastName)
	{
		this.lastName = lastName;
	}

	public LocalDate getBirthDate()
	{
		return birthDate;
	}

	public void setBirthDate(LocalDate birthDate)
	{
		this.birthDate = birthDate;
	}

	@Override
	public String toString()
	{
		return firstName + " " + lastName + ", " + birthDate.toString();
	}

	/* Domain specific business rules */
	public AgeCategory getAgeCategory()
	{
		if (birthDate == null)
		{
			return AgeCategory.UNKNOWN;
		}

		long years = ChronoUnit.YEARS.between(birthDate, LocalDate.now());

		if (years >= 0 && years < 2)
		{
			return AgeCategory.BABY;
		}
		else if (years >= 2 && years < 13)
		{
			return AgeCategory.CHILD;
		}
		else if (years >= 13 && years <= 19)
		{
			return AgeCategory.TEEN;
		}
		else if (years > 19 && years <= 50)
		{
			return AgeCategory.ADULT;
		}
		else if (years > 50)
		{
			return AgeCategory.SENIOR;
		}
		else
		{
			return AgeCategory.UNKNOWN;
		}
	}
}

TreeTableUtil.java

import java.time.LocalDate;

import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;

public class TreeTableUtil
{
	@SuppressWarnings("unchecked")
	public static TreeItem<Person> getModel()
	{
		// Create all persons
		// First level
		Person person1 = new Person("FirstName1", "LastName1", LocalDate.of(1930, 1, 1));

		// Second level
		Person person2 = new Person("FirstName2", "LastName2", LocalDate.of(1956, 12, 17));
		Person person3 = new Person("FirstName3", "LastName3", LocalDate.of(1961, 3, 1));
		Person person4 = new Person("FirstName4", "LastName4", LocalDate.of(1968, 1, 12));
		Person person5 = new Person("FirstName5", "LastName5", LocalDate.of(1978, 4, 14));

		// Third level
		Person person6 = new Person("FirstName6", "LastName6", LocalDate.of(1980, 5, 10));
		Person person7 = new Person("FirstName7", "LastName7", LocalDate.of(1981, 3, 20));
		Person person8 = new Person("FirstName8", "LastName8", LocalDate.of(1982, 6, 3));
		Person person9 = new Person("FirstName9", "LastName9", LocalDate.of(1990, 8, 27));
		Person person10 = new Person("FirstName10", "LastName10", LocalDate.of(1994, 5, 15));

		// Fourth level
		Person person11 = new Person("FirstName11", "LastName11", LocalDate.of(2010, 6, 3));
		Person person12 = new Person("FirstName12", "LastName12", LocalDate.of(2012, 10, 11));
		Person person13 = new Person("FirstName13", "LastName13", LocalDate.of(2012, 10, 11));

		// Build nodes
		TreeItem<Person> person6Node = new TreeItem<>(person6);
		person6Node.getChildren().addAll(new TreeItem<>(person11), new TreeItem<>(person12));

		TreeItem<Person> person7Node = new TreeItem<>(person7);
		person7Node.getChildren().addAll(new TreeItem<>(person13));

		TreeItem<Person> person2Node = new TreeItem<>(person2);
		person2Node.getChildren().addAll(person6Node, new TreeItem<>(person8),person7Node);

		TreeItem<Person> person3Node = new TreeItem<>(person3);
		person3Node.getChildren().addAll(new TreeItem<>(person9), new TreeItem<>(person10));

		TreeItem<Person> person4Node = new TreeItem<>(person4);
		TreeItem<Person> person5Node = new TreeItem<>(person5);

		// Create the root node and add children
		TreeItem<Person> rootNode = new TreeItem<>(person1);
		rootNode.getChildren().addAll(person2Node, person3Node, person4Node, person5Node);

		return rootNode;
	}

	// Returns Person Id TreeTableColumn
	public static TreeTableColumn<Person, Integer> getIdColumn()
	{
		TreeTableColumn<Person, Integer> idColumn = new TreeTableColumn<>("Id");
		idColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("personId"));
		return idColumn;
	}

	// Returns First Name TreeTableColumn
	public static TreeTableColumn<Person, String> getFirstNameColumn()
	{
		TreeTableColumn<Person, String> firstNameCol = new TreeTableColumn<>("First Name");
		firstNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("firstName"));
		return firstNameCol;
	}

	// Returns Last Name TreeTableColumn
	public static TreeTableColumn<Person, String> getLastNameColumn()
	{
		TreeTableColumn<Person, String> lastNameCol = new TreeTableColumn<>("Last Name");
		lastNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("lastName"));
		return lastNameCol;
	}

	// Returns Birth Date TreeTableColumn 
	public static TreeTableColumn<Person, LocalDate> getBirthDateColumn()
	{
		TreeTableColumn<Person, LocalDate> birthDateCol = new TreeTableColumn<>("Birth Date");
		birthDateCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("birthDate"));
		return birthDateCol;
	}

	// Returns Age Category TreeTableColumn
	public static TreeTableColumn<Person, Person.AgeCategory> getAgeCategoryColumn()
	{
		TreeTableColumn<Person, Person.AgeCategory> birthDateCol = new TreeTableColumn<>("Age Category");
		birthDateCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("ageCategory"));
		return birthDateCol;
	}
}

FxTreeTableViewExample1.java

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeTableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class FxTreeTableViewExample1 extends Application
{
	public static void main(String[] args)
	{
		Application.launch(args);
	}

	@Override
	public void start(Stage stage)
	{
		// Create a TreeTableView with model
		TreeTableView<Person> treeTable = new TreeTableView<>();
		treeTable.setPrefWidth(400);

		// Create the VBox
		VBox root = new VBox(treeTable);

		// Set the Style-properties of the VBox
		root.setStyle("-fx-padding: 10;" +
				"-fx-border-style: solid inside;" +
				"-fx-border-width: 2;" +
				"-fx-border-insets: 5;" +
				"-fx-border-radius: 5;" +
				"-fx-border-color: blue;");

		// Create the Scene
		Scene scene = new Scene(root);
		// Add the Scene to the Stage
		stage.setScene(scene);
		// Set the Title
		stage.setTitle("A simple TreeTableView");
		// Display the Stage
		stage.show();
	}
}

FxTreeTableViewExample2.java

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class FxTreeTableViewExample2 extends Application
{
	public static void main(String[] args)
	{
		Application.launch(args);
	}

	@Override
	public void start(Stage stage)
	{
		// Create a TreeTableView with model
		TreeTableView<Person> treeTable = new TreeTableView<>();
		treeTable.setPrefWidth(400);

		// Create three columns
		TreeTableColumn<Person, String> firstNameCol = new TreeTableColumn<>("First Name");
		TreeTableColumn<Person, String> lastNameCol = new TreeTableColumn<>("Last Name");
		TreeTableColumn<Person, String> birthDateCol = new TreeTableColumn<>("Birth Date");

		// Add columns to the TreeTableView
		treeTable.getColumns().add(firstNameCol);
		treeTable.getColumns().add(lastNameCol);
		treeTable.getColumns().add(birthDateCol);

		// Create the VBox
		VBox root = new VBox(treeTable);

		// Set the Style-properties of the VBox
		root.setStyle("-fx-padding: 10;" +
				"-fx-border-style: solid inside;" +
				"-fx-border-width: 2;" +
				"-fx-border-insets: 5;" +
				"-fx-border-radius: 5;" +
				"-fx-border-color: blue;");

		// Create the Scene
		Scene scene = new Scene(root);
		// Add the Scene to the Stage
		stage.setScene(scene);
		// Set the Title
		stage.setTitle("A simple TreeTableView with Colums");
		// Display the Stage
		stage.show();
	}
}

FxTreeTableViewExample3.java

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class FxTreeTableViewExample3 extends Application
{
	public static void main(String[] args)
	{
		Application.launch(args);
	}

	@Override
	public void start(Stage stage)
	{
		// Create the RootNode
		TreeItem<Person> rootNode = TreeTableUtil.getModel();
		rootNode.setExpanded(true);

		// Create a TreeTableView with model
		TreeTableView<Person> treeTable = new TreeTableView<>(rootNode);
		treeTable.setPrefWidth(400);

		// Add columns to the TreeTableView
		treeTable.getColumns().add(TreeTableUtil.getFirstNameColumn());
		treeTable.getColumns().add(TreeTableUtil.getLastNameColumn());
		treeTable.getColumns().add(TreeTableUtil.getBirthDateColumn());
		treeTable.getColumns().add(TreeTableUtil.getAgeCategoryColumn());

		// Create the VBox
		VBox root = new VBox(treeTable);

		// Set the Style-properties of the VBox
		root.setStyle("-fx-padding: 10;" +
				"-fx-border-style: solid inside;" +
				"-fx-border-width: 2;" +
				"-fx-border-insets: 5;" +
				"-fx-border-radius: 5;" +
				"-fx-border-color: blue;");

		// Create the Scene
		Scene scene = new Scene(root);
		// Add the Scene to the Stage
		stage.setScene(scene);
		// Set the Title
		stage.setTitle("A TreeTableView with Data");
		// Display the Stage
		stage.show();
	}
}

An instance of the TreeTableView represents a TreeTableView control. The class takes a generic type argument, which is the type of the item contained in the TreeItems. Recall that TreeItems provide a model for a TreeTableView. The generic type of the controls and its TreeItems are the same.

The TreeTableView class provides two constructors. The default constructor creates a TreeTableView with no data. The following statement creates a TreeTableView of Person. The control displays a placeholder, similar to the one shown by TableView. Like a TableView, TreeTableView contains a placeholder property, which is Node, and if you need to, you can supply your own placeholder:

// Create a TableView
TreeTableView<Person> treeTable = new TreeTableView<>();

An instance of the TreeTableColumn class represents a column in a TreeTableView. The getColumns() method of the TreeTableView class returns an ObservableList of TreeTableColumns, which are columns that are added to the TreeTableView. You need to add columns to this columns list.

The following snippet of code creates four columns and adds them to the TreeTableView.

// Create three columns
TreeTableColumn firstNameCol = new TreeTableColumn("First Name");
TreeTableColumn lastNameCol = new TreeTableColumn("Last Name");
TreeTableColumn birthDateCol = new TreeTableColumn("Birth Date");

// Add columns to the TreeTableView
treeTable.getColumns().add(firstNameCol);
treeTable.getColumns().add(lastNameCol);
treeTable.getColumns().add(birthDateCol);

Now you need to supply data for the control. TreeTableView displays hierarchical data in tabular form. It requires you to construct a hierarchical model using TreeItems.

You need to pass the root TreeItem to the TreeTableView. Like a TreeView, a TreeTableView contains a root property, which is the root TreeItem for the TreeView. The root property acts as a model for the TreeTableView to supply it data.

The following snippet of code creates a tree of some persons. The root TreeItem is set as the root of the TreeTableView.

Person person1 = new Person("FirstName1", "LastName1", LocalDate.of(1930, 1, 1));

// Second level
Person person2 = new Person("FirstName2", "LastName2", LocalDate.of(1956, 12, 17));
Person person3 = new Person("FirstName3", "LastName3", LocalDate.of(1961, 3, 1));
Person person4 = new Person("FirstName4", "LastName4", LocalDate.of(1968, 1, 12));
Person person5 = new Person("FirstName5", "LastName5", LocalDate.of(1978, 4, 14));

Person person9 = new Person("FirstName9", "LastName9", LocalDate.of(1990, 8, 27));
Person person10 = new Person("FirstName10", "LastName10", LocalDate.of(1994, 5, 15));

TreeItem<Person> person3Node = new TreeItem<>(person3);
person3Node.getChildren().addAll(new TreeItem<>(person9), new TreeItem<>(person10));

TreeItem<Person> person4Node = new TreeItem<>(person4);
TreeItem<Person> person5Node = new TreeItem<>(person5);

// Create the root node and add children
TreeItem<Person> rootNode = new TreeItem<>(person1);
rootNode.getChildren().addAll(person2Node, person3Node, person4Node, person5Node);

// Set the model for the TreeTableView
treeTable.setRoot(rootNode);

There is a missing link and the columns do not know how to extract data from the TreeItems. This is accomplished by setting the cell value factory for each column.

Setting the cell value factory for a TreeTableColumn is very similar to of the way you would for TableColumn. The following snippet of code sets the cell value factory for columns:

// Create Columns with Cell Factories
TreeTableColumn<Person, String> firstNameColumn = TreeTableUtil.getFirstNameColumn();
firstNameColumn.setCellFactory(TextFieldTreeTableCell.<Person>forTreeTableColumn());

TreeTableColumn<Person, String> lastNameColumn = TreeTableUtil.getLastNameColumn();
lastNameColumn.setCellFactory(TextFieldTreeTableCell.<Person>forTreeTableColumn());

TreeTableColumn<Person, LocalDate> birthDateColumn = TreeTableUtil.getBirthDateColumn();
LocalDateStringConverter converter = new LocalDateStringConverter();

birthDateColumn.setCellFactory(TextFieldTreeTableCell.<Person, LocalDate>forTreeTableColumn(converter));

A TreeItemPropertyValueFactory reads the specified property of the object stored in the value property of a TreeItem to populate the cells of the column.

In the example, each TreeItem contains a Person object.

If you ignore the disclosure node and indentations in the first column, this is exactly how a TableView shows the data. The disclosure node and the indentations are features of the TreeView.

By default, a TreeTableView shows the disclosure node in the first column. You can show it in any other column using the treeColumn property.

The following snippet of code shows the disclosure node in the Last Name column:

treeTable.setTreeColumn(lastNameCol);

Another constructor of the TreeTableView class takes the value for its root property as an argument. You can use it as follows:

TreeTableView<Person> treeTable = new TreeTableView<Person>(rootNode);

2.2 The GUI

The following image shows a empty TreeTableView.

A simple JavaFX TreeTableView
A simple JavaFX TreeTableView

The following image shows a TreeTableView with columns, but no content.

A simple JavaFX TreeTableView with Columns
A simple JavaFX TreeTableView with Columns

The following image shows a TreeTableView with columns and data.

A JavaFX TreeTableView with Data
A JavaFX TreeTableView with Data

3. Showing and Hiding Columns

3.1 The Code

FxTreeTableViewExample4.java

import java.time.LocalDate;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class FxTreeTableViewExample4 extends Application
{
	public static void main(String[] args)
	{
		Application.launch(args);
	}

	@Override
	public void start(Stage stage)
	{
		// Create the RootNode
		TreeItem<Person> rootNode = TreeTableUtil.getModel();
		rootNode.setExpanded(true);

		// Create a TreeTableView with model
		TreeTableView<Person> treeTable = new TreeTableView<>(rootNode);
		treeTable.setPrefWidth(400);

		// Create the TreeTableColumns
		TreeTableColumn<Person, String> firstNameColumn = TreeTableUtil.getFirstNameColumn();
		TreeTableColumn<Person, String> lastNameColumn = TreeTableUtil.getLastNameColumn();
		TreeTableColumn<Person, LocalDate> birthDateColumn = TreeTableUtil.getBirthDateColumn();
		TreeTableColumn<Person, Person.AgeCategory> ageCategoryColumn = TreeTableUtil.getAgeCategoryColumn();

		// Make the Age Category column invisible
		ageCategoryColumn.setVisible(false);

		// Add columns to the TreeTableView
		treeTable.getColumns().add(firstNameColumn);
		treeTable.getColumns().add(lastNameColumn);
		treeTable.getColumns().add(birthDateColumn);
		treeTable.getColumns().add(ageCategoryColumn);

		// Create the VBox
		VBox root = new VBox(treeTable);

		// Set the Style-properties of the VBox
		root.setStyle("-fx-padding: 10;" +
				"-fx-border-style: solid inside;" +
				"-fx-border-width: 2;" +
				"-fx-border-insets: 5;" +
				"-fx-border-radius: 5;" +
				"-fx-border-color: blue;");

		// Create the Scene
		Scene scene = new Scene(root);
		// Add the Scene to the Stage
		stage.setScene(scene);
		// Set the Title
		stage.setTitle("A Visibilty Example");
		// Display the Stage
		stage.show();
	}
}

Showing and hiding columns in a TreeTableView work the same way they do for TableView. By default, all columns in a TreeTableView are visible. The TreeTableColumn class has a visible property to set the visibility of a column. If you turn off the visibility of a parent column, a column with nested columns, all its nested columns will become invisible. The following code shows this:

TreeTableColumn<Person, Person.AgeCategory> ageCategoryColumn = TreeTableUtil.getAgeCategoryColumn();

// Make the Age Category column invisible
ageCategoryColumn.setVisible(false);

Sometimes you may want to let the user control the visibility of columns. The TreeTableView class has a tableMenuButtonVisible property. If it is set to true, a menu button is displayed in the header area. Clicking the Menu button displays a list of all leaf columns. Columns are displayed as radio menu items that can be used to toggle their visibility.

3.2 The GUI

The following image shows a TreeTableView with a hidden column.

A JavaFX TreeTableView Visibility Example
A JavaFX TreeTableView Visibility Example

4. Selecting Cells and Rows in a TreeTableView

4.1 The Code

FxTreeTableViewExample5.java

import java.time.LocalDate;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.TreeTableView.TreeTableViewSelectionModel;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class FxTreeTableViewExample5 extends Application
{
	public static void main(String[] args)
	{
		Application.launch(args);
	}

	@Override
	public void start(Stage stage)
	{
		// Create the RootNode
		TreeItem<Person> rootNode = TreeTableUtil.getModel();
		rootNode.setExpanded(true);

		// Create a TreeTableView with model
		TreeTableView<Person> treeTable = new TreeTableView<>(rootNode);
		treeTable.setPrefWidth(400);

		// Create the TreeTableColumns
		TreeTableColumn<Person, String> firstNameColumn = TreeTableUtil.getFirstNameColumn();
		TreeTableColumn<Person, String> lastNameColumn = TreeTableUtil.getLastNameColumn();
		TreeTableColumn<Person, LocalDate> birthDateColumn = TreeTableUtil.getBirthDateColumn();
		TreeTableColumn<Person, Person.AgeCategory> ageCategoryColumn = TreeTableUtil.getAgeCategoryColumn();

		// Add columns to the TreeTableView
		treeTable.getColumns().add(firstNameColumn);
		treeTable.getColumns().add(lastNameColumn);
		treeTable.getColumns().add(birthDateColumn);
		treeTable.getColumns().add(ageCategoryColumn);

		// Turn on multiple-selection mode for the TreeTableView
		TreeTableViewSelectionModel<Person> selection = treeTable.getSelectionModel();
		selection.setSelectionMode(SelectionMode.MULTIPLE);

		// Enable cell-level selection
		selection.setCellSelectionEnabled(true);

		// Create the VBox
		VBox root = new VBox(treeTable);

		// Set the Style-properties of the VBox
		root.setStyle("-fx-padding: 10;" +
				"-fx-border-style: solid inside;" +
				"-fx-border-width: 2;" +
				"-fx-border-insets: 5;" +
				"-fx-border-radius: 5;" +
				"-fx-border-color: blue;");

		// Create the Scene
		Scene scene = new Scene(root);
		// Add the Scene to the Stage
		stage.setScene(scene);
		// Set the Title
		stage.setTitle("A TreeTableView with a Selection Model");
		// Display the Stage
		stage.show();
	}
}

TreeTableView has a selection model represented by its property called selectionModel. A selection model is an instance of the TreeTableViewSelectionModel class, which is an inner static class of the TreeTableView class. The selection model supports cell-level and row-level selection. It also supports two selection modes: single and multiple.

In the single selection mode, only one cell or row can be selected at a time. In the multiple-selection mode, multiple cells or rows can be selected. By default, single row selection is enabled.

You can enable multirow selection using the following code:

// Turn on multiple-selection mode for the TreeTableView
TreeTableViewSelectionModel<Person> selection = treeTable.getSelectionModel();
selection.setSelectionMode(SelectionMode.MULTIPLE);

The cell-level selection can be enabled by setting the cellSelectionEnabled property of the selection model to true, as shown in the following snippet of code. When the property is set to true, the TreeTableView is put in cell-level selection mode and you cannot select an entire row. If multiple-selection mode is enabled, you can still select all cells in a row. However, the row itself is not reported as selected because the TreeTableView is in the cell-level selection mode. By default, cell-level selection mode is false.

// Enable cell-level selection
selection.setCellSelectionEnabled(true);

The selection model provides information about the selected cells and rows. The isSelected(int rowIndex) method returns true if the row at the specified rowIndex is selected. Use the isSelected(int rowIndex, TableColumn<S,?> column) method to determine if a cell at the specified rowIndex and column is selected. The getModelItem(int rowIndex) method returns the TreeItem for the specified rowIndex.

4.2 The GUI

The following image shows a TreeTableView with a selection model.

A JavaFX TreeTableView with a Selection model
A JavaFX TreeTableView with a Selection model

5. Editing Data in a TableView

5.1 The Code

FxTreeTableViewExample6.java

import java.time.LocalDate;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TextFieldTreeTableCell;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.converter.LocalDateStringConverter;

public class FxTreeTableViewExample6 extends Application
{
	public static void main(String[] args)
	{
		Application.launch(args);
	}

	@Override
	public void start(Stage stage)
	{
		// Create the RootNode
		TreeItem<Person> rootNode = TreeTableUtil.getModel();
		rootNode.setExpanded(true);

		// Create a TreeTableView with a model
		TreeTableView<Person> treeTable = new TreeTableView<Person>(rootNode);
		treeTable.setPrefWidth(400);

		// Must make the TreeTableView editable
		treeTable.setEditable(true);

		// Create Columns with Cell Factories
		TreeTableColumn<Person, String> firstNameColumn = TreeTableUtil.getFirstNameColumn();
		firstNameColumn.setCellFactory(TextFieldTreeTableCell.<Person>forTreeTableColumn());

		TreeTableColumn<Person, String> lastNameColumn = TreeTableUtil.getLastNameColumn();
		lastNameColumn.setCellFactory(TextFieldTreeTableCell.<Person>forTreeTableColumn());

		TreeTableColumn<Person, LocalDate> birthDateColumn = TreeTableUtil.getBirthDateColumn();
		LocalDateStringConverter converter = new LocalDateStringConverter();

		birthDateColumn.setCellFactory(TextFieldTreeTableCell.<Person, LocalDate>forTreeTableColumn(converter));

		// Add Columns to the Tree
		treeTable.getColumns().add(firstNameColumn);
		treeTable.getColumns().add(lastNameColumn);
		treeTable.getColumns().add(birthDateColumn);

		// Create the VBox
		VBox root = new VBox(treeTable);

		// Set the Style-properties of the VBox
		root.setStyle("-fx-padding: 10;" +
				"-fx-border-style: solid inside;" +
				"-fx-border-width: 2;" +
				"-fx-border-insets: 5;" +
				"-fx-border-radius: 5;" +
				"-fx-border-color: blue;");

		// Create the Scene
		Scene scene = new Scene(root);
		// Add the Scene to the Stage
		stage.setScene(scene);
		// Set the Title
		stage.setTitle("Editing Data in a TreeTableView");
		// Display the Stage
		stage.show();
	}
}

A cell in a TreeTableView can be editable. An editable cell switches between editing and nonediting modes. In editing mode, cell data can be modified by the user. In order for a cell to enter editing mode, the TreeTableView, TreeTableColumn, and TreeTableCell must be editable.

All three of them have an editable property, which can be set to true using the setEditable(true) method. By default, TreeTableColumn and TreeTableCell are editable. To make cells editable in a TreeTableView, you need to make the TreeTableView editable, as shown in the following code:

// Must make the TreeTableView editable
treeTable.setEditable(true);

The TreeTableColumn class supports three types of events:

  • onEditStart
  • onEditCommit
  • onEditCancel

The onEditStart event is fired when a cell in the column enters editing mode. The onEditCommit event is fired when the user successfully commits the editing, for example, by pressing the Enter key in a TextField. The onEditCancel event is fired when the user cancels the editing, for example, by pressing the Esc key in a TextField. The events are represented by an object of the TreeTableColumn.CellEditEvent class.

The event object encapsulates the old and new values in the cell, the TreeItem of the model being edited, TreeTableColumn, the TreeTablePosition indicating the cell position where the editing is happening, and the reference of the TreeTableView. Use the methods of the CellEditEvent class to get these values.

Making a TreeTableView editable does not let you edit its cell data. You need to do a little more of a plumbing job before you can edit data in cells. Cell editing capability is provided through specialized implementations of the TreeTableCell class.

JavaFX library provides a few of these implementations. Set the cell factory for a column to use one of the following implementations of the TreeTableCell to edit cell data:

  • CheckBoxTreeTableCell
  • ChoiceBoxTreeTableCell
  • ComboBoxTreeTableCell
  • TextFieldTreeTableCell

The only difference between editing cells in TableView and TreeTableView is the cell classes you will need to use. TableView uses subclasses of TableCell that are named as XxxTableCell. TreeTableView uses subclasses of TreeTableCell that are named as XxxTreeTableCell.

The following snippet of code sets the cell factory for the First Name column to use a TextField to edit data in its cells:

// Create Columns with Cell Factories
TreeTableColumn<Person, String> firstNameColumn = TreeTableUtil.getFirstNameColumn();
firstNameColumn.setCellFactory(TextFieldTreeTableCell.<Person>forTreeTableColumn());

When editing nonstring data in cell, you need to provide a StringConverter.

The following snippet of code sets a cell factory for a Birth Date column with a StringConverter, which converts a String to a LocalDate and vice versa.

The column type is LocalDate. By default, the LocalDateStringConverter assumes a date format of mm/dd/yyyy:

TreeTableColumn<Person, LocalDate> birthDateColumn = TreeTableUtil.getBirthDateColumn();
LocalDateStringConverter converter = new LocalDateStringConverter();

birthDateColumn.setCellFactory(TextFieldTreeTableCell.<Person, LocalDate>forTreeTableColumn(converter));

5.2 The GUI

The following image shows an editable TreeTableView.

An editable JavaFX TreeTableView
An editable JavaFX TreeTableView

6. Adding and Deleting Rows in a TableView

6.1 The Code

FxTreeTableViewExample7.java

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TextFieldTreeTableCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import java.time.LocalDate;
import javafx.scene.control.TreeTableView.TreeTableViewSelectionModel;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;
import javafx.util.converter.LocalDateStringConverter;

public class FxTreeTableViewExample7 extends Application
{
	// Create the TreeTableView
	private final TreeTableView<Person> treeTable = new TreeTableView<>();
	// Create a TextArea
	private final TextArea textarea = new TextArea();


	public static void main(String[] args)
	{
		Application.launch(args);
	}

	@Override
	public void start(Stage stage)
	{
		// Create the Root Node
		TreeItem<Person> rootNode = TreeTableUtil.getModel();
		rootNode.setExpanded(true);

		// Set the Properties of the Root Node
		treeTable.setRoot(rootNode);
		treeTable.setPrefWidth(400);
		treeTable.setEditable(true);
		treeTable.getSelectionModel().selectFirst();

		// Create Columns with Cell Factories
		TreeTableColumn<Person, String> firstNameColumn = TreeTableUtil.getFirstNameColumn();
		firstNameColumn.setCellFactory(TextFieldTreeTableCell.<Person>forTreeTableColumn());

		TreeTableColumn<Person, String> lastNameColumn = TreeTableUtil.getLastNameColumn();
		lastNameColumn.setCellFactory(TextFieldTreeTableCell.<Person>forTreeTableColumn());

		TreeTableColumn<Person, LocalDate> birthDateColumn = TreeTableUtil.getBirthDateColumn();
		LocalDateStringConverter converter = new LocalDateStringConverter();
		birthDateColumn.setCellFactory(TextFieldTreeTableCell.<Person, LocalDate>forTreeTableColumn(converter));

		// Add Columns to The TreeTableView
		treeTable.getColumns().add(firstNameColumn);
		treeTable.getColumns().add(lastNameColumn);
		treeTable.getColumns().add(birthDateColumn);

		// Add a placeholder to the TreeTableView.
		// It is displayed when the root node is deleted.
		treeTable.setPlaceholder(new Label("Click the Add button to add a row."));

		// Create the Label
		Label label = new Label("Please select a row to add/delete.");

		// Create the HBox
		HBox hbox = this.getButtons();

		// Create the VBox
		VBox root = new VBox(label, hbox, treeTable);
		root.setSpacing(10);

		// Create the Scene
		Scene scene = new Scene(root);
		// Add the Scene to the Stage
		stage.setScene(scene);
		// Set the Title
		stage.setTitle("Adding/Deleting Rows in a TreeTableView");
		// Display the Stage
		stage.show();
	}

	private HBox getButtons()
	{
		// Create the Buttons
		Button addButton = new Button("Add");
		Button deleteButton = new Button("Delete");

		// Create EventHandler for the Buttons
		addButton.setOnAction(new EventHandler<ActionEvent>()
		{
			@Override
			public void handle(ActionEvent event)
			{
				addRow();
			}
		});

		deleteButton.setOnAction(new EventHandler<ActionEvent>()
		{
			@Override
			public void handle(ActionEvent event)
			{
				deleteRow();
			}
		});

		// Create and return the HBox
		return new HBox(20, addButton, deleteButton);
	}

	private void addRow()
	{
		if (treeTable.getExpandedItemCount() == 0 )
		{
			// There is no row in the TreeTableView
			addNewRootItem();
		}
		else if (treeTable.getSelectionModel().isEmpty())
		{
			logging("Select a row to add.");
			return;
		}
		else
		{
			// Add Child
			addNewChildItem();
		}
	}

	private void addNewRootItem()
	{
		// Add a root Item
		TreeItem<Person> item = new TreeItem<>(new Person("New", "New", null));
		treeTable.setRoot(item);

		// Edit the item
		this.editItem(item);
	}

	private void addNewChildItem()
	{
		// Prepare a new TreeItem with a new Person object
		TreeItem<Person> item = new TreeItem<>(new Person("New", "New", null));

		// Get the selection model
		TreeTableViewSelectionModel<Person> sm = treeTable.getSelectionModel();

		// Get the selected row index
		int rowIndex = sm.getSelectedIndex();

		// Get the selected TreeItem
		TreeItem<Person> selectedItem = sm.getModelItem(rowIndex);

		// Add the new item as children to the selected item
		selectedItem.getChildren().add(item);

		// Make sure the new item is visible
		selectedItem.setExpanded(true);

		// Edit the item
		this.editItem(item);
	}

	private void editItem(TreeItem<Person> item)
	{
		// Scroll to the new item
		int newRowIndex = treeTable.getRow(item);
		treeTable.scrollTo(newRowIndex);

		// Put the first column in editing mode
		TreeTableColumn<Person, ?> firstCol = treeTable.getColumns().get(0);
		treeTable.getSelectionModel().select(item);
		treeTable.getFocusModel().focus(newRowIndex, firstCol);
		treeTable.edit(newRowIndex, firstCol);
	}

	private void deleteRow()
	{
		// Get the selection model
		TreeTableViewSelectionModel<Person> sm = treeTable.getSelectionModel();
		if (sm.isEmpty())
		{
			logging("Select a row to delete.");
			return;
		}

		// Get the selected Entry
		int rowIndex = sm.getSelectedIndex();
		TreeItem<Person> selectedItem = sm.getModelItem(rowIndex);
		TreeItem<Person> parent = selectedItem.getParent();

		if (parent != null)
		{
			// Remove the Item
			parent.getChildren().remove(selectedItem);
		}
		else
		{
			// Must be deleting the Root Item
			treeTable.setRoot(null);
		}
	}

	private void logging(String message)
	{
		this.textarea.appendText(message + "\n");
	}
}

Each row in a TreeTableView is represented by a TreeItem in its model. Adding and deleting a row in a TreeTableView is as simple as adding and deleting TreeItems in the model.

The above class shows how to add and delete rows. It displays a prebuilt family hierarchy in a TreeTableView along with Add and Delete buttons. Clicking the Add button adds a new row as a child row for the selected row. If there is no row, a new root item is added to the tree.

The new row is selected, scrolled to the view, and put in editing mode. The addRow() method contains the logic for adding a row. The Delete button deletes the selected row. Notice that all child rows of the selected row are deleted.

6.2 The GUI

The following image shows a TreeTableView, where rows can be added or removed.

A JavaFX TreeTableView with adding / deleting rows

7. Download the Source code

This was an example of javafx.scene.control.TreeTableView

Download
You can download the full source code of this example here: JavaFX TreeTableView Example

Andreas Pomarolli

Andreas has graduated from Computer Science and Bioinformatics at the University of Linz. During his studies he has been involved with a large number of research projects ranging from software engineering to data engineering and at least web engineering. His scientific focus includes the areas of software engineering, data engineering, web engineering and project management. He currently works as a software engineer in the IT sector where he is mainly involved with projects based on Java, Databases and Web Technologies.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button