FluentDataGrid SelectColumn: No row events with ItemsProvider, DeselectAll reverts to previous state
🐛 Bug Report
SelectColumn in FluentDataGrid does not trigger individual change events for the rows when used with ItemsProvider.
💻 Repro or Code Sample
To reproduce the issue:
- Have a
FluentDataGridwithItemsProviderand aSelectColumn. - Populate it with a few records.
- Select a single record.
- Click on the SelectAll checkbox. Observe that all records are selected, but no individual events are triggered for the rows.
- DeselectAll. The grid reverts to the previous state where only the initially selected records remains selected.
This behavior can be contrasted with using Items, where DeselectAll works as expected. You can see this in the Multi-Select sample at Fluent UI Blazor DataGrid Component.
.razor
<FluentDataGrid @ref="_dataGrid" ItemsProvider="_mergeListProvider" ItemSize="10" ShowHover="true" ResizableColumns="true" TGridItem="ImportFileMerge">
<EmptyContent>
No records were found!
</EmptyContent>
<LoadingContent>
<FluentStack Orientation="Orientation.Vertical" HorizontalAlignment="HorizontalAlignment.Center">
Loading...<br/>
<FluentProgress Width="100%"/>
</FluentStack>
</LoadingContent>
<ChildContent>
<!-- TODO There's a bug where the select-all and deselect-all functionality retains previous selections when using ItemsProvider. -->
<SelectColumn TGridItem="ImportFileMerge"
SelectMode="DataGridSelectMode.Multiple"
SelectFromEntireRow="@true"
Property="@(c => c!.Merge)"
OnSelect="@OnSelectChange" SelectAllChanged="OnSelectAllChanged">
</SelectColumn>
<PropertyColumn Title="Order" Property="@(c => c!.Sequence)" Sortable="true" Align="Align.Start" Width="75px">
</PropertyColumn>
<PropertyColumn Title="ID" Property="@(c => c!.ImportId)" Sortable="true" Align="Align.Start" Width="80px">
</PropertyColumn>
<PropertyColumn Title="Content" Property="@(c => c!.ContentType)" Sortable="true" Align="Align.Start" Width="120px">
</PropertyColumn>
<PropertyColumn Title="Status" Property="@(c => c!.Status)" Sortable="true" Align="Align.Start" Width="150px">
</PropertyColumn>
<PropertyColumn Title="File Name" Property="@(c => c!.Filename)" Sortable="true" Align="Align.Start">
</PropertyColumn>
</ChildContent>
</FluentDataGrid>
.cs
private GridItemsProvider<ImportFileMerge> _mergeListProvider = default!;
private FluentDataGrid<ImportFileMerge> _dataGrid = default!;
protected override async Task OnInitializedAsync()
{
_mergeListProvider = async request =>
{
var response = await GetImportFilesForMergeOperationAsync();
if (response == null)
{
return GridItemsProviderResult.From(items: new List<ImportFileMerge>(), totalItemCount: 0);
}
var entities = response.Value;
if (entities == null)
{
return GridItemsProviderResult.From(items: new List<ImportFileMerge>(), totalItemCount: 0);
}
int count = response.GetODataCount();
_dataGrid.SetLoadingState(false);
return GridItemsProviderResult.From(items: MapToImportFileMerge(entities), totalItemCount: count);
};
await base.OnInitializedAsync();
}
private void OnSelectChange((ImportFileMerge Item, bool Selected) obj)
{
// ... not getting hit by the `SelectAllChanged`
}
private void OnSelectAllChanged(bool? isSelected)
{
// ... can't retrieve the all datagrid items (which would solve the issue)
}
🤔 Expected Behavior
DeselectAll should remove all selections without reverting to the previous state when using ItemsProvider, similar to how it behaves with Items.
https://github.com/user-attachments/assets/00a932dc-4d84-49e3-935b-39c4f3bfac4d
😯 Current Behavior
When DeselectAll is used after SelectAll, the grid reverts to the previous state instead of deselecting all items. The issue seems tied to the lack of individual change events being triggered for each row when using ItemsProvider.
https://github.com/user-attachments/assets/7b44be41-2200-42b8-91f7-248a544188a5
💁 Possible Solution
Consider triggering individual change events for rows when SelectAll or DeselectAll is clicked, even when using ItemsProvider. Alternatively, provide a way to retrieve the latest SelectedItems or the entire DataGrid data.
🔦 Context
This issue is forcing me to remove the SelectAll option, as I need to capture changes in the selected items. Without individual row change events or a method to retrieve SelectedItems or the latest grid data, the current implementation is problematic.
🌍 Your Environment
- Windows 11 Enterprise
- Visual Studio 2022 Enterprise
- Google Chrome
Blazor Hybrid / MAUI
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.DataGrid.EntityFrameworkAdapter" Version="4.9.3" />
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.Emoji" Version="4.6.0" />
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.9.3" />
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.80" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.80" />
It is not possible to make it work with your situation.
You have set up the SelectColum to use manual management via Property and OnSelect. This means when an item is selected, it's internal state is updated so that the selected property (specified by Property function) is set to true.
When using the Select All functionality, only the internal SelectColumn's state is changed. On deselecting all, the internal column state is reset and it is restored to the item's previous state.
I have not figured out a way so the internal item's state could be changed. Maybe @dvoituron knows how to solve this.
I'm on holiday at the moment ;-) I can work on it in a few days.
Hi! Could this also be the reason why my grid with a itemprovider does not work with a OnRowClick or OnRowDoubleClick Event? I have tried both parameters but it seems that nothign is getting triggered.
My grid:
<FluentDataGrid OnRowDoubleClick="@OnView" Style="padding: 1rem" TGridItem="GetAppVersionControlResponse" ItemsProvider="@_appVersionControlProvider" Pagination="@_pagination" @ref="_appVersionControlGrid">
<LoadingContent>
<FluentProgressRing/>
</LoadingContent>
<ChildContent>
<PropertyColumn Property="@(p => p.Environment!.App!.Name)" Title="App" Sortable="false"/>
<PropertyColumn Property="@(p => p.Environment!.Name)" Title="Omgeving" Sortable="false"/>
<PropertyColumn Property="@(p => p.Platform!.Name)" Title="Platform" Sortable="false"/>
<PropertyColumn Property="@(p => p.StoreUrl)" Title="Store url" Sortable="false"/>
</ChildContent>
</FluentDataGrid>
My method:
public void OnView(GetAppVersionControlResponse appVersionControl)
{
Navigation.NavigateTo($"appversioncontrols/{appVersionControl.Id}");
}
It seems like no event is being triggered at all, so something might be going wrong with the binding?
Let me know if this is a complete separate issue, i thought it might be the same because we were both using a itemsprovider. if so i will move this question to a appropriate place.
@PascalVorwerk Could you please create a separate issue for this? Otherwise, it may lead to the current issue being hijacked.
@PascalVorwerk I just added OnRowDoubleClick="@(()=>DemoLogger.WriteLine("Row double clicked!"))" to the Remote Data example in the demos site (which is using the ItemsProvider and it is working as expected there:
So please create a new issue indeed with reproduction code we can run. Especially with the ItemsProvider it is hard to create working examples ourselves.
Sure will do!
I've used the “Remote Data” example and everything is correct using the current features.
In fact, when you use an ItemsProvider, the data is not entirely in memory. It is then necessary to manually store the selected items. You can capture selected lines individually via the OnSelect event. But you can also find out whether the user has clicked on the [All] box via the SelectAll event and its bool? isSelected attribute, which is True for selection of all elements or False when nothing is selected
By adding this SelectColumn and this code, you'll get the desired result.
- The variable
AllSelectedis null when multiple items are selected, or ´True/False` when All / No items are selected. - The variable
SelectedEventIdcontains the items selected (whenAllSelectedis null).
<SelectColumn TGridItem="FoodRecall"
SelectMode="DataGridSelectMode.Multiple"
SelectFromEntireRow="@true"
Property="@IsSelected"
OnSelect="@OnSelectChange"
SelectAllChanged="OnSelectAllChanged">
</SelectColumn>
private bool? AllSelected = null;
private Dictionary<string, bool> SelectedEventId = new ();
private bool IsSelected(FoodRecall item)
{
if (AllSelected is null)
{
return SelectedEventId.ContainsKey(item.Event_Id) ? SelectedEventId[item.Event_Id] : false;
}
return (bool)AllSelected;
}
private void OnSelectChange((FoodRecall Item, bool Selected) obj)
{
Console.WriteLine($"OnSelectChange: {obj.Item.Event_Id} - {obj.Selected}");
AllSelected = null;
var key = obj.Item.Event_Id;
var selected = obj.Selected;
if (SelectedEventId.ContainsKey(key))
{
SelectedEventId[key] = selected;
}
else
{
SelectedEventId.Add(key, selected);
}
}
private void OnSelectAllChanged(bool? isSelected)
{
Console.WriteLine($"OnSelectAllChanged: {isSelected}");
AllSelected = isSelected;
if (AllSelected == false)
{
SelectedEventId.Clear();
}
}
Without knowing which objects/elements will be used with the supplier, we can't include this functionality in the library (without performance problems).
I propose to close this Issue. If you encounter any further problems, we can reopen it.