Skip to content

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:

csharp

The 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:

SizeHintDescription
SizeHint.FillColumn expands to fill available space
SizeHint.ContentColumn fits its content
SizeHint.Fixed(n)Column has fixed width of n characters
csharp
.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
])

Cell Alignment

Align cell content using the Align() method:

csharp
h.Cell("Price").Width(SizeHint.Fixed(10)).Align(Alignment.Right)
h.Cell("Status").Align(Alignment.Center)

Focus and Navigation

Track which row has keyboard focus using RowKey(), Focus(), and OnFocusChanged():

csharp

Key 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 null for 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:

csharp

Selection API

csharp
.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 */ })

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:

csharp
.Compact()  // No horizontal separators (default)
.Full()     // Horizontal separators between rows

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:

csharp
.OnRowActivated((key, item) => {
    Console.WriteLine($"Activated: {item.Name}");
})

// Async version
.OnRowActivated(async (key, item) => {
    await OpenDetailsAsync(item);
})

Add a footer row for summaries or actions:

csharp
.Footer(f => [
    f.Cell($"Total: {products.Count} items"),
    f.Cell(""),
    f.Cell($"${products.Sum(p => p.Price):F2}").Align(Alignment.Right),
    f.Cell("")
])

Empty State

Customize the display when data is empty:

csharp
.Empty(ctx => ctx.VStack(v => [
    v.Text("No items found"),
    v.Button("Add Item").OnClick(_ => AddItem())
]))

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>:

csharp
public interface ITableDataSource<T>
{
    int TotalCount { get; }
    Task<IReadOnlyList<T>> GetItemsAsync(int start, int count, CancellationToken ct);
}

Keyboard Navigation

KeyAction
Up ArrowMove to previous row
Down ArrowMove to next row
Page UpMove up one page
Page DownMove down one page
HomeMove to first row
EndMove to last row
SpaceToggle selection (when selection column enabled)
EnterActivate row (fires OnRowActivated)
TabMove focus to next widget
Shift+TabMove focus to previous widget

Theming

Customize table appearance using theme elements:

csharp
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();

Available Theme Elements

ElementTypeDescription
BorderColorHex1bColorBorder color when unfocused
TableFocusedBorderColorHex1bColorBorder color when table has focus
FocusedBorderColorHex1bColorColor of the thick focus indicator
FocusedRowBackgroundHex1bColorBackground of focused row
HeaderForegroundHex1bColorHeader text color
HeaderBackgroundHex1bColorHeader background color
CheckboxCheckedstringChecked checkbox character (default: [x])
CheckboxUncheckedstringUnchecked checkbox character (default: [ ])
CheckboxIndeterminatestringPartial selection character (default: [-])
ShowFocusIndicatorboolWhether to show the thick focus bar

API Reference

Configuration Methods

MethodDescription
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

MethodDescription
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:

PropertyTypeDescription
RowIndexintZero-based index of the row
RowKeyobjectThe row's unique key
IsFocusedboolWhether this row has keyboard focus
IsSelectedboolWhether this row is selected
IsFirstboolWhether this is the first row
IsLastboolWhether this is the last row

Released under the MIT License.