TableWidget
Display tabular data with columns, headers, row navigation, and optional selection.
TableWidget is a powerful widget for presenting structured data in rows and columns. It supports keyboard navigation, row focus tracking, multi-select with checkboxes, virtualization for large datasets, and theming.
Basic Usage
Create a table by defining header cells and row cells using the fluent API:
dotnet runThe table requires:
- Data source: A list of objects to display
- Header: Column definitions with names and sizing
- Row builder: A function that creates cells for each data row
Navigation
Use Up/Down arrows to navigate between rows. Press Tab to move focus to other widgets.
Column Sizing
Control column widths using the Width() method on header cells:
| SizeHint | Description |
|---|---|
SizeHint.Fill | Column expands to fill available space |
SizeHint.Content | Column fits its content |
SizeHint.Fixed(n) | Column has fixed width of n characters |
.Header(h => [
h.Cell("Name").Width(SizeHint.Fill), // Expands to fill
h.Cell("Category").Width(SizeHint.Content), // Fits content
h.Cell("Price").Width(SizeHint.Fixed(10)) // Fixed 10 chars
])2
3
4
5
Cell Alignment
Align cell content using the Align() method:
h.Cell("Price").Width(SizeHint.Fixed(10)).Align(Alignment.Right)
h.Cell("Status").Align(Alignment.Center)2
Focus and Navigation
Track which row has keyboard focus using RowKey(), Focus(), and OnFocusChanged():
dotnet runKey Concepts
- RowKey: A function that returns a unique identifier for each row. Required for stable focus tracking across data changes.
- Focus: The currently focused row key. Pass
nullfor no focus. - OnFocusChanged: Called when the user navigates to a different row.
Row Keys Are Important
Without RowKey(), the table uses row indices which can cause focus to jump unexpectedly when data changes. Always provide a row key for dynamic data.
Selection Column
Add a checkbox column for multi-select functionality:
dotnet runSelection API
.SelectionColumn() // Basic selection (checkbox only)
.SelectionColumn(
item => item.IsSelected, // Read selection state
(item, selected) => item.IsSelected = selected // Write selection state
)
.OnSelectAll(() => { /* Select all items */ })
.OnDeselectAll(() => { /* Deselect all items */ })2
3
4
5
6
7
8
9
The header checkbox shows:
[ ]when no items are selected[-]when some items are selected[x]when all items are selected
Keyboard Shortcuts
Press Space to toggle selection on the focused row. Press Space on the header to select/deselect all.
Render Modes
Tables support two render modes:
.Compact() // No horizontal separators (default)
.Full() // Horizontal separators between rows2
Compact mode is more space-efficient and suitable for dense data. Full mode provides better visual separation for complex tables.
Row Activation
Handle double-click or Enter key on rows:
.OnRowActivated((key, item) => {
Console.WriteLine($"Activated: {item.Name}");
})
// Async version
.OnRowActivated(async (key, item) => {
await OpenDetailsAsync(item);
})2
3
4
5
6
7
8
Footer Row
Add a footer row for summaries or actions:
.Footer(f => [
f.Cell($"Total: {products.Count} items"),
f.Cell(""),
f.Cell($"${products.Sum(p => p.Price):F2}").Align(Alignment.Right),
f.Cell("")
])2
3
4
5
6
Empty State
Customize the display when data is empty:
.Empty(ctx => ctx.VStack(v => [
v.Text("No items found"),
v.Button("Add Item").OnClick(_ => AddItem())
]))2
3
4
Virtualization
Tables automatically virtualize large datasets, rendering only visible rows. This enables smooth scrolling through thousands of items.
For async data sources with pagination, implement ITableDataSource<T>:
public interface ITableDataSource<T>
{
int TotalCount { get; }
Task<IReadOnlyList<T>> GetItemsAsync(int start, int count, CancellationToken ct);
}2
3
4
5
Keyboard Navigation
| Key | Action |
|---|---|
| Up Arrow | Move to previous row |
| Down Arrow | Move to next row |
| Page Up | Move up one page |
| Page Down | Move down one page |
| Home | Move to first row |
| End | Move to last row |
| Space | Toggle selection (when selection column enabled) |
| Enter | Activate row (fires OnRowActivated) |
| Tab | Move focus to next widget |
| Shift+Tab | Move focus to previous widget |
Theming
Customize table appearance using theme elements:
var theme = Hex1bTheme.Create()
.Set(TableTheme.BorderColor, Hex1bColor.DarkGray)
.Set(TableTheme.TableFocusedBorderColor, Hex1bColor.Gray)
.Set(TableTheme.FocusedRowBackground, Hex1bColor.FromRgb(50, 50, 50))
.Set(TableTheme.HeaderForeground, Hex1bColor.Cyan);
await using var terminal = Hex1bTerminal.CreateBuilder()
.WithHex1bApp((app, options) =>
{
options.Theme = theme;
return ctx => /* ... */;
})
.Build();2
3
4
5
6
7
8
9
10
11
12
13
Available Theme Elements
| Element | Type | Description |
|---|---|---|
BorderColor | Hex1bColor | Border color when unfocused |
TableFocusedBorderColor | Hex1bColor | Border color when table has focus |
FocusedBorderColor | Hex1bColor | Color of the thick focus indicator |
FocusedRowBackground | Hex1bColor | Background of focused row |
HeaderForeground | Hex1bColor | Header text color |
HeaderBackground | Hex1bColor | Header background color |
CheckboxChecked | string | Checked checkbox character (default: [x]) |
CheckboxUnchecked | string | Unchecked checkbox character (default: [ ]) |
CheckboxIndeterminate | string | Partial selection character (default: [-]) |
ShowFocusIndicator | bool | Whether to show the thick focus bar |
API Reference
Configuration Methods
| Method | Description |
|---|---|
Header(builder) | Define column headers |
Row(builder) | Define row cell content |
Footer(builder) | Define footer cells |
Empty(builder) | Define empty state widget |
RowKey(selector) | Set row key selector for stable tracking |
Focus(key) | Set the focused row by key |
SelectionColumn() | Enable selection checkboxes |
SelectionColumn(getter, setter) | Enable selection with view model binding |
Compact() | Use compact render mode (default) |
Full() | Use full render mode with separators |
Event Handlers
| Method | Description |
|---|---|
OnFocusChanged(handler) | Called when focused row changes |
OnRowActivated(handler) | Called when row is activated (Enter/double-click) |
OnSelectAll(handler) | Called when header checkbox selects all |
OnDeselectAll(handler) | Called when header checkbox deselects all |
TableRowState Properties
The row builder receives a TableRowState object with these properties:
| Property | Type | Description |
|---|---|---|
RowIndex | int | Zero-based index of the row |
RowKey | object | The row's unique key |
IsFocused | bool | Whether this row has keyboard focus |
IsSelected | bool | Whether this row is selected |
IsFirst | bool | Whether this is the first row |
IsLast | bool | Whether this is the last row |
Related Widgets
- ListWidget - For simple string-based lists
- ScrollWidget - For scrollable content
- BorderWidget - For framing tables