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

Blazor动态组件点击事件向父/祖父组件传递的实现方案咨询

Blazor DataTable组件排序功能实现方案

核心思路

通过组件间回调参数传递实现事件向上冒泡:

  • DataTableSort子组件接收父组件DataTableColumn传递的点击回调
  • DataTableColumn将自身的列标识(对应数据属性名)通过回调传递给DataTable祖父组件
  • DataTable维护排序状态(当前排序列、升降序)并执行排序逻辑,更新表格数据

具体修改步骤

1. 给DataTable组件添加排序状态与逻辑

修改DataTable.razor:

@typeparam TRowData

<CascadingValue IsFixed="true" Value="this">@ChildContent</CascadingValue>

<table>
    <thead>
        <tr>
            @foreach (var column in columns)
            {
                @column.HeaderTemplate;
            }
        </tr>
    </thead>
    <tbody>
        @if (Items != null)
        {
            var sortedItems = GetSortedItems();
            foreach (var item in sortedItems)
            {
                <tr @key="item">
                    @foreach (var column in columns)
                    {
                        @column.CellTemplate(item);
                    }
                </tr>
            }
        }
    </tbody>
</table>

@code {
    [Parameter]
    public ICollection<TRowData> Items { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    private readonly List<DataTableColumn<TRowData>> columns = new();
    private string _currentSortColumn;
    private bool _isAscending = true;

    internal void AddColumn(DataTableColumn<TRowData> column)
    {
        columns.Add(column);
    }

    // 处理排序请求
    internal void RequestSort(string columnName)
    {
        if (_currentSortColumn == columnName)
        {
            // 同一列点击,切换升降序
            _isAscending = !_isAscending;
        }
        else
        {
            // 新列排序,默认升序
            _currentSortColumn = columnName;
            _isAscending = true;
        }
        StateHasChanged(); // 更新UI
    }

    // 获取排序后的数据集
    private IEnumerable<TRowData> GetSortedItems()
    {
        if (Items == null || string.IsNullOrEmpty(_currentSortColumn))
            return Items;

        // 通过反射实现基础排序,可后续用Expression优化性能
        var property = typeof(TRowData).GetProperty(_currentSortColumn);
        if (property == null)
            return Items;

        return _isAscending 
            ? Items.OrderBy(item => property.GetValue(item)) 
            : Items.OrderByDescending(item => property.GetValue(item));
    }

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            StateHasChanged();
        }
    }
}

2. 修改DataTableColumn组件,传递排序回调

更新DataTableColumn.razor中的HeaderTemplate,给DataTableSort绑定回调并传递列标识:

@typeparam TRowData
@using System.Linq.Expressions

@code {
    [CascadingParameter]
    public DataTable<TRowData> Owner { get; set; }

    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public bool Sortable { get; set; } = false; // 默认不可排序

    [Parameter]
    public string Format { get; set; }

    [Parameter]
    public Expression<Func<TRowData, object>> Expression { get; set; }

    [Parameter]
    public RenderFragment<TRowData> ChildContent { get; set; }

    private RenderFragment<TRowData> cellTemplate;
    private RenderFragment headerTemplate;
    private Func<TRowData, object> compiledExpression;
    private Expression lastCompiledExpression;
    private string _columnName;

    internal RenderFragment HeaderTemplate
    {
        get
        {
            return headerTemplate ??= (builder =>
            {
                var title = Title ?? (Expression != null ? GetMemberName(Expression) : string.Empty);
                _columnName = Expression != null ? GetMemberName(Expression) : null;

                builder.OpenElement(0, "th");
                builder.AddContent(1, title);

                if (Sortable && !string.IsNullOrEmpty(_columnName))
                {
                    // 渲染排序按钮并绑定回调
                    builder.OpenComponent<DataTableSort>(1);
                    builder.AddAttribute(2, "OnSortClicked", EventCallback.Factory.Create(this, () => Owner.RequestSort(_columnName)));
                    builder.AddAttribute(3, "IsAscending", _currentSortColumn == _columnName && Owner._isAscending);
                    builder.CloseComponent();
                }

                builder.CloseElement();
            });
        }
    }

    // 其余原有代码保持不变...

    private static string GetMemberName<T>(Expression<T> expression)
    {
        return expression.Body switch
        {
            MemberExpression m => m.Member.Name,
            UnaryExpression u when u.Operand is MemberExpression m => m.Member.Name,
            _ => throw new NotSupportedException("不支持该类型的表达式")
        };
    }
}

3. 修改DataTableSort组件,接收并触发回调

更新DataTableSort.razor,添加参数接收回调与排序状态:

<button @onclick="HandleSortClicked" style="margin-left:8px">
    @(IsAscending ? "↑" : "↓")
</button>

@code {
    // 接收父组件传递的排序回调
    [Parameter]
    public EventCallback OnSortClicked { get; set; }

    // 接收当前排序状态,用于显示箭头方向
    [Parameter]
    public bool IsAscending { get; set; } = true;

    private async Task HandleSortClicked()
    {
        await OnSortClicked.InvokeAsync();
    }
}

4. 在页面中启用列排序

修改Page.razor,给需要排序的列添加Sortable="true":

<DataTable Items="users">
    <DataTableColumn TRowData="User" Expression="u => u.Id" Sortable="true"/>
    <DataTableColumn TRowData="User" Expression="u => u.Username" Sortable="true" />
    <DataTableColumn TRowData="User" Expression="u => u.DateOfBirth" Sortable="true" />
    <DataTableColumn TRowData="User"><div>Column B</div></DataTableColumn>
</DataTable>

可选优化点

  • 用预编译Expression替代反射排序,提升大数据量场景下的性能
  • 支持多列排序:维护排序列优先级列表,实现多维度排序
  • 优化排序按钮样式,增强交互反馈

内容的提问来源于stack exchange,提问作者Musaffar Patel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 22:26:25