diff --git a/README.md b/README.md
index 5c77f99..4841a96 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@
| Preview | Component | Description | Docs |
|---|---|---|---|
|
| **SuperDataGrid** | Virtualized data grid — frozen columns/rows, hierarchical lazy-loading rows, reordering, resizing, filtering, sorting, inline editing, row selection, settings persistence | [📖 SUPERDATAGRID.md](SUPERDATAGRID.md) |
-|
| **SuperDataGrid Exporter** | Optional CSV and Excel export extension for complete filtered, sorted and virtualized datasets | [đź“– SUPERDATAGRIDEXPORTER.md](SUPERDATAGRIDEXPORTER.md) |
+|
| **SuperDataGrid Exporter** | Optional CSV and Excel export extension for checked rows, including filtered, sorted and virtualized data | [đź“– SUPERDATAGRIDEXPORTER.md](SUPERDATAGRIDEXPORTER.md) |
|
| **SuperLayout** | Responsive app layout — header, sidebar, body, footer, chat panel with collapsible sidebar | [📖 SUPERLAYOUT.md](SUPERLAYOUT.md) |
|
| **SuperContext** | Context-aware tabs and contextual panels — discover components by runtime type and zone, render one or many contexts, and isolate host state per instance | [📖 SUPERCONTEXT.md](SUPERCONTEXT.md) |
|
| **SuperTabs** | Dynamic tabbed interface — badges, closable tabs, lazy loading, persistence (URL + localStorage), keyboard navigation, service-driven management | [📖 SUPERTABS.md](SUPERTABS.md) |
diff --git a/SUPERDATAGRIDEXPORTER.md b/SUPERDATAGRIDEXPORTER.md
index 3a7034d..b74ed76 100644
--- a/SUPERDATAGRIDEXPORTER.md
+++ b/SUPERDATAGRIDEXPORTER.md
@@ -1,8 +1,8 @@
# SuperDataGrid CSV and Excel exporter
-The **SuperBlazorComponents.DataGridExporter** extension exports the complete
-filtered and sorted view of a **SuperDataGrid**, including rows that are not
-currently rendered by virtualization.
+The **SuperBlazorComponents.DataGridExporter** extension exports the rows that
+are checked in a **SuperDataGrid**. A checked row is captured when generation
+starts, so it remains exportable even if a later filter hides it.
## Installation
@@ -74,11 +74,23 @@ actions to the right of the grid header. The grid keeps the custom header area
separated from its built-in actions. Omit **IconOnly** to display the icon and
text together.
-Only currently visible columns are exported, in their current order. The
-exporter captures the grid filters and sort order once, then reads the complete
-result from **ItemsProvider** in batches. Hierarchical grids export root items
-only. The default batch size is 200 rows and can be changed with
-**SuperDataGridExporterOptions.BatchSize**.
+Only currently visible columns are exported, in their current order. If rows
+are selected individually, those exact objects are exported in selection order.
+If **Tout sélectionner** is used, the exporter reads all rows matching the
+captured filters and sort order from **ItemsProvider**, in batches of 200 by
+default, and skips rows explicitly unchecked afterwards. The batch size can be
+changed with **SuperDataGridExporterOptions.BatchSize**.
+
+The dialog is still opened when nothing is checked, but immediately displays:
+“Veuillez cocher au moins une ligne pour effectuer l’export.” Check a row and
+choose **Réessayer** to continue; no file is created while the selection is
+empty. Selection is frozen at the start of generation.
+
+For hierarchical grids, every checked row is exported, including checked child
+rows. Unchecked or unexpanded children are not invented by the exporter.
+Virtualized providers should implement **IDataItem.KeyValue** with a stable,
+unique key. The same key is used to apply exclusions and deduplicate rows when
+the provider materializes a new object instance for each batch.
## Custom columns
@@ -120,5 +132,7 @@ Set **Exportable="false"** to exclude a visible column.
sizes columns to their content. Excel cannot freeze columns from the right.
- One worksheet supports at most 1,048,575 exported data rows because the
header occupies the first Excel row.
-- The grid must use **ItemsProvider**; the currently rendered virtualized items
- alone are intentionally never treated as the complete dataset.
+- **ItemsProvider** is required for **Tout sélectionner** so the exporter can
+ retrieve every filtered row; it never treats the currently rendered
+ virtualized items alone as the complete dataset. Individually checked objects
+ are exported from the immutable selection captured at the start.
diff --git a/src/SuperBlazorComponents.DataGridExporter/Components/SuperDataGridExportDialog.razor b/src/SuperBlazorComponents.DataGridExporter/Components/SuperDataGridExportDialog.razor
index 6f65602..039adba 100644
--- a/src/SuperBlazorComponents.DataGridExporter/Components/SuperDataGridExportDialog.razor
+++ b/src/SuperBlazorComponents.DataGridExporter/Components/SuperDataGridExportDialog.razor
@@ -65,6 +65,9 @@
private bool _initialized;
private SuperDataGridExportResult? _result;
+ private const string SelectionRequiredMessage =
+ "Veuillez cocher au moins une ligne pour effectuer l’export.";
+
private string Extension => Format == SuperDataGridExportFormat.Csv ? ".csv" : ".xlsx";
protected override void OnParametersSet()
@@ -75,6 +78,8 @@
_fileName = string.IsNullOrWhiteSpace(DefaultFileName)
? $"export-{DateTime.Now:yyyyMMdd-HHmmss}"
: DefaultFileName;
+ if (!Grid.CaptureSelectionSnapshot().HasSelection)
+ _error = SelectionRequiredMessage;
_initialized = true;
}
diff --git a/src/SuperBlazorComponents.DataGridExporter/SuperBlazorComponents.DataGridExporter.csproj b/src/SuperBlazorComponents.DataGridExporter/SuperBlazorComponents.DataGridExporter.csproj
index 0263fb7..33fb83a 100644
--- a/src/SuperBlazorComponents.DataGridExporter/SuperBlazorComponents.DataGridExporter.csproj
+++ b/src/SuperBlazorComponents.DataGridExporter/SuperBlazorComponents.DataGridExporter.csproj
@@ -5,7 +5,7 @@
enable
SuperBlazorComponents.DataGridExporter
SuperBlazorComponents.DataGridExporter
- 2.0.8
+ 2.0.9
SuperBlazorComponents.DataGridExporter
SuperBlazorComponents DataGrid Exporter
Appliman
diff --git a/src/SuperBlazorComponents.DataGridExporter/SuperDataGridExportService.cs b/src/SuperBlazorComponents.DataGridExporter/SuperDataGridExportService.cs
index 3a8d1e4..14534b5 100644
--- a/src/SuperBlazorComponents.DataGridExporter/SuperDataGridExportService.cs
+++ b/src/SuperBlazorComponents.DataGridExporter/SuperDataGridExportService.cs
@@ -33,8 +33,18 @@ public Task ExportAsync(
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(grid);
- if (grid.ItemsProvider is null)
- throw new InvalidOperationException("The grid must define an ItemsProvider to export all rows.");
+
+ // Capture both parts of the view before starting any asynchronous work. This
+ // makes an export deterministic even when the user changes the grid while it
+ // is being generated.
+ var selection = grid.CaptureSelectionSnapshot();
+ if (!selection.HasSelection)
+ throw new InvalidOperationException(
+ "Veuillez cocher au moins une ligne pour effectuer l’export.");
+
+ if (selection.AllSelected && grid.ItemsProvider is null)
+ throw new InvalidOperationException(
+ "The grid must define an ItemsProvider to export all selected rows.");
var columns = ExportColumnResolver.Resolve(grid);
var query = grid.CaptureQuerySnapshot();
@@ -47,15 +57,17 @@ public Task ExportAsync(
format,
fileName,
(path, token) => format == SuperDataGridExportFormat.Csv
- ? WriteCsvAsync(path, grid.ItemsProvider, query, columns, token)
- : WriteExcelAsync(path, grid.ItemsProvider, query, columns, frozenColumnCount, token),
+ ? WriteCsvAsync(path, grid, grid.ItemsProvider, query, selection, columns, token)
+ : WriteExcelAsync(path, grid, grid.ItemsProvider, query, selection, columns, frozenColumnCount, token),
cancellationToken);
}
private async Task WriteCsvAsync(
string path,
- GridItemsProvider provider,
+ SuperDataGrid grid,
+ GridItemsProvider? provider,
SuperDataGridQuerySnapshot query,
+ SuperDataGridSelectionSnapshot selection,
IReadOnlyList> columns,
CancellationToken cancellationToken)
{
@@ -75,7 +87,7 @@ private async Task WriteCsvAsync(
await csv.NextRecordAsync();
var rowCount = 0;
- await foreach (var item in ReadAllAsync(provider, query, cancellationToken))
+ await foreach (var item in ReadSelectedAsync(grid, provider, query, selection, cancellationToken))
{
foreach (var column in columns)
{
@@ -94,8 +106,10 @@ private async Task WriteCsvAsync(
private async Task WriteExcelAsync(
string path,
- GridItemsProvider provider,
+ SuperDataGrid grid,
+ GridItemsProvider? provider,
SuperDataGridQuerySnapshot query,
+ SuperDataGridSelectionSnapshot selection,
IReadOnlyList> columns,
int frozenColumnCount,
CancellationToken cancellationToken)
@@ -111,7 +125,7 @@ private async Task WriteExcelAsync(
}
var rowCount = 0;
- await foreach (var item in ReadAllAsync(provider, query, cancellationToken))
+ await foreach (var item in ReadSelectedAsync(grid, provider, query, selection, cancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
if (rowCount >= ExcelMaximumDataRows)
@@ -138,6 +152,61 @@ private async Task WriteExcelAsync(
return rowCount;
}
+ private async IAsyncEnumerable ReadSelectedAsync(
+ SuperDataGrid grid,
+ GridItemsProvider? provider,
+ SuperDataGridQuerySnapshot query,
+ SuperDataGridSelectionSnapshot selection,
+ [EnumeratorCancellation] CancellationToken cancellationToken)
+ {
+ var emittedKeys = new HashSet