UI Toolbox logoUI Toolbox

DataGrid

DataGrid in UIToolbox shows a list of items in a table with typed cell editors. It wraps the native MultiColumnListView, so the rows are virtual: only the visible rows have elements.

Video tutorial: Data grid. All videos: Video tutorials.

<tb:DataGrid name="units" selection-mode="Multiple" style="height: 250px;">
    <tb:DataGridColumn header="Name" binding-path="Name" width="130" />
    <tb:DataGridColumn header="Level" binding-path="Level" width="70" />
    <tb:DataGridColumn header="Class" binding-path="Class" width="100" />
    <tb:DataGridColumn header="Action" cell-type="Button" button-text="Heal" width="70" sortable="false" />
</tb:DataGrid>
var grid = root.Q<DataGrid>("units");
grid.ItemsSource = units;   // a List<Unit>; the edits write to the items
grid.CellValueChanged += (i, column, old, now) => Debug.Log($"{units[i].Name}: {column.Header} {old} to {now}");
grid.CellClicked += (i, column) => grid.SetValue(i, grid.GetColumn("Health")!, 100f);
grid.SetFilter("Level", level => (int)level! >= 10);
grid.SearchText = "ash";
GUIUtility.systemCopyBuffer = grid.ToCsv(selectedOnly: true);

A grid with a check box column, pages of 25 rows with page buttons, and a footer row:

<tb:DataGrid name="units" selection-column="true" page-size="25" show-pagination="true" show-footer="true">
    <tb:DataGridColumn header="Name" binding-path="Name" width="130" can-hide="false" footer-text="Total" />
    <tb:DataGridColumn header="Level" binding-path="Level" width="70" footer-text="Avg " />
    <tb:DataGridColumn header="Notes" binding-path="Notes" width="200" visible="false" />
