Views

The same records read as a table, a card board, a tree or a pivot. A view is an object you assign to the control — not a mode you put it in — which is why switching one costs no reload and no second copy of the data.

Switching
var control = new ArionGrid(gridView);

control.MainView = cardView;    // same records, drawn as cards
control.MainView = pivotView;   // same records, summarised (Pro)

Each view owns its data projection, its drawing model and its behaviour — including its own header hit testing, because a pivot header is two-dimensional and a table header is not. The engine below them does not know which one it is drawing. The same ArionGrid type and the same MainView property exist on Avalonia and WPF; the WinForms host calls its control ArionGridControl.

Core and Pro

Table, card board and tree are in the open engine. The pivot is the Pro package ArionUI.Pivot, which builds on ArionUI.Grouping; chart, timeline and planning views are Pro packages too. All of them are free during the preview and installed with dotnet add package <id> --prerelease.

Table — GridView<T>

The default, and the one Columns covers. Beyond the columns themselves it takes group aggregates, row-level style rules and a row height:

GridView
var gridView = new GridView<Account>(
    columns,
    new ListItemsSource<Account>(accounts),
    groupAggregates: [new AggregateSpec("Sum", AggregateFn.Sum,
                                        o => ((Account)o).Amount, v => v.ToString("N2"))],
    rowRules: [new StyleRule(o => ((Account)o).Amount < 0,
                             new CellStyle(Foreground: Palette.Red))],
    rowHeight: 22);

AsDataGrid() turns on the usual set in one call — filter row, row header, selection lane, group band, summary footer and group subtotals. The same pieces can be switched on one by one, each call returning the view:

Options
gridView.WithFilterRow()          // the drawn filter row under the headers
        .WithGroupPanel()         // the drag-a-column-here band (needs grouping)
        .WithRowHeader()          // the marker lane on the left
        .WithSummaryFooter()      // grand totals
        .WithGroupSummaries()     // subtotals in group headers and footers
        .WithQuickSearch();       // the projection behind the quick-search palette

gridView.WithRowDetails(height: 120, composition: detailCells);   // master-detail

Grouping is enabled with one extra call, and only then do the group band, group rows and subtotals do anything:

Pro package ArionUI.Grouping (free during the preview)
gridView.EnableGrouping();

Card board — CardView

A card is a composition, built with the same Cell vocabulary as a composed cell. The template carries the card's size and the gap between cards; the view pairs it with a source.

CardView
var template = new CardTemplate(cardWidth: 210, cardHeight: 128, gap: 12,
    Cell.Stack(7,
        Cell.Row(6,
            Cell.Text<Account>(a => a.Name, fontSize: 13, weight: FontWeight.SemiBold),
            Cell.Text<Account>(a => $"#{a.Id}", fontSize: 11, color: Palette.TextDim,
                               align: TextAlign.Right, width: 52)),
        Cell.Text<Account>(a => a.Amount.ToString("N2") + " €",
                           fontSize: 16, weight: FontWeight.SemiBold, height: 24,
                           colorFn: a => a.Amount < 0 ? Palette.Red : Palette.Text),
        Cell.Row(8,
            Cell.Badge<Account>(a => Label(a.Status), a => Colour(a.Status)),
            Cell.Spacer(),
            Cell.Text<Account>(a => a.Booked.ToString("dd.MM.yyyy"),
                               fontSize: 12, color: Palette.TextDim,
                               align: TextAlign.Right, width: 78)),
        Cell.Progress<Account>(a => (double)a.Amount / 100_000, height: 8)));

var cardView = new CardView(template, new ListItemsSource<Account>(accounts));

Cards virtualize the same way rows do: a board over a million records draws the cards that fit and nothing else.

Pivot — PivotView

Pro package ArionUI.Pivot (free during the preview)
dotnet add package ArionUI.Pivot --prerelease

A pivot needs axes and measures. GroupSpec says what a level groups by and how its key reads; AggregateSpec says what is summarised and how the result formats. Both take object and cast — the pivot machinery is deliberately untyped, because a pivot's shape is chosen at runtime.

PivotView
var spec = new PivotSpec(
    // row axes
    [new GroupSpec(o => ((Account)o).Region),
     new GroupSpec(o => ((Account)o).Status, k => Label((Status)k!))],
    // column axes
    [new GroupSpec(o => ((Account)o).Booked.Year),
     new GroupSpec(o => (((Account)o).Booked.Month + 2) / 3, k => "Q" + k)],
    // measures
    [new AggregateSpec("Sum", AggregateFn.Sum,
                       o => ((Account)o).Amount, v => v.ToString("N2")),
     new AggregateSpec("Count", AggregateFn.Count)]);

var pivotView = new PivotView(
    new SortFilterSource(new ListItemsSource<Account>(accounts)), spec);

Both axes collapse, and a double-click on a value cell raises DrillDownRequested with the records behind it. Passing a SortFilterSource rather than the raw list is what lets a filter applied in the table carry into the pivot — the sources chain rather than duplicate.

Note

For a pivot the user reshapes at runtime, build a PivotFieldModel — a catalogue of available fields and measures — assign it to PivotView.Fields and call BuildSpec() on it. The pivot field list pane edits that model.

Tree — TreeView<T>

A hierarchy rather than a flat list: the same columns, over TreeNode roots, with Column.Tree supplying the indented label column. A node with children can be collapsed and expanded.

TreeView
var roots = new List<TreeNode>
{
    new() { Item = new Dept("Company", 100), Children =
    [
        new TreeNode { Item = new Dept("Sales", 40) },
        new TreeNode { Item = new Dept("Engineering", 60) },
    ] },
};

var tree = new TreeView<Dept>(
    [Column.Tree<Dept>("Name", d => d.Name).Fill(),
     TypedColumn.Number<Dept>("Budget", d => (double?)d.Budget)],
    roots);

Other views

BoardView
A board of tiles and controls — text boxes, check boxes, buttons, tables inside tiles — running on the grid's own engine. Open engine.
ChartView
Charts from a model or from a cell selection. Pro package ArionUI.Charts; ArionUI.Charts3D adds GPU surface charts (labelled preview in the package list).
TimelineView
A time axis with lanes and activity bars. Pro package ArionUI.Timeline.
Planning matrix
Time-axis columns with editable measures and roll-ups, generated as ordinary columns of the table by PlanningBuilder<T>. Pro package ArionUI.Planning.

Why they share everything below

Sorting and filtering are projections in front of your source — SortFilterSource, and with the Pro packages GroupingSource or PivotSource behind it — and each of them is an index mapping, not a copy. Select-all and re-sorting are index work. The renderer knows about none of it; it asks for the item at a visible index and draws it.

That is the reason a view swap is cheap, and it is the same reason a million records draw as fast as a thousand. The layering that makes it hold is on Architecture.