MVVM and binding
The controls expose the grid's state as bindable properties and take ICommands for what the user does, so a view model can drive a grid without a reference to the control. Columns are declared in code, on a view the view model owns.
Where the columns live
There is no column markup. A view model creates the GridView — columns with typed lambdas, a source over its collection — and exposes it; the view binds MainView to it. That keeps the declaration in one place, compiled and refactorable, and the view model can switch to a card board or a pivot by exposing another view.
public sealed class OrdersViewModel
{
public ObservableCollection<Order> Orders { get; } = new();
// The columns live in the view model, as code: one declaration, typed lambdas.
public GridView<Order> OrdersView { get; }
public Order? SelectedOrder { get; set; }
public bool IsLocked { get; set; }
public GridFilterState? Filters { get; set; }
public ICommand OpenOrderCommand { get; }
public IGridEditPolicy EditPolicy { get; }
// Handed in by the grid (OneWayToSource): scroll, select and edit without the control.
public IGridActions? GridActions { get; set; }
public OrdersViewModel()
{
OrdersView = new GridView<Order>(
[
TypedColumn.Number<Order>("No.", o => o.Id, format: "0").WithKey("id"),
Column.Text<Order>("Customer", o => o.Customer)
.WithKey("customer").Editable((o, v) => o.Customer = v).Fill(),
TypedColumn.Number<Order>("Amount", o => (double)o.Amount,
(o, v) => o.Amount = (decimal)(v ?? 0), "N2").WithKey("amount"),
],
new ObservableItemsSource<Order>(Orders))
.AsDataGrid();
OpenOrderCommand = new RelayCommand(item => Open((Order)item!));
EditPolicy = new OrderEditPolicy(OrdersView);
}
public void ShowNewest(Order order)
{
GridActions?.ScrollTo(order);
GridActions?.SelectItems(new[] { order });
}
private static void Open(Order order) { }
}WPF
<ToolBar DockPanel.Dock="Top">
<Button Content="Undo" Command="{Binding Api.Commands.UndoCommand, ElementName=Orders}" />
<Button Content="Copy" Command="{Binding Api.Commands.CopyCommand, ElementName=Orders}" />
</ToolBar>
<arion:ArionGrid x:Name="Orders"
MainView="{Binding OrdersView}"
SelectedItem="{Binding SelectedOrder}"
IsReadOnly="{Binding IsLocked}"
FilterState="{Binding Filters}"
RowActivatedCommand="{Binding OpenOrderCommand}"
EditPolicy="{Binding EditPolicy}" />The namespace is clr-namespace:ArionUI.Rendering.Wpf;assembly=ArionUI.Rendering.Wpf. The toolbar binds to the grid's own commands through Api.Commands; their CanExecute follows the grid's state.
Actions hands the view model an IGridActions — scroll to an item, select items, begin an edit, set a value, clear filters — through a OneWayToSource binding. WPF markup refuses that binding because the property has no CLR setter, so it is made in code:
public OrdersView()
{
InitializeComponent();
// WPF markup refuses Actions (it has no setter), so this one binding is made in code.
Orders.SetBinding(ArionGrid.ActionsProperty,
new Binding(nameof(OrdersViewModel.GridActions)) { Mode = BindingMode.OneWayToSource });
}Avalonia
<arion:ArionGrid MainView="{Binding OrdersView}"
SelectedItem="{Binding SelectedOrder}"
IsReadOnly="{Binding IsLocked}"
RowActivatedCommand="{Binding OpenOrderCommand}"
EditPolicy="{Binding EditPolicy}"
Actions="{Binding GridActions, Mode=OneWayToSource}" />Avalonia markup accepts the Actions binding directly. With compiled bindings, give the view an x:DataType as usual.
Bindable properties
| Property | Meaning |
|---|---|
MainView | The view: table, cards, tree, pivot, chart, timeline. |
ItemsSource | The data. Replacing it keeps the columns; an ObservableCollection is observed. See Data sources. |
SelectedItem, SelectedItems | The selected rows' items. Two-way by default. |
CurrentItem, CurrentCell | The item and the cell (GridCellRef) under the keyboard cursor. Two-way by default. |
SelectionMode | Cell or Row. |
FilterState | The complete filter state (GridFilterState), mirrored when the user filters. |
VisualTheme, Context | Theme; texts and cultures (see Localisation). |
IsReadOnly, IsLoading, LoadingText | Write protection; the drawn loading state. |
EmptyStateTitle, EmptyStateMessage, EmptyStateActionLabel, EmptyStateCommand | The drawn empty state and its button. |
FrozenColumns, FrozenRows, ShowFilterRow, Zoom | Layout. |
CanUserSortColumns, CanUserResizeColumns, CanUserReorderColumns, CanUserFilterColumns, CanUserGroupColumns | Which gestures the user has. |
Actions | Read by the view model, OneWayToSource. |
Commands into the grid
| Property | Executed with |
|---|---|
RowActivatedCommand | The row's data item, on a double-click. |
CellEditStartingCommand | The cell as a GridCellRef. It observes; it cannot veto — that is the edit policy's job. |
CellEditCommittedCommand | GridCellEditCommittedEventArgs: the hook for saving. |
CellContextMenuRequestedCommand | CellContextMenuEventArgs, whose entry list you may change. |
ClearFiltersRequestedCommand | No parameter. Bound, it replaces the grid's own clearing. |
EditPolicy | Not a command: the IGridEditPolicy asked before every edit. See Editing and validation. |
The same inputs exist without a binding on grid.Api.CommandInputs.
WinForms
ArionGridControl carries the same properties as plain .NET properties and implements INotifyPropertyChanged, so it can take part in WinForms data binding. The command inputs and the edit policy are properties there too. It is built from source for now; see Hosting and rendering.