</tb:DataGrid>
grid.SetFooter("Level", rows => rows.Count == 0 ? "" : rows.Average(i => units[i].Level).ToString("F1"));
columnsButton.clicked += () => grid.ShowColumnMenu(new Vector2(columnsButton.worldBound.xMin, columnsButton.worldBound.yMax));
grid.VisibleColumnsChanged += () => PlayerPrefs.SetString("columns", string.Join(",", grid.VisibleColumns));
grid.SetVisibleColumns(PlayerPrefs.GetString("columns", "Name,Level").Split(','));
Member Description
DataGridColumn.Header (header), BindingPath (binding-path), Width (width, 100), Sortable (sortable, true) The header text, the public field or property of the items (empty for a column without a value), the start width, and the header click sort.
DataGridColumn.CellType (cell-type) Auto (the default) picks the editor from the member type: Toggle for bool, Integer for int and long, Float for float, double and decimal, Dropdown for an enum or a string with Choices, Text for a string, and Label for other types. Button sends CellClicked.
DataGridColumn.ReadOnly (read-only), Format (format), Choices (choices), ButtonText (button-text) A label instead of an editor; the number or date format of a label; the comma list of a string dropdown; the button text (empty shows the value).
DataGridColumn.Visible (visible, true), CanHide (can-hide, true) The column shows. A column with CanHide off is not in the column menu.
DataGridColumn.FooterText (footer-text) The text of the footer cell of the column. Set it before ShowFooter, or call Refresh().
DataGridColumn.Name, Member, ValueType, CanWrite, ResolvedCellType, FormatValue The binding path or the header, the member after the grid has an item type, and the editor in use. A member without a public setter gets a label.
ItemsSource, ItemType, Refresh The items (an IList). A struct item is set back in the list. Call Refresh() after code adds, removes or changes items.
Columns, AddColumn, RemoveColumn, GetColumn, AutoColumns (auto-columns, true) The columns in the declared order. A grid without columns adds one per public field and property. Items of another type replace these, but not columns that code or UXML added.
SetCellRenderer(column, (index, item) => element) Builds the cells of a column with your code. The grid calls it each time it binds a row.
GetValue, SetValue Read and write a cell by source index. SetValue converts text and numbers to the member type and returns false when it cannot, or when the setter throws (the grid logs that exception).
ReadOnly (read-only) All cells show labels. Toggles are disabled. Buttons still work.
AllowSorting (allow-sorting), SortByColumn, ClearSort A header click sorts; Shift+click adds a column to the sort. The sort is stable. Nulls come first, numbers and dates sort by value, and text sorts A to Z in any case.
SetFilter, ClearFilter, ClearFilters, SearchText, ShownIndices One filter per column, and a search that shows the rows with a cell text that contains the text in any case. The search reads only the columns that show. ShownIndices gives the source indices of the shown rows, in the shown order.
SelectionMode (selection-mode, Multiple), SelectedIndices, SelectedItems, SetSelection, ClearSelection, SelectionChanged The selection by source index. It stays on the same items after a sort. A filter that hides a selected row removes it from the selection.
ItemsChosen A double click or Enter on rows, with the selected items.
CellValueChanged, CellClicked An edit or SetValue that changed a value; a button click. Both give the source index and the column.
AllowColumnResize (allow-column-resize), AllowColumnReorder (allow-column-reorder), RowHeight (row-height, 24) Header edge drags, header drags, and the fixed row height.
GroupBy (group-by), Groups, SetGroupExpanded, IsGroupExpanded, SetAllGroupsExpanded Groups the shown rows by the value of one column, with a header row per group ("Class: Mage (3)"). The groups sort by value, and follow the direction of a sort on that column. The rows keep the sort inside a group. Groups gives the header texts of the values in the shown order.
AllowColumnHide (allow-column-hide, true), ShowColumnMenu(position) A right-click on the header shows the column menu: one check item per column. ShowColumnMenu shows it at a panel position, for a button of your own.
VisibleColumns, SetVisibleColumns(names), VisibleColumnsChanged The names of the columns that show, in the shown order. SetVisibleColumns shows these columns and hides the others. The event comes after each column that shows or hides.
SelectionColumn (selection-column) Adds a first column with a check box per row, and a check box in the header for all shown rows.
PageSize (page-size, 0), Page, PageCount, PageChanged The rows on one page; 0 shows all rows. Page starts at 1 and is clamped to PageCount. PageChanged gives the new page.
ShowPagination (show-pagination), Pagination Shows page buttons under the grid while PageSize is above 0. Pagination is that Pagination control, for its own settings such as SiblingCount.
FilteredIndices The source indices of the rows of all pages, in the shown order. Without pages it is the same as ShownIndices.
ShowFooter (show-footer), SetFooter(column, rows => text) Shows a footer row with one cell per column. The cell shows FooterText, then the text of the callback. The callback gets FilteredIndices. Null removes it.
ToCsv(selectedOnly) RFC 4180 CSV of the shown or the selected rows, in the shown row and column order, with invariant numbers. With pages, it holds the rows of all pages. Columns without a member, hidden columns and group headers are left out.
View The native MultiColumnListView, for the settings that the grid does not wrap.

Column menu

Selection column

Pages

Tips:

Styling

Class Element
tb-data-grid The grid (grows; surface background, border). tb-data-grid--read-only is added with ReadOnly.
tb-data-grid__view The native MultiColumnListView. The grid colors its header (surface-alt, bold text), its rows (surface, surface-alt on odd rows, surface-hover under the pointer, accent when selected) and its sort arrow.
tb-data-grid__cell A cell. It holds one editor, label or button, without the field margins. The editors are flat until the pointer or the focus is on them.
tb-data-grid__cell-label The text of a label cell (ellipsis).
tb-data-grid__group The label of a group header row. It fills the row: --tb-color-surface-alt background, bold text, --tb-color-surface-hover on hover.
tb-data-grid__select, tb-data-grid__select-all The check box of a row and of the header in the selection column: native Toggle elements without margins.
tb-data-grid__footer The footer: --tb-color-surface-alt background and a top border. It clips its row.
tb-data-grid__footer-row The row of the footer cells (24 px high at least).
tb-data-grid__footer-cell A footer cell, a Label: bold text, ellipsis. The grid sets its width.
tb-data-grid__pagination The built-in Pagination: centered, with a top border.

To put the page buttons on the right and make the footer text normal:

.tb-data-grid__pagination { justify-content: flex-end; }
.tb-data-grid__footer-cell { -unity-font-style: normal; }