Build a reporting grid
Group records, filter from the column headers, count values with facets, add aggregates and cell presenters, and export what the reader selected.
A reporting grid is a CanopyDataGridView with a Reporting definition. Pages written from C# pick the reporting runtime by themselves; JavaScript hosts import @evotecit/canopyx/reporting and its reporting.css.
Group, filter and summarise
records below is your own data. Everything else is the view: grouping by site, filters in the column headers, a facet with value counts, a status bar, selection checkboxes and an average over the patch column.
using CanopyX;
using CanopyX.Html;
var devices = new CanopyDataGridView("devices", "Devices") {
Selection = CanopySelectionMode.Multiple,
StatusBar = true,
Noun = "devices",
Reporting = new CanopyGridReporting {
Grouping = new CanopyGridGrouping("site"),
HeaderFilters = true,
SelectionControls = true
}
};
devices.Columns.Add(new CanopyColumn("device", "Device") {
Sortable = true, Presenter = CanopyGridPresenter.Identifier(mono: true)
});
devices.Columns.Add(new CanopyColumn("site", "Site") { Sortable = true });
devices.Columns.Add(new CanopyColumn("patch", "Patch level") {
Kind = CanopyColumnKind.Number,
Sortable = true,
Presenter = CanopyGridPresenter.Progress()
});
devices.Columns.Add(new CanopyColumn("encrypted", "Encrypted") {
Sortable = true, Presenter = CanopyGridPresenter.Boolean()
});
devices.Reporting.FacetColumns.Add("site");
devices.Reporting.Aggregates.Add(new CanopyGridAggregate(
"patch-average", "patch", CanopyGridAggregateOperation.Average,
"Average patch level"));
foreach (var (name, site, patch, encrypted) in records) {
var row = new CanopyGridRow(name.ToLowerInvariant());
row.Cells["device"] = name;
row.Cells["site"] = site;
row.Cells["patch"] = new CanopyCell(patch) {
Text = $"{patch}%",
Tone = patch < 70 ? CanopyTone.Danger : CanopyTone.None
};
row.Cells["encrypted"] = encrypted;
devices.Rows.Add(row);
}
devices.SaveHtmlPage("devices.html");- Grouping leads the order with the grouping column. Group headings show full group counts, and collapsing a group hides its rows without removing them from selections or exports.
- Header filters open from a column's menu. Text columns offer a value list with counts over the complete query; number and date columns offer ranges.
- Facets declared with
FacetColumnssupply those value counts. A hosted source answers them itself. - Aggregates compute over the complete filtered query, not the rows on screen.
Present cells
Presenters change how a cell looks, never its value: sorting, search and export still use the raw value. Progress draws a bar for a numeric column, Boolean a check mark, and Identifier a monospaced name with optional secondary text. Others include State, StatusStrip, SparklineBars, SparklineLine, Link, List and LongText.
A cell's Tone names a state (success, warning, danger, info, muted or one of your own) and the theme maps it to colours; the device grid above marks low patch levels with danger.
Export what the reader chose
The export menu distinguishes filtered results, selected records and all results, and exports use the accepted query and stable record IDs, independently of the rows on screen. CSV and copying run in the browser; larger Office files go through your own onExport callback, which receives the same captured rows.
The device inventory demo combines all of this over 1,200 records with a 14-day status strip and sign-in sparklines.