New to Telerik UI for Blazor? Start a free 30-day trial
How to Automatically Expand the Currently Edited Row
Updated on Jan 27, 2026
Environment
| Product | Grid for Blazor |
Description
I want to automatically expand the detail template of the row that is currently being edited or the newly inserted item in the TelerikGrid.
Solution
To achieve automatic expansion of a detail template for the edited or newly inserted item, use the OnEdit and OnCreate events of the TelerikGrid with the SetStateAsync method. Below is an example implementation:
Expand currently edited row
<TelerikGrid @ref="@GridRef"
Data="@GridData"
TItem="@Product"
Pageable="true"
PageSize="10"
Sortable="true"
EditMode="@GridEditMode"
Navigable="true"
OnUpdate="UpdateProduct"
OnDelete="DeleteProduct"
OnCreate="CreateProduct"
OnEdit="@OnEdit"
OnStateInit="@OnProductGridStateInit">
<GridToolBarTemplate>
<GridCommandButton Command="Add" Icon="@SvgIcon.Plus">Add Product</GridCommandButton>
</GridToolBarTemplate>
<GridColumns>
<GridColumn Field=@nameof(Product.ProductName) Title="Product Name" />
<GridColumn Field=@nameof(Product.UnitPrice) Title="Unit Price" />
<GridColumn Field=@nameof(Product.UnitsInStock) Title="Units in Stock" />
<GridColumn Field=@nameof(Product.CreatedAt) Title="Date Created" />
<GridColumn Field=@nameof(Product.Discontinued) Title="Discontinued" Width="150px" />
<GridCommandColumn Width="200px">
@if (GridEditMode != GridEditMode.Incell)
{
<GridCommandButton Command="Edit" Icon="@SvgIcon.Pencil">Edit</GridCommandButton>
<GridCommandButton Command="Save" Icon="@SvgIcon.Save" ShowInEdit="true">Save</GridCommandButton>
<GridCommandButton Command="Cancel" Icon="@SvgIcon.Cancel" ShowInEdit="true">Cancel</GridCommandButton>
}
<GridCommandButton Command="Delete" Icon="@SvgIcon.Trash">Delete</GridCommandButton>
</GridCommandColumn>
</GridColumns>
<DetailTemplate Context="productItem">
@{
Product product = (Product)productItem;
<TelerikGrid Data="@product.OrderDetails"
Pageable="true"
Sortable="true"
PageSize="5"
EditMode="@GridEditMode"
Navigable="true"
OnUpdate="@((GridCommandEventArgs args) => UpdateOrder(product, args))"
OnDelete="@((GridCommandEventArgs args) => DeleteOrder(args, product))"
OnCreate="@((GridCommandEventArgs args) => CreateOrder(args, product))">
<GridToolBarTemplate>
<GridCommandButton Command="Add" Icon="@SvgIcon.Plus">Add Order</GridCommandButton>
</GridToolBarTemplate>
<GridColumns>
<GridColumn Field=@nameof(OrderDetails.OrderId) Title="Order ID" Editable="false" />
<GridColumn Field=@nameof(OrderDetails.UnitPrice) Title="Price" />
<GridColumn Field=@nameof(OrderDetails.Discount) Title="Discount">
<Template Context="order">
@(String.Format("{0:0.00}%", ((OrderDetails)order).Discount))
</Template>
</GridColumn>
<GridColumn Field=@nameof(OrderDetails.Quantity) Title="Quantity" />
<GridCommandColumn>
@if (GridEditMode != GridEditMode.Incell)
{
<GridCommandButton Command="Edit" Icon="@SvgIcon.Pencil">Edit</GridCommandButton>
<GridCommandButton Command="Save" Icon="@SvgIcon.Save" ShowInEdit="true">Save</GridCommandButton>
<GridCommandButton Command="Cancel" Icon="@SvgIcon.Cancel" ShowInEdit="true">Cancel</GridCommandButton>
}
<GridCommandButton Command="Delete" Icon="@SvgIcon.Trash">Delete</GridCommandButton>
</GridCommandColumn>
</GridColumns>
</TelerikGrid>
}
</DetailTemplate>
</TelerikGrid>
@code {
public TelerikGrid<Product> GridRef { get; set; }
private GridEditMode GridEditMode { get; set; } = GridEditMode.Inline;
private List<Product> GridData { get; set; } = new();
private int LastId { get; set; }
private DateTime StartDate = new DateTime(2018, 1, 1);
private static Random RandomGenerator = new Random();
// When a row enters edit mode, expand it
private async Task OnEdit(GridCommandEventArgs args)
{
var editedItem = (Product)args.Item;
var state = GridRef.GetState();
state.ExpandedItems = new List<Product> { editedItem }; // collapse all others
await GridRef.SetStateAsync(state);
}
#region CRUD Operations for Main Grid
private void UpdateProduct(GridCommandEventArgs args)
{
var item = (Product)args.Item;
var index = GridData.FindIndex(x => x.ProductId == item.ProductId);
if (index != -1)
{
GridData[index] = item;
}
}
private void CreateProduct(GridCommandEventArgs args)
{
var item = (Product)args.Item;
item.ProductId = ++LastId;
GridData.Insert(0, item);
item.OrderDetails = GenerateOrderDetails(item);
// Auto-expand the new item
_ = ExpandNewItem(item);
}
private async Task ExpandNewItem(Product newItem)
{
await Task.Delay(50); // ensure grid state has updated
var state = GridRef.GetState();
state.ExpandedItems = new List<Product> { newItem };
await GridRef.SetStateAsync(state);
}
private void DeleteProduct(GridCommandEventArgs args)
{
var item = (Product)args.Item;
GridData.Remove(item);
}
#endregion
#region CRUD for Details Grid
private void UpdateOrder(Product product, GridCommandEventArgs args)
{
var item = (OrderDetails)args.Item;
var data = product.OrderDetails;
int index = data.FindIndex(x => x.OrderId == item.OrderId);
if (index != -1)
{
data[index] = item;
}
}
private void CreateOrder(GridCommandEventArgs args, Product product)
{
var item = (OrderDetails)args.Item;
var data = product.OrderDetails;
item.OrderId = data.Count + 1;
data.Insert(0, item);
}
private void DeleteOrder(GridCommandEventArgs args, Product product)
{
var item = (OrderDetails)args.Item;
var data = product.OrderDetails;
data.Remove(item);
}
#endregion
private void OnProductGridStateInit(GridStateEventArgs<Product> args)
{
args.GridState.ExpandedItems = new List<Product>(); // start with nothing expanded
}
#region Data Generation
protected override void OnInitialized()
{
GridData = GenerateProducts();
}
private List<Product> GenerateProducts()
{
List<Product> products = new List<Product>();
for (int i = 1; i <= 3; i++)
{
var product = new Product()
{
ProductId = ++LastId,
ProductName = $"Product {LastId}",
SupplierId = i,
UnitPrice = (decimal)(i * 3.14),
UnitsInStock = (short)(i * 1),
Discontinued = RandomGenerator.NextDouble() >= 0.5,
CreatedAt = GetRandomDate(StartDate)
};
product.OrderDetails = GenerateOrderDetails(product);
products.Add(product);
}
return products;
}
private List<OrderDetails> GenerateOrderDetails(Product product)
{
double minDiscount = 0.1;
double maxDiscount = 0.2;
var orderDetails = new List<OrderDetails>();
for (int i = 1; i <= 2; i++)
{
orderDetails.Add(new OrderDetails()
{
OrderId = Int32.Parse($"{product.ProductId}{i}"),
UnitPrice = (decimal)product.UnitPrice,
Quantity = (short)(1 + (RandomGenerator.Next() % 10)),
Discount = (float)((RandomGenerator.NextDouble() * (maxDiscount - minDiscount) + minDiscount)) * 100,
ProductId = product.ProductId,
});
}
return orderDetails;
}
private DateTime GetRandomDate(DateTime startDate)
{
int range = (DateTime.Today - startDate).Days;
return startDate.AddDays(RandomGenerator.Next(range));
}
#endregion
#region Models
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; } = string.Empty;
public int SupplierId { get; set; }
public decimal UnitPrice { get; set; }
public short UnitsInStock { get; set; }
public bool Discontinued { get; set; }
public DateTime CreatedAt { get; set; }
public List<OrderDetails> OrderDetails { get; set; } = new();
// Ensures Grid can identify which rows are expanded
public override bool Equals(object? obj) => Equals(obj as Product);
public bool Equals(Product? obj) => obj != null && obj.ProductId == ProductId;
public override int GetHashCode() => ProductId.GetHashCode();
}
public class OrderDetails
{
public int OrderId { get; set; }
public decimal UnitPrice { get; set; }
public short Quantity { get; set; }
public float Discount { get; set; }
public int ProductId { get; set; }
}
#endregion
}