1 - 3 of 3 items
3 items
Self referencing allows a DataGrid row to render child rows. Use a self reference with @ref to control expansion programmatically.
ExpandRowTrigger and ReadChildData for hierarchical rows, and calls ExpandRow, CollapseRow, ExpandAllRows, and CollapseAllRows through a DataGrid self-reference.
<Button Color="Color.Primary" Margin="Margin.Is2.FromEnd" Disabled="@(!CanToggleSelectedRow)" Clicked="">Expand Selected</Button> <Button Color="Color.Secondary" Margin="Margin.Is2.FromEnd" Disabled="@(!CanToggleSelectedRow)" Clicked="">Collapse Selected</Button> <Button Color="Color.Success" Margin="Margin.Is2.FromEnd" Clicked="">Expand All</Button> <Button Color="Color.Warning" Clicked="">Collapse All</Button> <DataGrid @ref="dataGridRef" TItem="SelfReferenceEmployee" Data="" ExpandTrigger="DataGridExpandTrigger.RowAndToggleClick" ExpandRowTrigger="OnExpandRowTrigger" ReadChildData="OnReadChildData" @bind-SelectedRow="" Responsive ShowPager> <DataGridColumn Field="@nameof( SelfReferenceEmployee.FullName )" Caption="Employee" Width="Width.Px( 280 )" /> <DataGridColumn Field="@nameof( SelfReferenceEmployee.City )" Caption="City" /> <DataGridColumn Field="@nameof( SelfReferenceEmployee.Salary )" Caption="Salary" DisplayFormat="{0:C}" /> <DataGridCheckColumn Field="@nameof( SelfReferenceEmployee.IsActive )" Caption="Active" /> </DataGrid>
@code { [Inject] public EmployeeData EmployeeData { get; set; } private DataGrid<SelfReferenceEmployee> dataGridRef; private SelfReferenceEmployee selectedEmployee; private List<SelfReferenceEmployee> rootItems = new(); private Dictionary<int, List<SelfReferenceEmployee>> childLookup = new(); private bool CanToggleSelectedRow => selectedEmployee is not null; protected override async Task OnInitializedAsync() { var employees = ( await EmployeeData.GetDataAsync().ConfigureAwait( false ) ).Take( 24 ).ToList(); BuildSelfReferenceData( employees ); await base.OnInitializedAsync(); } private void BuildSelfReferenceData( IReadOnlyList<Employee> employees ) { var selfReferenceEmployees = employees.Select( x => new SelfReferenceEmployee { Id = x.Id, FullName = $"{x.FirstName} {x.LastName}", City = x.City, Salary = x.Salary, IsActive = x.IsActive, } ).ToList(); var roots = selfReferenceEmployees.Take( 3 ).ToList(); var levelOneNodes = new List<SelfReferenceEmployee>(); var currentIndex = roots.Count; for ( var rootIndex = 0; rootIndex < roots.Count && currentIndex < selfReferenceEmployees.Count; rootIndex++ ) { for ( var childIndex = 0; childIndex < 3 && currentIndex < selfReferenceEmployees.Count; childIndex++ ) { var child = selfReferenceEmployees[currentIndex++]; child.ParentId = roots[rootIndex].Id; levelOneNodes.Add( child ); } } for ( var levelOneIndex = 0; levelOneIndex < levelOneNodes.Count && currentIndex < selfReferenceEmployees.Count; levelOneIndex++ ) { for ( var childIndex = 0; childIndex < 2 && currentIndex < selfReferenceEmployees.Count; childIndex++ ) { var child = selfReferenceEmployees[currentIndex++]; child.ParentId = levelOneNodes[levelOneIndex].Id; } } childLookup = selfReferenceEmployees .Where( x => x.ParentId.HasValue ) .GroupBy( x => x.ParentId!.Value ) .ToDictionary( x => x.Key, x => x.ToList() ); rootItems = selfReferenceEmployees.Where( x => !x.ParentId.HasValue ).ToList(); } private bool OnExpandRowTrigger( DataGridExpandRowTriggerEventArgs<SelfReferenceEmployee> args ) { args.Expandable = childLookup.ContainsKey( args.Item.Id ); return true; } private void OnReadChildData( DataGridReadChildDataEventArgs<SelfReferenceEmployee> args ) { args.Data = childLookup.TryGetValue( args.Item.Id, out var children ) ? children : Enumerable.Empty<SelfReferenceEmployee>(); } private Task ExpandSelectedRow() => selectedEmployee is null ? Task.CompletedTask : dataGridRef.ExpandRow( selectedEmployee ); private Task CollapseSelectedRow() => selectedEmployee is null ? Task.CompletedTask : dataGridRef.CollapseRow( selectedEmployee ); private Task ExpandAllRows() => dataGridRef.ExpandAllRows(); private Task CollapseAllRows() => dataGridRef.CollapseAllRows(); private sealed class SelfReferenceEmployee { public int Id { get; set; } public int? ParentId { get; set; } public string FullName { get; set; } public string City { get; set; } public decimal Salary { get; set; } public bool IsActive { get; set; } } }
Editable rows with DataGridEditMode.Inline. Parent and child rows can both be edited.
<Button Color="Color.Success" Margin="Margin.Is2.FromEnd" Clicked="">Expand All</Button> <Button Color="Color.Warning" Margin="Margin.Is4.FromEnd" Clicked="">Collapse All</Button> <DataGrid @ref="dataGridRef" TItem="SelfReferenceEmployee" Data="" ExpandTrigger="DataGridExpandTrigger.RowAndToggleClick" ExpandRowTrigger="OnExpandRowTrigger" ReadChildData="OnReadChildData" RowUpdated="OnRowUpdated" Editable EditMode="DataGridEditMode.Inline" Responsive ShowPager> <DataGridColumns> <DataGridColumn Field="@nameof( SelfReferenceEmployee.FullName )" Caption="Employee" Width="Width.Px( 280 )" Editable /> <DataGridColumn Field="@nameof( SelfReferenceEmployee.City )" Caption="City" Editable /> <DataGridNumericColumn Field="@nameof( SelfReferenceEmployee.Salary )" Caption="Salary" DisplayFormat="{0:C}" Editable /> <DataGridCheckColumn Field="@nameof( SelfReferenceEmployee.IsActive )" Caption="Active" Editable /> <DataGridCommandColumn NewCommandAllowed="false" DeleteCommandAllowed="false" /> </DataGridColumns> </DataGrid> @if ( lastUpdatedEmployee is not null ) { <Alert Color="Color.Info" Margin="Margin.Is3.FromTop"> Last updated row: <strong>@lastUpdatedEmployee.FullName</strong> (@lastUpdatedEmployee.City) </Alert> }
@code { [Inject] public EmployeeData EmployeeData { get; set; } private DataGrid<SelfReferenceEmployee> dataGridRef; private SelfReferenceEmployee lastUpdatedEmployee; private List<SelfReferenceEmployee> rootItems = new(); private Dictionary<int, List<SelfReferenceEmployee>> childLookup = new(); protected override async Task OnInitializedAsync() { var employees = ( await EmployeeData.GetDataAsync().ConfigureAwait( false ) ).Take( 24 ).ToList(); BuildSelfReferenceData( employees ); await base.OnInitializedAsync(); } private void BuildSelfReferenceData( IReadOnlyList<Employee> employees ) { var selfReferenceEmployees = employees.Select( x => new SelfReferenceEmployee { Id = x.Id, FullName = $"{x.FirstName} {x.LastName}", City = x.City, Salary = x.Salary, IsActive = x.IsActive, } ).ToList(); var roots = selfReferenceEmployees.Take( 3 ).ToList(); var levelOneNodes = new List<SelfReferenceEmployee>(); var currentIndex = roots.Count; for ( var rootIndex = 0; rootIndex < roots.Count && currentIndex < selfReferenceEmployees.Count; rootIndex++ ) { for ( var childIndex = 0; childIndex < 3 && currentIndex < selfReferenceEmployees.Count; childIndex++ ) { var child = selfReferenceEmployees[currentIndex++]; child.ParentId = roots[rootIndex].Id; levelOneNodes.Add( child ); } } for ( var levelOneIndex = 0; levelOneIndex < levelOneNodes.Count && currentIndex < selfReferenceEmployees.Count; levelOneIndex++ ) { for ( var childIndex = 0; childIndex < 2 && currentIndex < selfReferenceEmployees.Count; childIndex++ ) { var child = selfReferenceEmployees[currentIndex++]; child.ParentId = levelOneNodes[levelOneIndex].Id; } } childLookup = selfReferenceEmployees .Where( x => x.ParentId.HasValue ) .GroupBy( x => x.ParentId!.Value ) .ToDictionary( x => x.Key, x => x.ToList() ); rootItems = selfReferenceEmployees.Where( x => !x.ParentId.HasValue ).ToList(); } private bool OnExpandRowTrigger( DataGridExpandRowTriggerEventArgs<SelfReferenceEmployee> args ) { args.Expandable = childLookup.ContainsKey( args.Item.Id ); return true; } private void OnReadChildData( DataGridReadChildDataEventArgs<SelfReferenceEmployee> args ) { args.Data = childLookup.TryGetValue( args.Item.Id, out var children ) ? children : Enumerable.Empty<SelfReferenceEmployee>(); } private Task OnRowUpdated( SavedRowItem<SelfReferenceEmployee, Dictionary<string, object>> args ) { lastUpdatedEmployee = args.NewItem; return Task.CompletedTask; } private Task ExpandAllRows() => dataGridRef.ExpandAllRows(); private Task CollapseAllRows() => dataGridRef.CollapseAllRows(); private sealed class SelfReferenceEmployee { public int Id { get; set; } public int? ParentId { get; set; } public string FullName { get; set; } public string City { get; set; } public decimal Salary { get; set; } public bool IsActive { get; set; } } }
ExpandTemplate when you need full control over self-reference expand rendering. The first regular column with ExpandTemplate becomes the self-reference host, and the template context exposes Item, Level, Expandable, Expanded, and Toggle().
<DataGrid TItem="SelfReferenceEmployee" Data="" ExpandTrigger="DataGridExpandTrigger.ToggleClick" ExpandRowTrigger="OnExpandRowTrigger" ReadChildData="OnReadChildData" Responsive ShowPager> <DataGridColumns> <DataGridColumn Field="@nameof( SelfReferenceEmployee.FullName )" Caption="Employee" Width="Width.Px( 300 )"> <ExpandTemplate Context="row"> <Span Display="Display.InlineFlex" VerticalAlignment="VerticalAlignment.Middle" TextOverflow="TextOverflow.NoWrap"> @if ( row.Level > 0 ) { <Span Display="Display.InlineBlock" Width="@Width.Rem( row.Level )"></Span> } @if ( row.Expandable ) { <Button Size="Size.ExtraSmall" Color="Color.Light" Margin="Margin.Is2.FromEnd" Clicked="@(() => row.Toggle())"> <Icon Name="@(row.Expanded ? IconName.ChevronDown : IconName.ChevronRight)" /> </Button> } else { <Span Display="Display.InlineBlock" Width="@Width.Rem( 1 )" Margin="Margin.Is2.FromEnd"></Span> } </Span> </ExpandTemplate> <DisplayTemplate Context="cell"> <Span TextWeight="TextWeight.SemiBold">@cell.DisplayValue</Span> </DisplayTemplate> </DataGridColumn> <DataGridColumn Field="@nameof( SelfReferenceEmployee.City )" Caption="City" /> <DataGridNumericColumn Field="@nameof( SelfReferenceEmployee.Salary )" Caption="Salary" DisplayFormat="{0:C}" /> </DataGridColumns> </DataGrid>
@code { [Inject] public EmployeeData EmployeeData { get; set; } private List<SelfReferenceEmployee> rootItems = new(); private Dictionary<int, List<SelfReferenceEmployee>> childLookup = new(); protected override async Task OnInitializedAsync() { var employees = ( await EmployeeData.GetDataAsync().ConfigureAwait( false ) ).Take( 24 ).ToList(); BuildSelfReferenceData( employees ); await base.OnInitializedAsync(); } private void BuildSelfReferenceData( IReadOnlyList<Employee> employees ) { var selfReferenceEmployees = employees.Select( x => new SelfReferenceEmployee { Id = x.Id, FullName = $"{x.FirstName} {x.LastName}", City = x.City, Salary = x.Salary, } ).ToList(); var roots = selfReferenceEmployees.Take( 3 ).ToList(); var levelOneNodes = new List<SelfReferenceEmployee>(); var currentIndex = roots.Count; for ( var rootIndex = 0; rootIndex < roots.Count && currentIndex < selfReferenceEmployees.Count; rootIndex++ ) { for ( var childIndex = 0; childIndex < 3 && currentIndex < selfReferenceEmployees.Count; childIndex++ ) { var child = selfReferenceEmployees[currentIndex++]; child.ParentId = roots[rootIndex].Id; levelOneNodes.Add( child ); } } for ( var levelOneIndex = 0; levelOneIndex < levelOneNodes.Count && currentIndex < selfReferenceEmployees.Count; levelOneIndex++ ) { for ( var childIndex = 0; childIndex < 2 && currentIndex < selfReferenceEmployees.Count; childIndex++ ) { var child = selfReferenceEmployees[currentIndex++]; child.ParentId = levelOneNodes[levelOneIndex].Id; } } childLookup = selfReferenceEmployees .Where( x => x.ParentId.HasValue ) .GroupBy( x => x.ParentId!.Value ) .ToDictionary( x => x.Key, x => x.ToList() ); rootItems = selfReferenceEmployees.Where( x => !x.ParentId.HasValue ).ToList(); } private bool OnExpandRowTrigger( DataGridExpandRowTriggerEventArgs<SelfReferenceEmployee> args ) { args.Expandable = childLookup.ContainsKey( args.Item.Id ); return true; } private void OnReadChildData( DataGridReadChildDataEventArgs<SelfReferenceEmployee> args ) { args.Data = childLookup.TryGetValue( args.Item.Id, out var children ) ? children : Enumerable.Empty<SelfReferenceEmployee>(); } private sealed class SelfReferenceEmployee { public int Id { get; set; } public int? ParentId { get; set; } public string FullName { get; set; } public string City { get; set; } public decimal Salary { get; set; } } }
See the documentation below for a complete reference to all of the props and classes available to the components mentioned here.