Extras
DataGrid
BitDataGrid displays an information-rich collection of items and gives people the tools to work through it: sorting, quick search and per-column filters, paging, grouping with aggregates, selection and inline editing, resizable/reorderable/frozen columns, master-detail and tree rows, keyboard cell navigation, row and column virtualization, and CSV/Excel export of everything matching or just the selection. Bind a list, an IQueryable that a provider such as EF Core executes at the source, or a callback for server-side paging or infinite scrolling. Use a data grid when information density is critical.
Notes
To use this component, you need to install the Bit.BlazorUI.Extras(opens in a new tab) NuGet package, as described in the Optional steps of the Getting started page.
Usage
Every example is live. Open its code to see exactly what produced the component running underneath.
Basic & sorting
AllowUnsorted="false"
(on the grid or on one column) to cycle between ascending and descending only, for data whose
source order means nothing. Hold Ctrl (or ⌘) and click additional headers for multi-column sorting.
The item type flows from the grid to its columns automatically (no per-column TItem needed), a column binds to its property with the strongly typed Property selector (the string-based Field parameter also remains available), and columns can be wrapped in an optional Columns element.
Filtering & paging
This example binds columns with the string-based Field parameter - useful when column definitions come from configuration - instead of the strongly typed Property selector used elsewhere.
Selection
SelectionMode picks between single-row selection (a click anywhere on the row)
and multiple selection with per-row checkboxes and a select-all box in the header, which
covers the rows of the current page and shows the indeterminate state while only some are
selected. SelectedItems is two-way bindable, and the selection is tracked by
the row's KeyField rather than by object reference, so it survives a refresh
that re-materializes the rows. Switching modes drops a selection the new mode can't hold;
IsRowSelectionDisabled excludes individual rows (select-all skips them).
0 selected
Inline editing
bool?),
date, date-and-time (offsets and sub-minute precision preserved) and enum. Edits are buffered
and only written to the row on Save, so Cancel always leaves it untouched.
Editable="false" keeps a column read-only while the rest of the row is edited,
and EditTemplate supplies your own editor for a column.
Grouping & aggregates
Aggregate types, AggregateBy computes a custom
aggregate - here the Supplier column counts distinct suppliers per group and overall.
GroupsInitiallyCollapsed opens a grouped grid as a compact list of headers to
drill into, and ExpandAllGroupsAsync/CollapseAllGroupsAsync flip
every level at once - including groups that don't exist yet, so the choice survives a
regrouping or a data refresh.
Templates & detail rows
Field) is not sortable by default - give it a
SortBy key selector to sort it, like the computed Value column here.
When the ordering itself is the unusual part rather than the key, a column can also
supply its own Comparer - the Stock column sorts in-stock rows ahead of
out-of-stock ones instead of by the raw number.
Resize, reorder & freeze columns
AutoFitColumnAsync /
AutoFitAllColumnsAsync). MinWidth and MaxWidth
bound both the drag and the fit. The ID and Name columns are frozen, staying visible
while you scroll horizontally; FrozenEnd pins to the opposite edge instead.
Column header groups
Group on consecutive columns to render them under a single spanning header cell -
a second header row that banners related columns together. Only adjacent columns sharing the
same name join one span, so reordering the columns reshapes the banners; a column with no
Group simply leaves a gap above it.
Column spanning
ColSpan function on a column to let a single cell span several columns based on its row.
Discontinued rows span the Name cell over Category; premium rows span Price over Stock.
Virtualization
Height and RowHeight.
10,000 rows
Server-side data
OnRead callback to take over sorting, filtering, searching and paging
(e.g. against a database). Each request carries the page window, the active sort, filter and
group descriptors, the quick-search term and a CancellationToken the grid cancels
when a newer request supersedes it - so a stale response can never overwrite fresher data.
Return the page plus the grand TotalCount, and optionally
Aggregates computed over the whole matching set so the footer shows real totals
rather than per-page numbers. This example simulates a backend with a small delay.
Last request → skip 0, take 10, sorts: 0, filters: 0, total: 523
Infinite scrolling
OnLoadMore instead of binding Items. The grid appends the next batch
automatically as the user scrolls toward the end - with no total count or paging UI. A fixed
Height is required. CSV/Excel exports issue a single request with
Take = null ("all rows"), so the handler should honor a null Take.
Batch #1 → loaded rows 1–40 (40 rows)
Tree view (hierarchical rows)
ChildrenSelector parameter to a function that returns each item's direct children.
The grid then treats Items as the root nodes and renders expand/collapse toggles with
indentation, and reports itself to assistive tech as a treegrid whose rows carry their
own depth (aria-level) and expanded state. TreeInitiallyExpanded decides
how it opens, and ExpandAllAsync/CollapseAllAsync drive it from code.
Sorting applies within each set of siblings, so the hierarchy is preserved.
Searching and filtering work on the hierarchy rather than flattening it: the tree is pruned
to the branches that contain a match, those branches open themselves so nothing hides behind
a collapsed ancestor, and a folder that matches keeps its whole subtree. Clearing the term
restores the expand state you had - the grid never writes to it.
Master detail
DetailTemplate can render anything - including another BitDataGrid.
Each supplier expands to show a nested, sortable grid of the products it provides.
Row reordering
RowReorderable="true" to show a drag handle on each row. Grab the ⠿ handle and drop it
onto another row. The grid reorders the bound list in place and raises OnRowReorder.
Cell events & context menu
OnCellClick, OnCellDoubleClick and OnCellContextMenu
with the row, column and the underlying mouse event. Here OnCellContextMenu opens a custom
context menu at the pointer position (right-click any cell).
Click, double-click or right-click any cell.
Keyboard cell navigation
CellNavigation="true" to enable a roving tab stop. Use arrow keys, Home/End,
Ctrl+Home/End, PageUp/PageDown to move, Enter/F2 to edit (Esc to cancel), and Delete to
delete the focused row (requires Editable).
Variable row height
RowHeightSelector a function that returns the desired height (in pixels) for a given row.
Here premium products (price over $500) get a taller row.
Empty & loading states
EmptyTemplate to customize the placeholder shown when there is no data,
or rely on the built-in "No records to display" message. The busy state is the same story:
setting Loading shows a spinner (and marks the grid aria-busy),
and a LoadingTemplate replaces that spinner with your own skeleton or message.
Borders, striping & row numbers
Bordered and
Striped parameters. ShowRowNumbers adds the line-number gutter
desktop grids put on the leading edge: it counts the row's position in the whole
dataset, so it keeps going across pages (turn to page 2 and the numbers continue at 9)
and across virtualized windows instead of restarting. It is chrome rather than data, so
exports and clipboard copies never carry it.
Filter operators
FilterOperators to let users pick the comparison for each filter instead of the fixed
default: text columns offer contains / doesn't contain / starts with / ends with / = / ≠,
while number and date columns offer = ≠ > ≥ < ≤. Every type also ends with
Is blank / Is not blank, the two criteria that need no value at all - picking one
applies immediately and the editor beside it disappears, since there is nothing left to type.
Changing any other operator re-applies the current filter text under it.
Individual columns can override the grid-level setting - the Supplier column opts out here,
keeping its fixed default filter.
Edit validation
Validate function to check proposed edits. While any editor holds an
invalid value, the message renders under it and Save is blocked. Edits are buffered - the underlying
object is only modified when Save is clicked, so Cancel always leaves the row untouched.
Try clearing the Name, or entering a negative Price or Stock.
State persistence
GetState() captures the user-adjustable state - page, page size, search term, sorts,
filters, groups (including which of them are open) and the per-column visibility, width and
order - as a serializable snapshot;
ApplyStateAsync() restores it. The snapshot survives a JSON round trip to local
storage or a user-preferences store: filter values come back as JsonElements and
are re-coerced to each column's own type on restore. Entries for columns that no longer exist
are ignored. Adjust the grid (sort, filter, resize, hide columns, change page), save the state,
shuffle things around, then restore.
Adjust the grid, then save its state.
Server-side virtualization
Virtualize with OnRead (and no paging) to browse an arbitrarily large
remote dataset: row windows are fetched on demand as you scroll - this example simulates 100,000 rows.
The footer shows server-provided aggregates (via BitDataGridReadResult.Aggregates)
computed over the whole filtered dataset, and the Rating column is pinned to the end edge
with FrozenEnd.
Localization
Strings parameter
(a BitDataGridStrings). Assign a customized instance to localize the grid; this
example renders a Persian (RTL) grid.
IQueryable data source
IQueryable<T> to Items and the grid translates filtering,
sorting and paging into expression trees composed onto the queryable - so a remote LINQ
provider such as EF Core executes them at the source (SQL WHERE/ORDER BY/OFFSET)
and only the current page is ever materialized. This example uses an in-memory queryable;
bind dbContext.Products the same way.
Excel export
ShowExcelExport adds a toolbar button that downloads the full (filtered/sorted)
data as a real .xlsx workbook - generated in-process with no external library.
Numbers, booleans and dates are written as native cell types, so the sheet can compute,
sort and date-filter on them instead of on text, and the workbook mirrors the grid's
layout: a bold, frozen header row carrying Excel's own AutoFilter, the grid's column
widths, frozen columns as Excel freeze panes and ColSpan cells as merged cells.
With ExcelExportStyled the workbook also carries the grid's current visual
theme - the rendered header/row colors, the striped alternating background, the border
color and bold/italic fonts are sampled from the live DOM at export time, so whatever theme
is active (light, dark, custom) is what lands in the file.
Exports always cover all matching rows, not just the rendered ones: server and
infinite-scrolling modes fetch the full set through OnRead/OnLoadMore,
and tree mode includes collapsed branches. ExportFileName names the downloaded
files (products.csv / products.xlsx here), and the CSV is written
with CRLF line endings and a UTF-8 byte-order mark so Excel opens accented text correctly.
Export with complex layouts
DetailTemplate, frozen ID/Name columns and
ColSpan spanning (discontinued rows span Name over Category) - try scrolling,
expanding details and filtering, then export. The Excel file mirrors the layout:
the frozen ID/Name columns (plus the header row) become a freeze pane and each spanning
cell becomes a merged cell. The data stays faithful in both formats: frozen columns keep
their declared order, a templated field column (Name) exports the raw field value rather
than its markup, and detail content is not exported (master rows only). A column without a
bound field has nothing to write, so it is skipped - unless it is given an
ExportValue selector, which is how the computed Value column here exports a
real number that spreadsheet formulas can work on. The reverse is Exportable="false",
which keeps a purely presentational column out of the file - the Supplier column opts out here.
CSV has no layout concepts, so it stays flat - there every column exports its own
value, including the Category a span covers.
Lazy tree loading
ChildrenProvider (instead of ChildrenSelector) to fetch a node's
children asynchronously on first expand - e.g. from a backend - with the results cached for
later toggles. Pair it with HasChildrenSelector so unloaded nodes know whether to
render an expand toggle. Expanding a folder below simulates a 600 ms backend call.
Touch drag & drop
Column virtualization
VirtualizeColumns renders only the columns in (and near) the horizontal viewport,
replacing scrolled-out runs with spacer cells - for grids with very many columns. This example
declares 40 measurement columns (plus a frozen ID column) but only the visible window exists in
the DOM while you scroll horizontally. Combine it with row Virtualize for large
datasets in both dimensions. Columns need explicit pixel widths.
Programmatic control
SortByAsync /
ClearSortsAsync, ApplyFilterAsync / ClearFiltersAsync,
GroupByAsync / UngroupAsync, GoToPageAsync,
SetPageSizeAsync, SearchAsync and RefreshAsync.
The traffic runs the other way too: OnSortChange, OnFilterChange,
OnGroupChange and OnPageChange report every view change - however
it was made - so a parent can react to it or persist it. The log below is written by them.
The grid reports every view change here.
Detail rows from the row & from code
ExpandDetailOnRowClick
expands the DetailTemplate from a click anywhere on the row (and
Enter on a focused cell), so ShowDetailToggle="false" can hide the
column altogether. It combines with selection - the click below both selects the row and
expands it. The same state is reachable from code with ExpandDetailAsync,
CollapseDetailAsync, ToggleDetailAsync,
ExpandAllDetailsAsync and CollapseAllDetailsAsync, or declaratively
by binding ExpandedDetailItems. Every change raises OnDetailToggle.
Click any row to reveal its details.
Quick search
ShowSearchBox puts a single search field in the toolbar that filters across
every searchable column at once - the grid-wide counterpart of the per-column filter row.
Matching is case-insensitive and runs against the text each column renders, so users search
exactly what they see (a formatted price matches on its formatted form). A column opts out with
Searchable="false" - the Stock column here - and the term is two-way bindable
through SearchText, so it can also be driven from your own field or persisted with
the rest of the grid state. The box searches as you type, coalescing a burst of keystrokes into
one search after SearchDebounce milliseconds of quiet (300 by default; set
0 to apply every keystroke, which a small in-memory grid can afford but a remote
one should not). In server and infinite modes the term arrives as
BitDataGridReadRequest.Search; over an IQueryable it is translated
into a single OR of Contains predicates the provider runs at the source;
and a tree grid prunes itself to the branches that contain a match.
No search term.
Conditional row styling & cell tooltips
RowClass and RowStyle take a function of the row and return a CSS class
or inline style for it, so the data can speak for itself: out-of-stock rows are flagged red here
and premium rows are tinted. Both run per row and are appended after the grid's own classes and
layout style, so they win any property they repeat.
ShowCellTooltips gives every value cell a native tooltip with its full text - the
answer to a value the column is too narrow to show - and a column can opt in or out on its own
with ShowTooltip. Narrow the Name column and hover a long product name to see it.
Clipboard, range selection & selected-rows export
CellNavigation on, a focused cell also
answers Space (toggle the row), Ctrl+A (select every row of the
view) and - once ClipboardCopy is set - Ctrl/⌘+C,
which copies the selected rows (or the focused one) to the system clipboard as tab-separated text
with a header line, ready to paste into Excel or Sheets as real columns.
The same operations are public API: SelectAllAsync,
ClearSelectionAsync and CopyToClipboardAsync.
Every export takes the selection too: passing selectedOnly: true to
ExportCsvAsync, ExportExcelAsync, ToCsvAsync or
ToExcelAsync narrows the file to the selected rows, written in view order rather
than in the order they were picked.
Select rows, then copy them.
Text wrapping
WrapCellText flips that for grids whose
content is prose: headers and cells wrap onto as many lines as they need and each row grows
to fit its tallest cell, so nothing is hidden behind a tooltip. A column overrides the grid
with WrapText - the ID, price and stock columns here stay on one line, because a
short value has nothing to gain from wrapping and a ragged column is harder to compare down.
Wrapping is deliberately inert while Virtualize is on: virtualization needs every
row to be exactly RowHeight tall, which a wrapped row is not.
Row double-click & editing from code
OnRowDoubleClick is the row-level counterpart of OnCellDoubleClick:
it reports the row itself, which is what "open this record" hangs off in most applications.
Paired with the editing lifecycle - BeginEdit, CommitEditAsync,
CancelEditAsync, AddNewRowAsync, DeleteRowAsync and the
EditingItem the grid currently holds open - it turns the double-click into the
edit gesture spreadsheets have trained everyone to expect, alongside (not instead of) the
command column's own buttons. The row still commits with Enter and cancels with
Esc.
Double-click a row to edit it.
RTL
Direction parameter to BitDir.Rtl.
API
Every parameter, public member, sub-class and enum this component exposes.
BitDataGrid parameters
| Name | Type | Default value | Description |
|---|---|---|---|
| Items | IEnumerable<TItem>? | null | The data source bound to the grid for client-side processing. An IQueryable<T> (e.g. an EF Core DbSet) gets filtering/sorting/paging translated into expression trees the provider executes at the source, materializing only the current page. |
| OnRead | Func<BitDataGridReadRequest, Task<BitDataGridReadResult<TItem>>>? | null | Server-side data callback. When set, the grid delegates sort/filter/page/group to the caller. |
| OnLoadMore | Func<BitDataGridReadRequest, Task<BitDataGridReadResult<TItem>>>? | null | Infinite-scrolling data callback. Loads rows in batches and appends the next batch as the user scrolls toward the end. CSV/Excel exports issue a single request with Take = null ("all rows"), so the handler should honor a null Take. |
| LoadMoreBatchSize | int | 50 | Number of rows fetched per batch in infinite-scrolling mode. |
| ChildContent | RenderFragment? | null | Column definitions and other declarative children. |
| Columns | RenderFragment? | null | Alias of ChildContent, letting column definitions read declaratively as <Columns>...</Columns>. Both fragments are rendered when both are set. |
| Loading | bool | false | Replaces the grid body with a loading row (LoadingTemplate, or a spinner and the localized loading text) while data is being fetched, and marks the grid aria-busy. Rows are hidden until loading ends. |
| ShowSearchBox | bool | false | Renders a quick-search box in the toolbar that filters rows across every searchable column at once, matching case-insensitively against the text each column renders. Forwarded as BitDataGridReadRequest.Search in server/infinite modes; translated into an OR of Contains predicates over the string columns for an IQueryable source; a tree grid is pruned to the branches containing a match. Suppressed on a lazily-loaded tree (ChildrenProvider), whose unloaded children cannot be examined. |
| SearchText | string? | null | The quick-search term (supports two-way binding), so the search can also be driven from an external field. |
| SearchTextChanged | EventCallback<string?> | Raised whenever the quick-search term changes. | |
| SearchDebounce | int | 300 | How long (ms) the search box waits after the last keystroke before applying the term, so the grid searches as the user types without re-querying on every character. 0 applies each keystroke immediately. |
| KeyField | Func<TItem, object>? | null | Optional key selector used for selection/edit identity. Defaults to reference equality. |
| ChildrenSelector | Func<TItem, IEnumerable<TItem>?>? | null | Child selector that turns the grid into a hierarchical tree grid. |
| ChildrenProvider | Func<TItem, Task<IEnumerable<TItem>?>>? | null | Async children provider for a lazily-loaded tree grid: children are fetched on a node's first expand (e.g. from a backend) and cached. Mutually exclusive with ChildrenSelector; pair with HasChildrenSelector. |
| HasChildrenSelector | Func<TItem, bool>? | null | Tells whether a node can have children before they are loaded, so unloaded lazy nodes render an expand toggle. Only used with ChildrenProvider. |
| TreeInitiallyExpanded | bool | false | When tree mode is active, controls whether nodes start expanded. Ignored in lazy mode (ChildrenProvider). |
| Class | string? | null | Custom CSS class for the root element. |
| Style | string? | null | Custom inline style for the root element. |
| Height | string? | null | Height of the scroll viewport, e.g. "480px". Required for virtualization and infinite scrolling. |
| Striped | bool | true | Renders alternate-row striping. |
| Hoverable | bool | true | Highlights the row under the pointer. |
| Bordered | bool | true | Renders cell borders. |
| ShowHeader | bool | true | Renders the header row. |
| ShowFooter | bool | false | Renders the footer/aggregate row. |
| ShowRowNumbers | bool | false | Renders a narrow leading gutter numbering the rows by their position in the whole dataset, so the count continues across pages, virtualized windows and infinite-scroll batches. It is chrome, not data: exports and clipboard copies never carry it. |
| ShowCellTooltips | bool | false | Gives every value cell a native tooltip with its full text, so a value the column is too narrow to show stays readable on hover. Overridable per column with ShowTooltip; cells rendered by a Template are excluded. |
| WrapCellText | bool | false | Lets long headers and cell values wrap onto several lines instead of clipping to one, with each row growing to fit its tallest cell. Overridable per column with WrapText; ignored while Virtualize is on, which requires a uniform row height. |
| AriaLabel | string? | null | Accessible name of the grid itself, so screen-reader users can tell it apart from other grids on the page. Falls back to Strings.GridLabel. |
| RowClass | Func<TItem, string?>? | null | Per-row CSS class selector, appended after the grid's own row classes - the conditional row styling counterpart of AG Grid's rowClassRules. |
| RowStyle | Func<TItem, string?>? | null | Per-row inline style selector, appended after the row's layout style. |
| Direction | BitDir | BitDir.Ltr | Text direction (LTR/RTL). |
| Sortable | bool | true | Enables column sorting by clicking headers. |
| MultiSort | bool | true | Enables multi-column sorting via Ctrl/⌘+click with priority badges. |
| AllowUnsorted | bool | true | Whether a third header click returns the column to its unsorted state (ascending → descending → unsorted). Set false to cycle between ascending and descending only. Overridable per column with AllowUnsorted. |
| Filterable | bool | false | Renders a per-column quick-filter row. A tree grid filters by pruning the hierarchy to the branches containing a match; the row is suppressed on a lazily-loaded tree (ChildrenProvider). |
| FilterOperators | bool | false | Shows an operator dropdown next to text/number/date filter editors so users pick the comparison (contains/starts with/=/≠/>/≥/</≤) instead of the fixed default. |
| Strings | BitDataGridStrings | new() | All user-visible strings rendered by the grid; assign a customized instance to localize the UI. |
| Resizable | bool | false | Enables column resizing by dragging header edges. |
| Reorderable | bool | false | Enables column reordering via drag-and-drop (mouse via native HTML5 DnD, touch/pen via a pointer-event fallback). |
| Groupable | bool | false | Enables grouping via a header button on groupable columns. |
| GroupsInitiallyCollapsed | bool | false | Groups start collapsed instead of expanded, so a grouped grid opens as a compact list of group headers. Flipped at runtime by ExpandAllGroupsAsync/CollapseAllGroupsAsync. |
| ShowToolbar | bool | false | Renders the toolbar area. |
| ShowColumnChooser | bool | false | Renders a column show/hide chooser in the toolbar. |
| ShowCsvExport | bool | false | Renders a CSV export button. The export covers all matching rows in every data mode, not just the rendered ones. |
| ShowExcelExport | bool | false | Renders an Excel (.xlsx) export button. The workbook is generated in-process with no external dependency, covers all matching rows in every data mode, and mirrors the grid's layout: bold frozen header row, column widths, leading frozen columns as a freeze pane and ColSpan cells as merged cells. |
| ExcelExportStyled | bool | false | When true, Excel exports also carry the grid's current visual theme: the rendered header/row colors, striped alternating rows, border color and bold/italic fonts are sampled from the live DOM at export time (so the active theme - including dark mode - lands in the workbook). Falls back to the plain bold-header styling when JS is unavailable (prerendering). |
| ExportFileName | string? | null | Base name (without extension) of the downloaded export files, e.g. "orders" for orders.csv / orders.xlsx. Defaults to "export". |
| CellNavigation | bool | false | Enables keyboard cell navigation with a roving tabindex (arrows/Home/End/PageUp/PageDown to move, Enter/F2 to edit, Esc to cancel, Space to toggle the row's selection, Ctrl+A to select all rows when SelectionMode is Multiple, Delete to delete the focused row when Editable). |
| ClipboardCopy | bool | false | Enables copying to the system clipboard with Ctrl/⌘+C on a focused cell (requires CellNavigation) and through CopyToClipboardAsync. Copies the selected rows - or the focused one - as tab-separated text with a header line, so it pastes into a spreadsheet as columns. |
| RowReorderable | bool | false | Enables drag-and-drop row reordering (mouse, touch and pen; plus keyboard via the drag handle's arrow keys). |
| OnRowReorder | EventCallback<BitDataGridRowReorderEventArgs<TItem>> | Raised when a row is dropped onto another row during reordering. | |
| SelectionMode | BitDataGridSelectionMode | BitDataGridSelectionMode.None | How rows can be selected (None/Single/Multiple). |
| SelectedItems | IReadOnlyList<TItem>? | null | The selected items (supports two-way binding). |
| SelectedItemsChanged | EventCallback<IReadOnlyList<TItem>> | Raised when the selection changes. | |
| OnRowClick | EventCallback<TItem> | Raised when a row is clicked. | |
| OnRowDoubleClick | EventCallback<TItem> | Raised when a row is double-clicked - the usual hook for "open this record", or for starting an inline edit with BeginEdit. | |
| OnCellClick | EventCallback<BitDataGridCellEventArgs<TItem>> | Raised when a data cell is clicked. | |
| OnCellDoubleClick | EventCallback<BitDataGridCellEventArgs<TItem>> | Raised when a data cell is double-clicked. | |
| OnCellContextMenu | EventCallback<BitDataGridCellEventArgs<TItem>> | Raised when a data cell is right-clicked. | |
| IsRowSelectionDisabled | Func<TItem, bool>? | null | Predicate returning true when a given row may not be selected. |
| Pageable | bool | false | Enables paging with a pager UI. |
| PageSize | int | 20 | The number of rows per page. |
| PageSizeOptions | int[] | { 10, 20, 50, 100 } | The page-size options offered in the pager dropdown. |
| PagerPosition | BitDataGridPagerPosition | BitDataGridPagerPosition.Bottom | Where the pager renders relative to the grid. |
| Virtualize | bool | false | Renders only the visible rows for large datasets. Requires a fixed Height and RowHeight. In server mode (OnRead) with paging off, row windows are fetched on demand as the user scrolls; with OnLoadMore, the accumulated batches are virtualized so the DOM stays bounded. Requires a uniform row height, so RowHeightSelector and WrapCellText are ignored while it is on and expanded DetailTemplate rows are not accounted for - pair master-detail with paging instead. |
| RowHeight | float | 36 | Uniform row height in pixels (required when virtualizing). |
| RowHeightSelector | Func<TItem, float>? | null | Optional per-row height selector (ignored while virtualizing). |
| VirtualizeColumns | bool | false | Renders only the columns in (and near) the horizontal viewport, replacing scrolled-out runs with spacers - for grids with very many columns. Requires explicit px column widths; not applied with column header groups or ColSpans. |
| Editable | bool | false | Enables inline editing with a command column. |
| NewItemFactory | Func<TItem>? | null | Factory used by the toolbar Add button to create a new row. |
| OnRowSave | EventCallback<TItem> | Raised when an edited row is saved. | |
| OnRowCancel | EventCallback<TItem> | Raised when an edit is cancelled. | |
| OnRowDelete | EventCallback<TItem> | Raised when a row is deleted (via the command column's Delete button or the Delete key in cell navigation). | |
| OnRowCreate | EventCallback<TItem> | Raised when a new row is created. | |
| EmptyTemplate | RenderFragment? | null | Custom content rendered when there is no data. |
| LoadingTemplate | RenderFragment? | null | Custom content rendered in place of the built-in spinner while Loading is true. |
| ToolbarTemplate | RenderFragment? | null | Custom content rendered in the toolbar's start area. |
| DetailTemplate | RenderFragment<TItem>? | null | Expandable master-detail content rendered under a row. |
| ShowDetailToggle | bool | true | Renders the built-in expand/collapse toggle column while a DetailTemplate is set. Turn it off to drive the detail rows from row clicks (ExpandDetailOnRowClick) or from code; only the toggle column disappears, the detail rows still render. |
| ExpandDetailOnRowClick | bool | false | Expands (and collapses) a row's detail content when the row itself is clicked. Combines with SelectionMode: a click both selects the row and toggles its detail. Clicks inside the reorder/select/command cells are excluded. |
| ExpandedDetailItems | IReadOnlyList<TItem>? | null | Rows whose detail content is expanded, as a two-way bindable list. Binding it takes control of the expanded state, letting a parent expand or collapse details declaratively. |
| ExpandedDetailItemsChanged | EventCallback<IReadOnlyList<TItem>> | Raised with the new set of expanded rows whenever a detail row is expanded or collapsed. | |
| OnDetailToggle | EventCallback<BitDataGridDetailEventArgs<TItem>> | Raised when a single row's detail content is expanded or collapsed. | |
| OnSortChange | EventCallback<IReadOnlyList<BitDataGridSortDescriptor>> | Raised with the new sort descriptors whenever the sorting changes, by header click or through the programmatic API. | |
| OnFilterChange | EventCallback<IReadOnlyList<BitDataGridFilterDescriptor>> | Raised with the new filter descriptors whenever the filtering changes. | |
| OnGroupChange | EventCallback<IReadOnlyList<BitDataGridGroupDescriptor>> | Raised with the new group descriptors whenever the grouping changes. | |
| OnPageChange | EventCallback<int> | Raised with the new 1-based page number whenever the page or the page size changes. |
BitDataGrid public members
| Name | Type | Default value | Description |
|---|---|---|---|
| RefreshAsync | Task | Recomputes the data view (filter → sort → group → page) and re-renders the grid. | |
| SortByAsync | Task | SortByAsync(columnId, direction, additive) - programmatically sorts by a column; BitDataGridSortDirection.None removes the sort. | |
| ClearSortsAsync | Task | Removes all active sorts and refreshes. | |
| ApplyFilterAsync | Task | ApplyFilterAsync(columnId, operator, value) - programmatically applies a filter, replacing any existing one on the column. | |
| ApplyRangeFilterAsync | Task | ApplyRangeFilterAsync(columnId, from, toExclusive) - applies a half-open range filter (>= from AND < toExclusive), the shape a "between" criterion takes; emitted as two ordinary comparison descriptors so remote and queryable sources need no special handling. | |
| ClearFilterAsync | Task | ClearFilterAsync(columnId) - removes the filter(s) applied to a column. | |
| ClearFiltersAsync | Task | Clears all active column filters and refreshes. | |
| GroupByAsync | Task | GroupByAsync(columnId) - adds the column as the next (nested) grouping level. | |
| UngroupAsync | Task | UngroupAsync(columnId) - removes the column's grouping level. | |
| ClearGroupsAsync | Task | Removes all active groupings and refreshes. | |
| ExpandAllGroupsAsync | Task | Expands every group at every nesting level, and makes newly-built groups expanded (so the choice survives a regrouping or a data refresh). | |
| CollapseAllGroupsAsync | Task | Collapses every group at every nesting level, and makes newly-built groups collapsed. | |
| SearchAsync | Task | SearchAsync(text) - applies the grid-wide quick-search term and resets to the first page; null or empty clears it. Raises SearchTextChanged. | |
| ActiveSearch | string? | null | The active quick-search term, or null when no search is applied. |
| SelectAllAsync | Task | Selects every selectable row of the current view (the whole filtered set locally; the loaded rows in server, queryable and infinite modes). Requires SelectionMode.Multiple. | |
| ClearSelectionAsync | Task | Clears the row selection. | |
| CopyToClipboardAsync | Task<int> | CopyToClipboardAsync() / CopyToClipboardAsync(fallbackRow) - copies the selected rows (or, in the second overload, the given row when nothing is selected) to the system clipboard as tab-separated text with a header line, and returns how many rows were copied; 0 when the clipboard is unavailable. | |
| AutoFitColumnAsync | Task | AutoFitColumnAsync(columnId) - sizes a column to its widest rendered content, the same result as double-clicking its resize handle. | |
| AutoFitAllColumnsAsync | Task | Auto-fits every visible column to its widest rendered content. | |
| GoToPageAsync | Task | GoToPageAsync(page) - navigates to the given 1-based page (clamped to the valid range). | |
| SetPageSizeAsync | Task | SetPageSizeAsync(size) - changes the page size and resets to the first page (without mutating the PageSize parameter). | |
| GetState | BitDataGridState | Captures the user-adjustable state (page, page size, quick search, sorts, filters, groups with their expand/collapse state, and column layout) as a serializable snapshot. | |
| ApplyStateAsync | Task | ApplyStateAsync(state) - restores a state snapshot captured by GetState. | |
| ExportCsvAsync | Task | ExportCsvAsync(selectedOnly) - generates the full (filtered/sorted) data as CSV and triggers a client-side download. Covers all matching rows in every data mode - server/infinite modes fetch them through OnRead/OnLoadMore, tree mode includes collapsed branches - or only the selected rows when selectedOnly is true. | |
| ToCsv | string | ToCsv(selectedOnly) - builds a CSV string of the full dataset synchronously - tree mode includes collapsed branches, queryable mode covers all pages. Only server/infinite modes are limited to the loaded rows (their providers are async); use ToCsvAsync there. | |
| ToCsvAsync | Task<string> | ToCsvAsync(selectedOnly) - builds a CSV string of the full dataset (or only the selected rows) - server/infinite modes issue an OnRead/OnLoadMore request with no paging (Take = null), tree mode includes collapsed branches. | |
| ToExcelAsync | Task<byte[]> | ToExcelAsync(selectedOnly) - generates the full (filtered/sorted) dataset, or only the selected rows, as an Excel workbook (.xlsx). Numbers, booleans and dates land as native cell types, so the sheet can sort, filter and compute on them. The workbook mirrors the grid's layout: a bold frozen header row carrying Excel's own AutoFilter, column widths, leading frozen columns as a freeze pane and ColSpan cells as merged cells; with ExcelExportStyled it also carries the grid's rendered theme (colors, striping, borders, fonts). | |
| ExportExcelAsync | Task | ExportExcelAsync(selectedOnly) - generates the .xlsx workbook and triggers a client-side download. | |
| ActiveSorts | IReadOnlyList<BitDataGridSortDescriptor> | [] | The active sort descriptors, in priority order. |
| ActiveFilters | IReadOnlyList<BitDataGridFilterDescriptor> | [] | The active filter descriptors. |
| ActiveGroups | IReadOnlyList<BitDataGridGroupDescriptor> | [] | The active group descriptors, in nesting order. |
| TotalCount | int | 0 | Total number of rows in the current (filtered) view; the server-reported total in server mode. |
| TotalPages | int | 1 | Total number of pages while paging is active. |
| CurrentPage | int | 1 | The 1-based current page. |
| ExpandAllAsync | Task | Expands every node in the tree. No-op outside tree mode. | |
| CollapseAllAsync | Task | Collapses every node in the tree. No-op outside tree mode. | |
| IsDetailExpanded | bool | IsDetailExpanded(item) - true when the given row's DetailTemplate content is currently expanded. | |
| ExpandDetailAsync | Task | ExpandDetailAsync(item) - expands the given row's detail content. No-op without a DetailTemplate. | |
| CollapseDetailAsync | Task | CollapseDetailAsync(item) - collapses the given row's detail content. | |
| ToggleDetailAsync | Task | ToggleDetailAsync(item) - expands the given row's detail content when collapsed, and collapses it otherwise. | |
| SetDetailExpandedAsync | Task | SetDetailExpandedAsync(item, expanded) - expands or collapses a row's detail content. | |
| ExpandAllDetailsAsync | Task | Expands the detail content of every row of the current view - in local mode every row matching the active filters, not only the rendered page; in server, queryable and infinite modes only the rows loaded so far. Raises OnDetailToggle once per newly expanded row. | |
| CollapseAllDetailsAsync | Task | Collapses every expanded detail row, raising OnDetailToggle once per row. | |
| EditingItem | TItem? | null | The row currently in inline-edit mode, or null when no edit is open. |
| BeginEdit | void | BeginEdit(item) - puts a row into inline-edit mode from code, exactly as its Edit button (or Enter/F2 on a navigable cell) does, snapshotting its values so CancelEditAsync can restore them. | |
| CommitEditAsync | Task | Commits the open inline edit (writing the buffered values to the row and raising OnRowSave). Refuses while any editor holds an invalid value. | |
| CancelEditAsync | Task | Abandons the open inline edit, restoring the row to the values it had when the edit began, and raises OnRowCancel. | |
| AddNewRowAsync | Task | Appends a blank row built by NewItemFactory above the view and opens it for editing, exactly as the toolbar Add button does. | |
| DeleteRowAsync | Task | DeleteRowAsync(item) - drops the row from the selection and raises OnRowDelete so the caller can remove it from the data source, then refreshes. |
BitDataGridColumn properties
Defines a column inside a BitDataGrid. Place these as child content of the grid.
| Name | Type | Default value | Description |
|---|---|---|---|
| Field | string? | null | Name of the property this column is bound to. Supports nested paths ("Address.City"). Prefer Property for a strongly typed, refactor-safe alternative. |
| Property | Expression<Func<TItem, object?>>? | null | Typed selector of the property this column is bound to, e.g. Property="p => p.Name". A strongly typed, refactor-safe alternative to Field that supports nested member chains (p => p.Address.City). Takes precedence over Field when both are set. |
| ColumnId | string? | null | Stable identifier for the column. Defaults to the resolved Property/Field path. |
| Title | string? | null | Header text. Defaults to a humanized Property/Field name. |
| Width | string? | null | CSS width, e.g. "120px" or "20%". When null the column shares remaining space. |
| MinWidth | int | 60 | Minimum width in pixels the column can be resized to. |
| MaxWidth | int? | null | Maximum width in pixels the column can be resized to. |
| Sortable | bool? | null | Overrides the grid-level Sortable for this column. |
| SortBy | Func<TItem, object?>? | null | Custom sort key selector. Enables sorting for template-only columns and overrides the field value as the sort key (client mode). |
| SortDescendingFirst | bool | false | When true, the first click on the header sorts descending instead of ascending. |
| AllowUnsorted | bool? | null | Overrides the grid-level AllowUnsorted: whether a third header click returns this column to its unsorted state. |
| Comparer | IComparer<object?>? | null | Custom comparer applied to this column's sort keys, for orderings the default null-safe value comparer cannot express. Client-side only (server and queryable sources sort at the source). |
| Validate | Func<TItem, object?, string?>? | null | Validator for inline edits: receives the row and the proposed value, returns an error message to reject it (blocking Save) or null to accept. |
| Filterable | bool? | null | Overrides the grid-level Filterable for this column. |
| FilterOperators | bool? | null | Overrides the grid-level FilterOperators (the operator dropdown next to this column's filter editor). |
| Resizable | bool? | null | Overrides the grid-level Resizable for this column. |
| Reorderable | bool? | null | Overrides the grid-level Reorderable for this column. |
| Editable | bool? | null | Overrides the grid-level Editable for this column. |
| Groupable | bool? | null | Overrides the grid-level Groupable for this column. |
| Searchable | bool? | null | Whether the grid's quick-search box searches this column. Defaults to every field-bound (or ExportValue-backed) column. |
| Exportable | bool? | null | Whether the column is included in CSV/Excel exports. Defaults to every column that has a value to write (a bound field or an ExportValue selector). |
| ExportValue | Func<TItem, object?>? | null | Value selector used by exports and the clipboard instead of the bound field - the export counterpart of Template. Gives a template-only column a real exported value (numbers and booleans still land in Excel as native cell types). |
| ShowTooltip | bool? | null | Overrides the grid-level ShowCellTooltips for this column's cells. |
| WrapText | bool? | null | Overrides the grid-level WrapCellText: lets this column's header and cells wrap onto several lines instead of clipping to one, with the row growing to fit. |
| Frozen | bool | false | Pins the column to the start edge so it stays visible while scrolling horizontally. |
| FrozenEnd | bool | false | Pins the column to the end edge (right in LTR, left in RTL). Typical for action/status columns. |
| Group | string? | null | Optional header group name. Consecutive columns sharing the same value render under a single spanning header cell. |
| ColSpan | Func<TItem, int?>? | null | Optional per-row column span. |
| Visible | bool | true | Whether the column is visible. |
| Align | BitDataGridColumnAlign | BitDataGridColumnAlign.Left | Horizontal alignment of cell content. |
| Format | string? | null | A .NET format string applied to the value (e.g. "C2", "yyyy-MM-dd"). |
| DataType | BitDataGridColumnDataType | BitDataGridColumnDataType.Auto | The data type used to pick the editor/filter. |
| Aggregate | BitDataGridAggregateType | BitDataGridAggregateType.None | The footer/group aggregate function. |
| AggregateBy | Func<IReadOnlyList<TItem>, object?>? | null | Custom aggregate function for computations beyond the built-ins (e.g. distinct count). Takes precedence over Aggregate. |
| AggregateFormat | string? | null | Format string for the aggregate value. Falls back to Format. |
| HeaderClass | string? | null | Custom CSS class applied to the header cell. |
| CellClass | string? | null | Custom CSS class applied to each data cell. |
| Template | RenderFragment<TItem>? | null | Custom rendering for a data cell. |
| HeaderTemplate | RenderFragment? | null | Custom rendering for the header cell content. |
| EditTemplate | RenderFragment<TItem>? | null | Custom editor rendered when the row/cell is in edit mode. |
| FooterTemplate | RenderFragment<BitDataGridAggregateResult>? | null | Custom rendering for the footer/aggregate cell. |
BitDataGridReadRequest properties
Describes the data the grid needs from a server-side/infinite source (passed to OnRead/OnLoadMore).
| Name | Type | Default value | Description |
|---|---|---|---|
| Skip | int | 0 | Zero-based number of items to skip. |
| Take | int? | null | Maximum number of items to return (null means all). |
| Sorts | IReadOnlyList<BitDataGridSortDescriptor> | [] | The active sort descriptors ordered by priority. |
| Filters | IReadOnlyList<BitDataGridFilterDescriptor> | [] | The active filter descriptors. |
| Groups | IReadOnlyList<BitDataGridGroupDescriptor> | [] | The active group descriptors in nesting order, letting a server-side handler reconstruct the grouping. Empty when no grouping is active. |
| Search | string? | null | The grid-wide quick-search term, or null when no search is active. A free-text term to match across the columns the handler considers searchable, in addition to the per-column Filters. |
| CancellationToken | CancellationToken | A token that is cancelled when the request is superseded by a newer one. |
BitDataGridReadResult<TItem> properties
Result returned from a grid's OnRead/OnLoadMore callback.
| Name | Type | Default value | Description |
|---|---|---|---|
| Items | IReadOnlyList<TItem> | The items for the current page/window. | |
| TotalCount | int | The total number of items matching the current filters (ignored in infinite mode). | |
| Aggregates | IReadOnlyList<BitDataGridAggregateResult>? | null | Optional aggregates computed by the data source over the whole filtered dataset; when provided, the footer shows these instead of aggregating the current page locally. |
BitDataGridCellEventArgs<TItem> properties
Arguments passed to cell-level event callbacks.
| Name | Type | Default value | Description |
|---|---|---|---|
| Item | TItem | The row item. | |
| Column | BitDataGridColumn<TItem> | The column the cell belongs to. | |
| ColumnId | string | The column field/identifier. | |
| ColumnTitle | string | The column's display title. | |
| Value | object? | null | The raw value of the cell. |
| Mouse | MouseEventArgs | The underlying browser mouse event. |
BitDataGridDetailEventArgs<TItem> properties
Arguments raised when a row's master-detail content is expanded or collapsed.
| Name | Type | Default value | Description |
|---|---|---|---|
| Item | TItem | The row whose detail content was toggled. | |
| Expanded | bool | True when the detail content was expanded, false when it was collapsed. |
BitDataGridRowReorderEventArgs<TItem> properties
Arguments raised when a row is reordered via drag-and-drop.
| Name | Type | Default value | Description |
|---|---|---|---|
| DraggedItem | TItem | The dragged row item. | |
| TargetItem | TItem | The drop-target row item. | |
| FromIndex | int? | The original index of the dragged item, or null when the bound Items is not an indexable list. | |
| ToIndex | int? | The destination index, or null when the bound Items is not an indexable list. |
BitDataGridSortDescriptor properties
Describes the sort state applied to a single column (found on BitDataGridReadRequest.Sorts).
| Name | Type | Default value | Description |
|---|---|---|---|
| ColumnId | string | The identifier of the column being sorted. | |
| Direction | BitDataGridSortDirection | BitDataGridSortDirection.Ascending | The sort direction. |
| Priority | int | int.MaxValue | Priority for multi-column sorting (1 = primary). |
BitDataGridFilterDescriptor properties
Describes a filter applied to a single column (found on BitDataGridReadRequest.Filters).
| Name | Type | Default value | Description |
|---|---|---|---|
| ColumnId | string | The identifier of the column being filtered. | |
| Operator | BitDataGridFilterOperator | BitDataGridFilterOperator.Contains | The comparison operator applied to the value. |
| Value | object? | null | The value compared against the column's cell value. |
BitDataGridGroupDescriptor properties
Describes a grouping applied to a column.
| Name | Type | Default value | Description |
|---|---|---|---|
| ColumnId | string | The identifier of the column being grouped. | |
| Direction | BitDataGridSortDirection | BitDataGridSortDirection.Ascending | The sort direction applied to the group keys. |
BitDataGridAggregateResult properties
Holds the computed aggregate value for a column footer or group (passed to a column's FooterTemplate).
| Name | Type | Default value | Description |
|---|---|---|---|
| ColumnId | string | The identifier of the aggregated column. | |
| Type | BitDataGridAggregateType | The aggregate function that produced the value. | |
| Value | object? | null | The raw aggregate value. |
| FormattedValue | string | string.Empty | The aggregate value formatted using the column's AggregateFormat/Format. |
BitDataGridState properties
A serializable snapshot of the grid's user-adjustable state, captured with GetState() and restored with ApplyStateAsync(). Enables persisting grid state across sessions.
| Name | Type | Default value | Description |
|---|---|---|---|
| CurrentPage | int | 1 | The 1-based current page. |
| PageSize | int? | null | The user-selected page size, or null when the grid's PageSize parameter applies. |
| Search | string? | null | The grid-wide quick-search term, or null when no search was active. |
| Sorts | List<BitDataGridSortDescriptor> | [] | The active sort descriptors. |
| Filters | List<BitDataGridFilterDescriptor> | [] | The active filter descriptors. |
| Groups | List<BitDataGridGroupDescriptor> | [] | The active group descriptors. |
| GroupsCollapsed | bool | false | Whether groups were collapsed by default when the snapshot was taken. |
| GroupExpansionOverrides | List<string> | [] | The groups whose expanded state differed from GroupsCollapsed, by their stable paths - so a restored view opens exactly the groups the user had open. Tree-node expansion is not captured (a tree key is not guaranteed to be serializable). |
| Columns | List<BitDataGridColumnState> | [] | Per-column layout state. |
BitDataGridColumnState properties
A per-column layout entry inside BitDataGridState.Columns.
| Name | Type | Default value | Description |
|---|---|---|---|
| ColumnId | string | string.Empty | The column's stable identifier (ColumnId or Field). |
| Visible | bool | true | Whether the column is shown (column-chooser state). |
| Width | double? | null | The resized width in pixels, or null when the column was never resized. |
| Order | int | 0 | The display position among all columns. |
BitDataGridStrings properties
All user-visible (and screen-reader) strings rendered by the grid, defaulting to English. Assign a customized instance to the Strings parameter to localize - including empty/loading texts, pager texts, filter placeholders and operators, edit buttons, aggregate labels, aria-labels and live-region announcements.
| Name | Type | Default value | Description |
|---|---|---|---|
| GridLabel | string | "Data grid" | Default accessible name of the grid element, used when no AriaLabel is given. |
| EmptyText | string | "No records to display." | Shown when the grid has no rows to display. |
| LoadingText | string | "Loading…" | Shown while Loading is true. |
| PagerRangeFormat | string | "{0}–{1} of {2}" | Pager range summary format. |
| PagerPageFormat | string | "Page {0} of {1}" | Pager page summary format. |
| InvalidValueError | string | "Invalid value for {0}." | Error shown when an edited value can't be converted to the column's type. |
| SearchPlaceholder | string | "Search…" | Placeholder of the toolbar's quick-search box. |
| … | string | Plus ~50 more: toolbar/edit button texts, search and column-chooser labels, filter placeholder and operator labels, boolean/enum option texts, group/detail/tree/reorder/resize aria-labels and tooltips, aggregate label formats and aria-live announcements. |
BitDataGridColumnAlign enum
| Name | Value | Description |
|---|---|---|
| Left | 0 | |
| Center | 1 | |
| Right | 2 |
BitDataGridSortDirection enum
| Name | Value | Description |
|---|---|---|
| None | 0 | |
| Ascending | 1 | |
| Descending | 2 |
BitDataGridSelectionMode enum
| Name | Value | Description |
|---|---|---|
| None | 0 | |
| Single | 1 | |
| Multiple | 2 |
BitDataGridAggregateType enum
| Name | Value | Description |
|---|---|---|
| None | 0 | |
| Sum | 1 | |
| Average | 2 | |
| Count | 3 | |
| Min | 4 | |
| Max | 5 | |
| Custom | 6 | The value was produced by the column's custom AggregateBy delegate rather than a built-in function. |
BitDataGridPagerPosition enum
| Name | Value | Description |
|---|---|---|
| Bottom | 0 | |
| Top | 1 | |
| TopAndBottom | 2 |
BitDir enum
| Name | Value | Description |
|---|---|---|
| Ltr | 0 | |
| Rtl | 1 | |
| Auto | 2 |
BitDataGridColumnDataType enum
| Name | Value | Description |
|---|---|---|
| Auto | 0 | |
| Text | 1 | |
| Number | 2 | |
| Boolean | 3 | |
| Date | 4 | |
| DateTime | 5 | |
| DateTimeOffset | 6 | |
| Enum | 7 |
BitDataGridFilterOperator enum
| Name | Value | Description |
|---|---|---|
| Unspecified | 0 | |
| Contains | 1 | |
| DoesNotContain | 2 | |
| StartsWith | 3 | |
| EndsWith | 4 | |
| Equals | 5 | |
| NotEquals | 6 | |
| GreaterThan | 7 | |
| GreaterThanOrEqual | 8 | |
| LessThan | 9 | |
| LessThanOrEqual | 10 | |
| IsEmpty | 11 | |
| IsNotEmpty | 12 |
Feedback
Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.