You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 02:54:26