Shiny Controls v1.0 - The Ultra Control Suite for .NET MAUI & BlazorO...M...G!
DataGrid | Async Detail & IsBusy
Loading a breakdown on demand
Section titled “Loading a breakdown on demand”Set a RowDetailLoader and the content is fetched the first time its row opens. The caret becomes a spinner while the fetch runs — the caret is the button, so that is where the progress belongs — and the detail row shows RowDetailLoadingTemplate (a spinner by default). RowDetailTemplate is not built until the load completes, so it can assume its data has arrived.
@* Blazor *@<DataGrid TItem="Order" Items="orders" RowDetailLoader="LoadLinesAsync" IsBusyChanged="b => busy = b"> <Columns> <PropertyColumn Property="x => x.Reference" /> <PropertyColumn Property="x => x.Total" Format="C0" /> </Columns> <RowDetailTemplate> @foreach (var line in lines[context.Id]) { <div>@line.Sku — @line.Quantity</div> } </RowDetailTemplate> <RowDetailLoadingTemplate> <span class="shiny-dg-busy"></span> Fetching lines… </RowDetailLoadingTemplate></DataGrid>
@code { bool busy; Dictionary<int, List<Line>> lines = new();
async Task LoadLinesAsync(Order order) => lines[order.Id] = await api.GetLinesAsync(order.Id);}<!-- MAUI - the loader fills an observable property; the template binds to it as usual --><shiny:DataGrid ItemsSource="{Binding Orders}" RowDetailLoader="{Binding LoadLines}"> <shiny:DataGrid.RowDetailLoadingTemplate> <DataTemplate x:DataType="local:Order"> <HorizontalStackLayout Spacing="8"> <ActivityIndicator IsRunning="True" WidthRequest="16" HeightRequest="16" /> <Label Text="Fetching lines…" VerticalOptions="Center" /> </HorizontalStackLayout> </DataTemplate> </shiny:DataGrid.RowDetailLoadingTemplate> <shiny:DataGrid.RowDetailTemplate> <DataTemplate x:DataType="local:Order"> <CollectionView ItemsSource="{Binding Lines}" /> </DataTemplate> </shiny:DataGrid.RowDetailTemplate> <shiny:DataGridColumn Title="Reference" PropertyName="Reference" /></shiny:DataGrid>- Each item loads once.
InvalidateRowDetail(item)forgets it so the next expand refetches — and reloads immediately if that row is open right now. Passnullfor every row. - A throw collapses the row again and raises
RowDetailLoadFailed, mirroringChildrenLoadFailed. ExpandAlldoes start detail loads (one per expanded row, so the work is bounded by what is on screen) — unlike a lazy tree, whose depth is not.
IsBusy
Section titled “IsBusy”IsBusy is true while any row is waiting on ChildrenLoader or RowDetailLoader. On MAUI it is a read-only BindableProperty, so bind a page-level indicator straight to it; on Blazor read the property and subscribe to IsBusyChanged. IsRowBusy(item) is the per-row form.
<!-- MAUI --><ActivityIndicator IsRunning="{Binding Source={x:Reference Grid}, Path=IsBusy}" />Next Steps
Section titled “Next Steps”- Detail (Breakdown) Rows — a full-width row under an expanded row
- TreeDataGrid — the same grid in hierarchy mode


