The grid API

Everything an application does with a grid goes through one object, grid.Api. It is the same object on Avalonia, WPF and WinForms, so code written against it does not change with the host.

The facade is grouped, because a flat surface of this size stops being findable. The host controls add bindable properties on top of it for MVVM (see MVVM and binding); underneath, those properties write into the same objects. For every member and signature, see the API reference.

What is on it

MemberWhat it holds
StateTheme, context, scroll position and extent, loading and empty state, read-only, behaviour switches (CanUserSortColumns and its siblings, smooth scrolling, tooltips, the selection footer).
LayoutFrozen rows and columns (left and right), the filter row, zoom, column count, cell bounds, refresh.
SelectionWhat is selected and the current cell; set it by position or by item. See Selection and clipboard.
EditingOpen, type, commit, cancel, or set a value in one call. See Editing and validation.
CommandsICommands for a toolbar (undo, redo, copy, paste, select all, search, clear filters …) and the methods behind them.
EventsSelection, edits, row activation, button clicks, context menus, column moves, view changes.
FiltersThe complete filter state as a serializable object: capture, apply, clear, changed.
ActionsThe item-addressed operations a view model needs: scroll to, select, begin edit, set value.
RowsOpen and close groups, tree nodes and detail bands by item.
PresentationProviders for tooltips, column headers, the empty state and the loading text; a rounded frame; the day the date picker treats as today.
PanesThe stack of drawn panes — filter lists, menus, pickers — including panes of your own.
CommandInputsThe ICommands and the edit policy a view model hands into the grid.
TimingA tick in step with the frames, for data pushed on a clock.
AccessibilityWhat the screen-reader layer sees; Announce for messages of your own.
TestingA driver for UI tests: clicks, keys, typing and a state line, without a real mouse.
EngineThe engine underneath: GetItem(index) for the item at a view row.

State and appearance

State and Layout
var api = grid.Api;

api.State.VisualTheme = ArionTheme.Dark();
api.Layout.FrozenColumns = 1;              // keep the first column in place
api.State.IsLoading = true;                // the drawn loading state ...
api.State.LoadingText = "Loading orders";
api.State.EmptyStateTitle = "No orders";   // ... and the drawn empty state
api.State.EmptyStateMessage = "Orders you create appear here.";
api.State.ScrollChanged += pos => Log($"{pos.FirstVisibleItem} of {pos.VisibleRowCount}");

State.Changed reports every property change as a GridStateChange(PropertyName, OldValue, NewValue). The empty state draws in the grid itself; besides title and message it can carry an action button (EmptyStateActionLabel with EmptyStateAction or EmptyStateCommand).

Events

Events
api.Events.RowActivated += item => Open((Order)item);          // double-click on a row
api.Events.SelectionChanged += e => Log($"{e.SelectedItems.Count} selected");
api.Events.CellEditCommitted += e => Save(e.Item, e.ElementId, e.NewValue);
api.Events.ViewChanged += e => Log($"view changed: {e.Changes}");   // sort, filter, group ...

RowActivated passes the row's data item, not its index, because an index shifts with every sort and filter; it is raised by a double-click on a row. ButtonClicked reports the item index and the element id of a Column.Button or a Cell.Button. A few events let the application take over a gesture: if something handles SearchRequested, Ctrl+F opens your search instead of the grid's, and the same holds for QuickSearchRequested and ClearFiltersRequested.

Element ids and column positions

Columns are addressed two ways, and mixing them up only shows once a user has moved a column. An element id is the column's identity: it is assigned when the view is built and does not change when columns are reordered or hidden. A column index is the visible position.

From key to id
int amountId = view.ColumnIdOfKey("amount");   // stable: the column's identity
int firstVisibleId = view.ElementIdAt(0);      // what sits at visible position 0 now

Events, filters, sorting and the Pro modules speak element ids. Selection.Set, Selection.SetRange and Editing.SetValue take a visible column index, like the keyboard does; GridCellRef carries both. The item-addressed calls on Actions take the column key, which is the safest of the three.

Acting on items

Actions and Commands
api.Actions.ScrollTo(order);                          // false if filtered away
api.Actions.SelectItems(new[] { order });
api.Actions.SetValue(order, "amount", "120");         // through the normal edit path
api.Commands.Copy(withHeaders: true);
api.Commands.AutoSizeAllColumns();

Actions.SetValue goes through the same path as typing — validation, the edit policy, the edit events and undo all apply — and returns a GridEditOutcome. Items are found by reference first and by Equals only as a fallback, with a scan of the current view.

Presentation hooks

Tooltips, empty state, headers
api.Presentation.CellTooltipProvider = c => c.IsTextTruncated ? c.DisplayText : null;

api.Presentation.EmptyStateProvider = c => c.HasActiveFilters
    ? new GridEmptyStateContent("No matches", "Clear the filters to see all orders.")
    : null;   // null: the standard empty state

api.Presentation.ColumnHeaderProvider = h => h.HasActiveFilter
    ? new GridColumnHeaderContent(Foreground: Palette.Accent)
    : null;

A provider returning null keeps the standard behaviour, and members left null in its result fall back to the defaults. The default tooltip shows the full text of a truncated cell.

Context menus

The grid builds its own cell and header menus. The events hand you the entry list before it opens, so you extend the menu rather than replace it:

An entry in the cell menu
api.Events.CellContextMenuRequested += e =>
{
    if (api.Engine.GetItem(e.ItemIndex) is not Order clicked)
    {
        return;
    }

    e.Entries.Add(MenuItem.Separator());
    e.Entries.Add(new MenuItem("Open order", () => Open(clicked)));
};

HeaderContextMenuRequested works the same way for the column header. Panes of your own — anything derived from ControlPaneContent — open with Api.Panes.Show and close with Api.Panes.CloseAll.

When your code throws

Column getters, tooltips and chart series are your code, called by the grid. When one of them throws outside the drawing of a frame, the grid reports it instead of crashing:

Failures
api.DataReadFailed += failure => Log($"{failure.Source}: {failure.Exception.Message}");
GridDiagnostics.Reported += d => Log($"{d.Kind} {d.Source}: {d.Message}");

DataReadFailed names the reader — "tooltip", "accessibility", "hit-test", "autosize" — and repeated failures from the same reader are damped. GridDiagnostics.Reported is process-wide and carries subscriber failures, unavailable platform features and state that could not be applied.