/*
* Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.scene.control;
import com.sun.javafx.scene.control.Properties;
import javafx.scene.control.skin.NestedTableColumnHeader;
import javafx.scene.control.skin.TableColumnHeader;
import javafx.scene.control.skin.TableHeaderRow;
import javafx.scene.control.skin.TableViewSkin;
import javafx.scene.control.skin.TableViewSkinBase;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.css.CssMetaData;
import javafx.css.Styleable;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventTarget;
import javafx.event.EventType;
import javafx.scene.Node;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.util.Callback;
import javafx.collections.WeakListChangeListener;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.value.ObservableValue;
import javafx.beans.value.WritableValue;
A TableView
is made up of a number of TableColumn instances. Each TableColumn in a table is responsible for displaying (and editing) the contents of that column. As well as being responsible for displaying and editing data for a single column, a TableColumn also contains the necessary properties to:
- Be resized (using
minWidth
/prefWidth
/maxWidth
and width
properties) - Have its
visibility
toggled - Display
header text
- Display any
nested columns
it may contain - Have a
context menu
when the user right-clicks the column header area - Have the contents of the table be sorted (using
comparator
, sortable
and sortType
)
When creating a TableColumn instance, perhaps the two most important properties to set are the column text
(what to show in the column header area), and the column cell value factory
(which is used to populate individual cells in the column). This can be achieved using some variation on the following code:
ObservableList<Person> data = ...
TableView<Person> tableView = new TableView<Person>(data);
TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
// p.getValue() returns the Person instance for a particular TableView row
return p.getValue().firstNameProperty();
}
});
tableView.getColumns().add(firstNameCol);}
This approach assumes that the object returned from p.getValue()
has a JavaFX ObservableValue
that can simply be returned. The benefit of this is that the TableView will internally create bindings to ensure that, should the returned ObservableValue
change, the cell contents will be automatically refreshed. In situations where a TableColumn must interact with classes created before JavaFX, or that generally do not wish to use JavaFX apis for properties, it is possible to wrap the returned value in a ReadOnlyObjectWrapper
instance. For example:
firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
return new ReadOnlyObjectWrapper(p.getValue().getFirstName());
}
});
It is hoped that over time there will be convenience cell value factories developed and made available to developers. As of the JavaFX 2.0 release, there is one such convenience class: PropertyValueFactory
. This class removes the need to write the code above, instead relying on reflection to look up a given property from a String. Refer to the PropertyValueFactory
class documentation for more information on how to use this with a TableColumn. Finally, for more detail on how to use TableColumn, there is further documentation in the TableView
class documentation. Type parameters: See Also: Since: JavaFX 2.0
/**
* A {@link TableView} is made up of a number of TableColumn instances. Each
* TableColumn in a table is responsible for displaying (and editing) the contents
* of that column. As well as being responsible for displaying and editing data
* for a single column, a TableColumn also contains the necessary properties to:
* <ul>
* <li>Be resized (using {@link #minWidthProperty() minWidth}/{@link #prefWidthProperty() prefWidth}/{@link #maxWidthProperty() maxWidth}
* and {@link #widthProperty() width} properties)
* <li>Have its {@link #visibleProperty() visibility} toggled
* <li>Display {@link #textProperty() header text}
* <li>Display any {@link #getColumns() nested columns} it may contain
* <li>Have a {@link #contextMenuProperty() context menu} when the user
* right-clicks the column header area
* <li>Have the contents of the table be sorted (using
* {@link #comparatorProperty() comparator}, {@link #sortableProperty() sortable} and
* {@link #sortTypeProperty() sortType})
* </ul>
*
* When creating a TableColumn instance, perhaps the two most important properties
* to set are the column {@link #textProperty() text} (what to show in the column
* header area), and the column {@link #cellValueFactoryProperty() cell value factory}
* (which is used to populate individual cells in the column). This can be
* achieved using some variation on the following code:
*
* <pre>
* {@code
* ObservableList<Person> data = ...
* TableView<Person> tableView = new TableView<Person>(data);
*
* TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
* firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
* public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
* // p.getValue() returns the Person instance for a particular TableView row
* return p.getValue().firstNameProperty();
* }
* });
* }
* tableView.getColumns().add(firstNameCol);}</pre>
*
* This approach assumes that the object returned from <code>p.getValue()</code>
* has a JavaFX {@link ObservableValue} that can simply be returned. The benefit of this
* is that the TableView will internally create bindings to ensure that,
* should the returned {@link ObservableValue} change, the cell contents will be
* automatically refreshed.
*
* <p>In situations where a TableColumn must interact with classes created before
* JavaFX, or that generally do not wish to use JavaFX apis for properties, it is
* possible to wrap the returned value in a {@link ReadOnlyObjectWrapper} instance. For
* example:
*
* <pre>
* {@code
* firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
* public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
* return new ReadOnlyObjectWrapper(p.getValue().getFirstName());
* }
* });}</pre>
*
* It is hoped that over time there will be convenience cell value factories
* developed and made available to developers. As of the JavaFX 2.0 release,
* there is one such convenience class: {@link PropertyValueFactory}. This class
* removes the need to write the code above, instead relying on reflection to
* look up a given property from a String. Refer to the
* <code>PropertyValueFactory</code> class documentation for more information
* on how to use this with a TableColumn.
*
* Finally, for more detail on how to use TableColumn, there is further documentation in
* the {@link TableView} class documentation.
*
* @param <S> The type of the TableView generic type (i.e. S == TableView<S>)
* @param <T> The type of the content in all cells in this TableColumn.
* @see TableView
* @see TableCell
* @see TablePosition
* @since JavaFX 2.0
*/
public class TableColumn<S,T> extends TableColumnBase<S,T> implements EventTarget {
/***************************************************************************
* *
* Static properties and methods *
* *
**************************************************************************/
Parent event for any TableColumn edit event.
Type parameters: - <S> – The type of the TableView generic type
- <T> – The type of the content in all cells in this TableColumn
Returns: The any TableColumn edit event
/**
* Parent event for any TableColumn edit event.
* @param <S> The type of the TableView generic type
* @param <T> The type of the content in all cells in this TableColumn
* @return The any TableColumn edit event
*/
@SuppressWarnings("unchecked")
public static <S,T> EventType<CellEditEvent<S,T>> editAnyEvent() {
return (EventType<CellEditEvent<S,T>>) EDIT_ANY_EVENT;
}
private static final EventType<?> EDIT_ANY_EVENT =
new EventType<>(Event.ANY, "TABLE_COLUMN_EDIT");
Indicates that the user has performed some interaction to start an edit event, or alternatively the TableView.edit(int, TableColumn)
method has been called. Type parameters: Returns: The start an edit event
/**
* Indicates that the user has performed some interaction to start an edit
* event, or alternatively the {@link TableView#edit(int, javafx.scene.control.TableColumn)}
* method has been called.
* @param <S> The type of the TableView generic type
* @param <T> The type of the content in all cells in this TableColumn
* @return The start an edit event
*/
@SuppressWarnings("unchecked")
public static <S,T> EventType<CellEditEvent<S,T>> editStartEvent() {
return (EventType<CellEditEvent<S,T>>) EDIT_START_EVENT;
}
private static final EventType<?> EDIT_START_EVENT =
new EventType<>(editAnyEvent(), "EDIT_START");
Indicates that the editing has been canceled, meaning that no change should
be made to the backing data source.
Type parameters: - <S> – The type of the TableView generic type
- <T> – The type of the content in all cells in this TableColumn
Returns: The cancel an edit event
/**
* Indicates that the editing has been canceled, meaning that no change should
* be made to the backing data source.
* @param <S> The type of the TableView generic type
* @param <T> The type of the content in all cells in this TableColumn
* @return The cancel an edit event
*/
@SuppressWarnings("unchecked")
public static <S,T> EventType<CellEditEvent<S,T>> editCancelEvent() {
return (EventType<CellEditEvent<S,T>>) EDIT_CANCEL_EVENT;
}
private static final EventType<?> EDIT_CANCEL_EVENT =
new EventType<>(editAnyEvent(), "EDIT_CANCEL");
Indicates that the editing has been committed by the user, meaning that
a change should be made to the backing data source to reflect the new
data.
Type parameters: - <S> – The type of the TableView generic type
- <T> – The type of the content in all cells in this TableColumn
Returns: The commit an edit event
/**
* Indicates that the editing has been committed by the user, meaning that
* a change should be made to the backing data source to reflect the new
* data.
* @param <S> The type of the TableView generic type
* @param <T> The type of the content in all cells in this TableColumn
* @return The commit an edit event
*/
@SuppressWarnings("unchecked")
public static <S,T> EventType<CellEditEvent<S,T>> editCommitEvent() {
return (EventType<CellEditEvent<S,T>>) EDIT_COMMIT_EVENT;
}
private static final EventType<?> EDIT_COMMIT_EVENT =
new EventType<>(editAnyEvent(), "EDIT_COMMIT");
If no cellFactory is specified on a TableColumn instance, then this one will be used by default. At present it simply renders the TableCell item property within the graphic
property if the item
is a Node, or it simply calls toString()
if it is not null, setting the resulting string inside the text
property. /**
* If no cellFactory is specified on a TableColumn instance, then this one
* will be used by default. At present it simply renders the TableCell item
* property within the {@link TableCell#graphicProperty() graphic} property
* if the {@link Cell#item item} is a Node, or it simply calls
* <code>toString()</code> if it is not null, setting the resulting string
* inside the {@link Cell#textProperty() text} property.
*/
public static final Callback<TableColumn<?,?>, TableCell<?,?>> DEFAULT_CELL_FACTORY =
new Callback<TableColumn<?,?>, TableCell<?,?>>() {
@Override public TableCell<?,?> call(TableColumn<?,?> param) {
return new TableCell<Object,Object>() {
@Override protected void updateItem(Object item, boolean empty) {
if (item == getItem()) return;
super.updateItem(item, empty);
if (item == null) {
super.setText(null);
super.setGraphic(null);
} else if (item instanceof Node) {
super.setText(null);
super.setGraphic((Node)item);
} else {
super.setText(item.toString());
super.setGraphic(null);
}
}
};
}
};
/***************************************************************************
* *
* Constructors *
* *
**************************************************************************/
Creates a default TableColumn with default cell factory, comparator, and
onEditCommit implementation.
/**
* Creates a default TableColumn with default cell factory, comparator, and
* onEditCommit implementation.
*/
public TableColumn() {
getStyleClass().add(DEFAULT_STYLE_CLASS);
setOnEditCommit(DEFAULT_EDIT_COMMIT_HANDLER);
// we listen to the columns list here to ensure that widths are
// maintained properly, and to also set the column hierarchy such that
// all children columns know that this TableColumn is their parent.
getColumns().addListener(weakColumnsListener);
tableViewProperty().addListener(observable -> {
// set all children of this tableView to have the same TableView
// as this column
for (TableColumn<S, ?> tc : getColumns()) {
tc.setTableView(getTableView());
}
// This code was commented out due to RT-22391, with this enabled
// the parent column will be null, which is not desired
// // set the parent of this column to also have this tableView
// if (getParentColumn() != null) {
// getParentColumn().setTableView(getTableView());
// }
});
}
Creates a TableColumn with the text set to the provided string, with
default cell factory, comparator, and onEditCommit implementation.
Params: - text – The string to show when the TableColumn is placed within the TableView.
/**
* Creates a TableColumn with the text set to the provided string, with
* default cell factory, comparator, and onEditCommit implementation.
* @param text The string to show when the TableColumn is placed within the TableView.
*/
public TableColumn(String text) {
this();
setText(text);
}
*
Listeners *
*
/***************************************************************************
* *
* Listeners *
* *
**************************************************************************/
private EventHandler<CellEditEvent<S,T>> DEFAULT_EDIT_COMMIT_HANDLER = t -> {
int index = t.getTablePosition().getRow();
List<S> list = t.getTableView().getItems();
if (list == null || index < 0 || index >= list.size()) return;
S rowData = list.get(index);
ObservableValue<T> ov = getCellObservableValue(rowData);
if (ov instanceof WritableValue) {
((WritableValue)ov).setValue(t.getNewValue());
}
};
private ListChangeListener<TableColumn<S,?>> columnsListener = c -> {
while (c.next()) {
// update the TableColumn.tableView property
for (TableColumn<S,?> tc : c.getRemoved()) {
// Fix for RT-16978. In TableColumnHeader we add before we
// remove when moving a TableColumn. This means that for
// a very brief moment the tc is duplicated, and we can prevent
// nulling out the tableview and parent column. Without this
// here, in a very special circumstance it is possible to null
// out the entire content of a column by reordering and then
// sorting another column.
if (getColumns().contains(tc)) continue;
tc.setTableView(null);
tc.setParentColumn(null);
}
for (TableColumn<S,?> tc : c.getAddedSubList()) {
tc.setTableView(getTableView());
}
updateColumnWidths();
}
};
private WeakListChangeListener<TableColumn<S,?>> weakColumnsListener =
new WeakListChangeListener<TableColumn<S,?>>(columnsListener);
*
Instance Variables *
*
/***************************************************************************
* *
* Instance Variables *
* *
**************************************************************************/
// Contains any children columns that should be nested within this column
private final ObservableList<TableColumn<S,?>> columns = FXCollections.<TableColumn<S,?>>observableArrayList();
/***************************************************************************
* *
* Properties *
* *
**************************************************************************/
// --- TableView
The TableView that this TableColumn belongs to.
/**
* The TableView that this TableColumn belongs to.
*/
private ReadOnlyObjectWrapper<TableView<S>> tableView = new ReadOnlyObjectWrapper<TableView<S>>(this, "tableView");
public final ReadOnlyObjectProperty<TableView<S>> tableViewProperty() {
return tableView.getReadOnlyProperty();
}
final void setTableView(TableView<S> value) { tableView.set(value); }
public final TableView<S> getTableView() { return tableView.get(); }
// --- Cell value factory
The cell value factory needs to be set to specify how to populate all cells within a single TableColumn. A cell value factory is a Callback
that provides a CellDataFeatures
instance, and expects an ObservableValue
to be returned. The returned ObservableValue instance will be observed internally to allow for immediate updates to the value to be reflected on screen. An example of how to set a cell value factory is:
lastNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
// p.getValue() returns the Person instance for a particular TableView row
return p.getValue().lastNameProperty();
}
});
}
A common approach is to want to populate cells in a TableColumn using a single value from a Java bean. To support this common scenario, there is the PropertyValueFactory
class. Refer to this class for more information on how to use it, but briefly here is how the above use case could be simplified using the PropertyValueFactory class:
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("lastName"));
See Also:
/**
* The cell value factory needs to be set to specify how to populate all
* cells within a single TableColumn. A cell value factory is a {@link Callback}
* that provides a {@link CellDataFeatures} instance, and expects an
* {@link ObservableValue} to be returned. The returned ObservableValue instance
* will be observed internally to allow for immediate updates to the value
* to be reflected on screen.
*
* An example of how to set a cell value factory is:
*
* <pre><code>
* lastNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
* public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
* // p.getValue() returns the Person instance for a particular TableView row
* return p.getValue().lastNameProperty();
* }
* });
* }
* </code></pre>
*
* A common approach is to want to populate cells in a TableColumn using
* a single value from a Java bean. To support this common scenario, there
* is the {@link PropertyValueFactory} class. Refer to this class for more
* information on how to use it, but briefly here is how the above use case
* could be simplified using the PropertyValueFactory class:
*
* <pre><code>
* lastNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("lastName"));
* </code></pre>
*
* @see PropertyValueFactory
*/
private ObjectProperty<Callback<CellDataFeatures<S,T>, ObservableValue<T>>> cellValueFactory;
public final void setCellValueFactory(Callback<CellDataFeatures<S,T>, ObservableValue<T>> value) {
cellValueFactoryProperty().set(value);
}
public final Callback<CellDataFeatures<S,T>, ObservableValue<T>> getCellValueFactory() {
return cellValueFactory == null ? null : cellValueFactory.get();
}
public final ObjectProperty<Callback<CellDataFeatures<S,T>, ObservableValue<T>>> cellValueFactoryProperty() {
if (cellValueFactory == null) {
cellValueFactory = new SimpleObjectProperty<Callback<CellDataFeatures<S,T>, ObservableValue<T>>>(this, "cellValueFactory");
}
return cellValueFactory;
}
// --- Cell Factory
The cell factory for all cells in this column. The cell factory
is responsible for rendering the data contained within each TableCell for
a single table column.
By default TableColumn uses the default cell
factory
, but this can be replaced with a custom implementation, for example to show data in a different way or to support editing.There is a lot of documentation on creating custom cell factories elsewhere (see Cell
and TableView
for example).
Finally, there are a number of pre-built cell factories available in the cell
package.
/**
* The cell factory for all cells in this column. The cell factory
* is responsible for rendering the data contained within each TableCell for
* a single table column.
*
* <p>By default TableColumn uses the {@link #DEFAULT_CELL_FACTORY default cell
* factory}, but this can be replaced with a custom implementation, for
* example to show data in a different way or to support editing.There is a
* lot of documentation on creating custom cell factories
* elsewhere (see {@link Cell} and {@link TableView} for example).</p>
*
* <p>Finally, there are a number of pre-built cell factories available in the
* {@link javafx.scene.control.cell} package.
*/
private final ObjectProperty<Callback<TableColumn<S,T>, TableCell<S,T>>> cellFactory =
new SimpleObjectProperty<Callback<TableColumn<S,T>, TableCell<S,T>>>(
this, "cellFactory", (Callback<TableColumn<S,T>, TableCell<S,T>>) ((Callback) DEFAULT_CELL_FACTORY)) {
@Override protected void invalidated() {
TableView<S> table = getTableView();
if (table == null) return;
Map<Object,Object> properties = table.getProperties();
if (properties.containsKey(Properties.RECREATE)) {
properties.remove(Properties.RECREATE);
}
properties.put(Properties.RECREATE, Boolean.TRUE);
}
};
public final void setCellFactory(Callback<TableColumn<S,T>, TableCell<S,T>> value) {
cellFactory.set(value);
}
public final Callback<TableColumn<S,T>, TableCell<S,T>> getCellFactory() {
return cellFactory.get();
}
public final ObjectProperty<Callback<TableColumn<S,T>, TableCell<S,T>>> cellFactoryProperty() {
return cellFactory;
}
// --- Sort Type
Used to state whether this column, if it is part of a sort order (see TableView.getSortOrder()
for more details), should be sorted in ascending or descending order. Simply toggling this property will result in the sort order changing in the TableView, assuming of course that this column is in the sortOrder ObservableList to begin with. /**
* Used to state whether this column, if it is part of a sort order (see
* {@link TableView#getSortOrder()} for more details), should be sorted in
* ascending or descending order.
* Simply toggling this property will result in the sort order changing in
* the TableView, assuming of course that this column is in the
* sortOrder ObservableList to begin with.
*/
private ObjectProperty<SortType> sortType;
public final ObjectProperty<SortType> sortTypeProperty() {
if (sortType == null) {
sortType = new SimpleObjectProperty<SortType>(this, "sortType", SortType.ASCENDING);
}
return sortType;
}
public final void setSortType(SortType value) {
sortTypeProperty().set(value);
}
public final SortType getSortType() {
return sortType == null ? SortType.ASCENDING : sortType.get();
}
// --- On Edit Start
private ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditStart;
public final void setOnEditStart(EventHandler<CellEditEvent<S,T>> value) {
onEditStartProperty().set(value);
}
public final EventHandler<CellEditEvent<S,T>> getOnEditStart() {
return onEditStart == null ? null : onEditStart.get();
}
This event handler will be fired when the user successfully initiates
editing.
Returns: the on edit start property
/**
* This event handler will be fired when the user successfully initiates
* editing.
* @return the on edit start property
*/
public final ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditStartProperty() {
if (onEditStart == null) {
onEditStart = new SimpleObjectProperty<EventHandler<CellEditEvent<S,T>>>(this, "onEditStart") {
@Override protected void invalidated() {
eventHandlerManager.setEventHandler(TableColumn.<S,T>editStartEvent(), get());
}
};
}
return onEditStart;
}
// --- On Edit Commit
private ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditCommit;
public final void setOnEditCommit(EventHandler<CellEditEvent<S,T>> value) {
onEditCommitProperty().set(value);
}
public final EventHandler<CellEditEvent<S,T>> getOnEditCommit() {
return onEditCommit == null ? null : onEditCommit.get();
}
This event handler will be fired when the user successfully commits their
editing.
Returns: the on edit commit property
/**
* This event handler will be fired when the user successfully commits their
* editing.
* @return the on edit commit property
*/
public final ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditCommitProperty() {
if (onEditCommit == null) {
onEditCommit = new SimpleObjectProperty<EventHandler<CellEditEvent<S,T>>>(this, "onEditCommit") {
@Override protected void invalidated() {
eventHandlerManager.setEventHandler(TableColumn.<S,T>editCommitEvent(), get());
}
};
}
return onEditCommit;
}
// --- On Edit Cancel
private ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditCancel;
public final void setOnEditCancel(EventHandler<CellEditEvent<S,T>> value) {
onEditCancelProperty().set(value);
}
public final EventHandler<CellEditEvent<S,T>> getOnEditCancel() {
return onEditCancel == null ? null : onEditCancel.get();
}
This event handler will be fired when the user cancels editing a cell.
Returns: the on edit cancel property
/**
* This event handler will be fired when the user cancels editing a cell.
* @return the on edit cancel property
*/
public final ObjectProperty<EventHandler<CellEditEvent<S,T>>> onEditCancelProperty() {
if (onEditCancel == null) {
onEditCancel = new SimpleObjectProperty<EventHandler<CellEditEvent<S, T>>>(this, "onEditCancel") {
@Override protected void invalidated() {
eventHandlerManager.setEventHandler(TableColumn.<S,T>editCancelEvent(), get());
}
};
}
return onEditCancel;
}
/***************************************************************************
* *
* Public API *
* *
**************************************************************************/
{@inheritDoc} /** {@inheritDoc} */
@Override public final ObservableList<TableColumn<S,?>> getColumns() {
return columns;
}
{@inheritDoc} /** {@inheritDoc} */
@Override public final ObservableValue<T> getCellObservableValue(int index) {
if (index < 0) return null;
// Get the table
final TableView<S> table = getTableView();
if (table == null || table.getItems() == null) return null;
// Get the rowData
final List<S> items = table.getItems();
if (index >= items.size()) return null; // Out of range
final S rowData = items.get(index);
return getCellObservableValue(rowData);
}
{@inheritDoc} /** {@inheritDoc} */
@Override public final ObservableValue<T> getCellObservableValue(S item) {
// Get the factory
final Callback<CellDataFeatures<S,T>, ObservableValue<T>> factory = getCellValueFactory();
if (factory == null) return null;
// Get the table
final TableView<S> table = getTableView();
if (table == null) return null;
// Call the factory
final CellDataFeatures<S,T> cdf = new CellDataFeatures<S,T>(table, this, item);
return factory.call(cdf);
}
*
Stylesheet Handling *
*
/***************************************************************************
* *
* Stylesheet Handling *
* *
**************************************************************************/
private static final String DEFAULT_STYLE_CLASS = "table-column";
{@inheritDoc}
Returns: "TableColumn" Since: JavaFX 8.0
/**
* {@inheritDoc}
* @return "TableColumn"
* @since JavaFX 8.0
*/
@Override
public String getTypeSelector() {
return "TableColumn";
}
{@inheritDoc}
Returns: getTableView()
Since: JavaFX 8.0
/**
* {@inheritDoc}
* @return {@code getTableView()}
* @since JavaFX 8.0
*/
@Override
public Styleable getStyleableParent() {
return getTableView(); }
{@inheritDoc}
Since: JavaFX 8.0
/**
* {@inheritDoc}
* @since JavaFX 8.0
*/
@Override
public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
return getClassCssMetaData();
}
Returns: The CssMetaData associated with this class, which may include the
CssMetaData of its superclasses. Since: JavaFX 8.0
/**
* @return The CssMetaData associated with this class, which may include the
* CssMetaData of its superclasses.
* @since JavaFX 8.0
*/
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return Collections.emptyList();
}
{@inheritDoc} /** {@inheritDoc} */
@Override public Node getStyleableNode() {
if (! (getTableView().getSkin() instanceof TableViewSkin)) return null;
TableViewSkin<?> skin = (TableViewSkin<?>) getTableView().getSkin();
TableHeaderRow tableHeader = null;
for (Node n : skin.getChildren()) {
if (n instanceof TableHeaderRow) {
tableHeader = (TableHeaderRow)n;
}
}
NestedTableColumnHeader rootHeader = null;
for (Node n : tableHeader.getChildren()) {
if (n instanceof NestedTableColumnHeader) {
rootHeader = (NestedTableColumnHeader) n;
}
}
// we now need to do a search for the header. We'll go depth-first.
return scan(rootHeader);
}
private TableColumnHeader scan(TableColumnHeader header) {
// firstly test that the parent isn't what we are looking for
if (TableColumn.this.equals(header.getTableColumn())) {
return header;
}
if (header instanceof NestedTableColumnHeader) {
NestedTableColumnHeader parent = (NestedTableColumnHeader) header;
for (int i = 0; i < parent.getColumnHeaders().size(); i++) {
TableColumnHeader result = scan(parent.getColumnHeaders().get(i));
if (result != null) {
return result;
}
}
}
return null;
}
/***************************************************************************
* *
* Support Interfaces *
* *
**************************************************************************/
A support class used in TableColumn as a wrapper class to provide all necessary information for a particular Cell
. Once instantiated, this class is immutable. Type parameters: Since: JavaFX 2.0
/**
* A support class used in TableColumn as a wrapper class
* to provide all necessary information for a particular {@link Cell}. Once
* instantiated, this class is immutable.
*
* @param <S> The TableView type
* @param <T> The TableColumn type
* @since JavaFX 2.0
*/
public static class CellDataFeatures<S,T> {
private final TableView<S> tableView;
private final TableColumn<S,T> tableColumn;
private final S value;
Instantiates a CellDataFeatures instance with the given properties
set as read-only values of this instance.
Params: - tableView – The TableView that this instance refers to.
- tableColumn – The TableColumn that this instance refers to.
- value – The value for a row in the TableView.
/**
* Instantiates a CellDataFeatures instance with the given properties
* set as read-only values of this instance.
*
* @param tableView The TableView that this instance refers to.
* @param tableColumn The TableColumn that this instance refers to.
* @param value The value for a row in the TableView.
*/
public CellDataFeatures(TableView<S> tableView,
TableColumn<S,T> tableColumn, S value) {
this.tableView = tableView;
this.tableColumn = tableColumn;
this.value = value;
}
Returns the value passed in to the constructor.
Returns: the value passed in to the constructor
/**
* Returns the value passed in to the constructor.
* @return the value passed in to the constructor
*/
public S getValue() {
return value;
}
Returns the TableColumn
passed in to the constructor. Returns: the TableColumn passed in to the constructor
/**
* Returns the {@link TableColumn} passed in to the constructor.
* @return the TableColumn passed in to the constructor
*/
public TableColumn<S,T> getTableColumn() {
return tableColumn;
}
Returns the TableView
passed in to the constructor. Returns: the TableView passed in to the constructor
/**
* Returns the {@link TableView} passed in to the constructor.
* @return the TableView passed in to the constructor
*/
public TableView<S> getTableView() {
return tableView;
}
}
An event that is fired when a user performs an edit on a table cell.
Type parameters: - <S> – The type of the TableView generic type
- <T> – The type of the content in all cells in this TableColumn
Since: JavaFX 2.0
/**
* An event that is fired when a user performs an edit on a table cell.
* @param <S> The type of the TableView generic type
* @param <T> The type of the content in all cells in this TableColumn
* @since JavaFX 2.0
*/
public static class CellEditEvent<S,T> extends Event {
private static final long serialVersionUID = -609964441682677579L;
Common supertype for all cell edit event types.
Since: JavaFX 8.0
/**
* Common supertype for all cell edit event types.
* @since JavaFX 8.0
*/
public static final EventType<?> ANY = EDIT_ANY_EVENT;
// represents the new value input by the end user. This is NOT the value
// to go back into the TableView.items list - this new value represents
// just the input for a single cell, so it is likely that it needs to go
// back into a property within an item in the TableView.items list.
private final T newValue;
// The location of the edit event
private transient final TablePosition<S,T> pos;
Creates a new event that can be subsequently fired to the relevant listeners.
Params: - table – The TableView on which this event occurred.
- pos – The position upon which this event occurred.
- eventType – The type of event that occurred.
- newValue – The value input by the end user.
/**
* Creates a new event that can be subsequently fired to the relevant listeners.
*
* @param table The TableView on which this event occurred.
* @param pos The position upon which this event occurred.
* @param eventType The type of event that occurred.
* @param newValue The value input by the end user.
*/
public CellEditEvent(TableView<S> table, TablePosition<S,T> pos,
EventType<CellEditEvent<S,T>> eventType, T newValue) {
super(table, Event.NULL_SOURCE_TARGET, eventType);
if (table == null) {
throw new NullPointerException("TableView can not be null");
}
this.pos = pos;
this.newValue = newValue;
}
Returns the TableView upon which this event occurred.
Returns: The TableView control upon which this event occurred.
/**
* Returns the TableView upon which this event occurred.
* @return The TableView control upon which this event occurred.
*/
public TableView<S> getTableView() {
return pos.getTableView();
}
Returns the TableColumn upon which this event occurred.
Returns: The TableColumn that the edit occurred in.
/**
* Returns the TableColumn upon which this event occurred.
*
* @return The TableColumn that the edit occurred in.
*/
public TableColumn<S,T> getTableColumn() {
return pos.getTableColumn();
}
Returns the position upon which this event occurred.
Returns: The position upon which this event occurred.
/**
* Returns the position upon which this event occurred.
* @return The position upon which this event occurred.
*/
public TablePosition<S,T> getTablePosition() {
return pos;
}
Returns the new value input by the end user. This is not the value
to go back into the TableView.items list - this new value represents
just the input for a single cell, so it is likely that it needs to go
back into a property within an item in the TableView.items list.
Returns: An Object representing the new value input by the user.
/**
* Returns the new value input by the end user. This is <b>not</b> the value
* to go back into the TableView.items list - this new value represents
* just the input for a single cell, so it is likely that it needs to go
* back into a property within an item in the TableView.items list.
*
* @return An Object representing the new value input by the user.
*/
public T getNewValue() {
return newValue;
}
Attempts to return the old value at the position referred to in the TablePosition returned by getTablePosition()
. This may return null for a number of reasons. Returns: Returns the value stored in the position being edited, or null
if it can not be retrieved.
/**
* Attempts to return the old value at the position referred to in the
* TablePosition returned by {@link #getTablePosition()}. This may return
* null for a number of reasons.
*
* @return Returns the value stored in the position being edited, or null
* if it can not be retrieved.
*/
public T getOldValue() {
S rowData = getRowValue();
if (rowData == null || pos.getTableColumn() == null) {
return null;
}
// if we are here, we now need to get the data for the specific column
return (T) pos.getTableColumn().getCellData(rowData);
}
Convenience method that returns the value for the row (that is, from the TableView items
list), for the row contained within the TablePosition
returned in getTablePosition()
. Returns: the value for the row
/**
* Convenience method that returns the value for the row (that is, from
* the TableView {@link TableView#itemsProperty() items} list), for the
* row contained within the {@link TablePosition} returned in
* {@link #getTablePosition()}.
* @return the value for the row
*/
public S getRowValue() {
List<S> items = getTableView().getItems();
if (items == null) return null;
int row = pos.getRow();
if (row < 0 || row >= items.size()) return null;
return items.get(row);
}
}
Enumeration that specifies the type of sorting being applied to a specific
column.
Since: JavaFX 2.0
/**
* Enumeration that specifies the type of sorting being applied to a specific
* column.
* @since JavaFX 2.0
*/
public static enum SortType {
Column will be sorted in an ascending order.
/**
* Column will be sorted in an ascending order.
*/
ASCENDING,
Column will be sorted in a descending order.
/**
* Column will be sorted in a descending order.
*/
DESCENDING;
// UNSORTED
}
}