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.
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.
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:
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:
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-detailGrouping is enabled with one extra call, and only then do the group band, group rows and subtotals do anything:
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.
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
dotnet add package ArionUI.Pivot --prereleaseA 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.
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.
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.
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.Charts3Dadds 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 packageArionUI.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.
