Blazor模板化Grid组件通用展开/折叠详情展示实现问题
实现Blazor模板化Grid的通用展开区域嵌套属性展示
核心思路
通过Blazor的模板参数结合反射解析属性路径,让Grid组件同时支持自定义展开内容模板和自动解析泛型对象的嵌套属性,适配任意类型的展示需求,同时保留样式定制能力。
具体实现方案
1. 调整Grid组件的泛型与模板定义
给Grid组件添加自定义展开模板参数,同时内置自动解析嵌套属性的渲染逻辑:
@typeparam TItem <table class="grid-table"> @foreach (var item in Items) { <tr @onclick="() => ToggleExpand(item)" class="grid-row"> @RowTemplate(item) </tr> @if (ExpandedItems.Contains(item)) { <tr class="expand-row"> <td colspan="@ColumnCount"> <!-- 优先使用自定义模板,无则自动渲染嵌套属性 --> @(ExpandTemplate != null ? ExpandTemplate(item) : RenderAutoNestedProps(item)) </td> </tr> } } </table> @code { [Parameter] public IEnumerable<TItem> Items { get; set; } = Enumerable.Empty<TItem>(); [Parameter] public RenderFragment<TItem> RowTemplate { get; set; } [Parameter] public RenderFragment<TItem> ExpandTemplate { get; set; } [Parameter] public int ColumnCount { get; set; } = 1; [Parameter] public List<string> NestedPropPaths { get; set; } = new(); // 指定要展示的嵌套属性路径,如["AddressDetails", "ContactDetails.Phone"] private HashSet<TItem> ExpandedItems { get; set; } = new(); private void ToggleExpand(TItem item) { if (ExpandedItems.Contains(item)) ExpandedItems.Remove(item); else ExpandedItems.Add(item); } // 自动渲染嵌套属性的RenderFragment private RenderFragment RenderAutoNestedProps(TItem item) { return builder => { int seq = 0; foreach (var path in NestedPropPaths) { var propValue = GetNestedPropValue(item, path); builder.OpenElement(seq++, "div"); builder.AddAttribute(seq++, "class", "nested-prop-item"); builder.AddContent(seq++, $"{FormatPropName(path)}: {propValue ?? "N/A"}"); builder.CloseElement(); } }; } // 通过属性路径解析嵌套属性值 private object GetNestedPropValue(object obj, string propPath) { if (obj == null || string.IsNullOrWhiteSpace(propPath)) return null; var propSegments = propPath.Split('.'); foreach (var segment in propSegments) { var propInfo = obj.GetType().GetProperty(segment); if (propInfo == null) return null; obj = propInfo.GetValue(obj); if (obj == null) break; } return obj?.ToString(); } // 格式化属性名称为友好展示格式 private string FormatPropName(string propPath) { var lastSegment = propPath.Split('.').Last(); return System.Text.RegularExpressions.Regex.Replace(lastSegment, "(?<!^)([A-Z])", " $1"); } }
2. 父组件两种使用方式
方式一:完全自定义展开内容(适配复杂样式/逻辑)
<Grid Items="@Employees" ColumnCount="3" RowTemplate="@EmployeeRowTemplate"> <ExpandTemplate> @context => <div class="custom-expand-card"> <h4>员工详情</h4> <div class="address-block"> <span>地址:</span>@context.AddressDetails.Street, @context.AddressDetails.City </div> <div class="contact-block"> <span>电话:</span>@context.ContactDetails.Phone <span>邮箱:</span>@context.ContactDetails.Email </div> </div> </ExpandTemplate> </Grid> @code { private List<Employee> Employees { get; set; } = new() { new Employee { Name = "李四", Age = 32, Position = "开发工程师", AddressDetails = new Address { Street = "科技园路456号", City = "深圳" }, ContactDetails = new Contact { Phone = "139XXXX5678", Email = "lisi@example.com" } } }; private RenderFragment<Employee> EmployeeRowTemplate => emp => @<td>@emp.Name</td><td>@emp.Age</td><td>@emp.Position</td>; public class Employee { public string Name { get; set; } public int Age { get; set; } public string Position { get; set; } public Address AddressDetails { get; set; } public Contact ContactDetails { get; set; } } public class Address { public string Street { get; set; } public string City { get; set; } } public class Contact { public string Phone { get; set; } public string Email { get; set; } } }
方式二:自动解析嵌套属性(通用快速场景)
<Grid Items="@Employees" ColumnCount="3" RowTemplate="@EmployeeRowTemplate" NestedPropPaths="@new List<string> { "AddressDetails", "ContactDetails.Phone", "ContactDetails.Email" }"> </Grid>
3. 样式定制示例
.grid-table { width: 100%; border-collapse: collapse; } .grid-row { cursor: pointer; border-bottom: 1px solid #eee; transition: background-color 0.2s; } .grid-row:hover { background-color: #f5f5f5; } .expand-row { background-color: #f8f9fa; } .nested-prop-item { padding: 8px 16px; border-bottom: 1px solid #ddd; } .custom-expand-card { padding: 12px; border-radius: 4px; background-color: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.1); } .custom-expand-card .address-block, .custom-expand-card .contact-block { margin-bottom: 8px; line-height: 1.6; }
关键扩展点
- 可以给属性添加自定义特性(如
[Display(Name = "联系电话")]),替换FormatPropName方法实现更友好的名称展示 - 新增
PropFormatter参数,支持对特定属性值进行格式化(如日期、数字) - 支持动态控制嵌套属性的显示/隐藏,通过父组件传递过滤后的属性路径列表
内容的提问来源于stack exchange,提问作者LogicalDesk
相关产品推荐
相关产品推荐

