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

Blazor组件接收HashSet参数后未自动重渲染问题求助

Blazor组件接收HashSet参数后未自动重渲染问题

问题场景

  • 主页面注入DataExchange服务,首次渲染后通过await Task.Run(() => DataExchange.AccessData<fcPersons>());从数据库查询并将HashSet存入服务;
  • GenericGridComponent首次渲染时绑定服务的DataChanged事件,事件触发时将Elements属性赋值为服务返回的HashSet,但组件UI未重绘;
  • 期望不手动调用StateHasChanged(),认为Blazor应自动检测参数变更并重渲染。

相关代码

主页面代码

@inject DataExchange DataExchange
@rendermode InteractiveServer

<GenericGridComponent
    T="fcPersonen">
</GenericGridComponent>

@code {
    protected async override Task OnAfterRenderAsync(Boolean firstRender)
    {
        if (firstRender == true)
        {
            await Task.Run(() => DataExchange.AccessData<fcPersonen>());
        }
        await base.OnAfterRenderAsync(firstRender);
    }
}

GenericGridComponent代码

@typeparam T where T :class, new()
@inject DataExchange DataExchange

@using System.Reflection

@if (Elements != null)
{
    <table class="table table-bordered">
        <thead class="thead">
            <tr>
                @foreach (PropertyInfo propertyInfo in Elements.First().GetType().GetProperties())
                {
                    <th scope="col">
                        <div style="width:fit-content;">
                            <p>@propertyInfo.Name</p>
                        </div>
                    </th>
                }
            </tr>
        </thead>
        <tbody>
            <Virtualize TItem="T" Context="obj" Items="Elements">
                <tr>
                    @foreach (PropertyInfo propertyInfo in obj.GetType().GetProperties())
                    {
                        <td>@propertyInfo.GetValue(obj)</td>
                    }
                </tr>
            </Virtualize>
        </tbody>
    </table>
}

@code {
    protected override void OnAfterRender(Boolean firstRender)
    {
        if (firstRender == true)
        {
            DataExchange.DataChanged += DataChanged;
        }
    }

    private void DataChanged()
    {
        Elements = DataExchange.GetData<T>();
    }

    private HashSet<T>? Elements { get; set; }
}

问题原因

Blazor的自动状态检测依赖组件渲染上下文:

  1. DataChanged事件是在组件渲染上下文之外触发的(比如服务内部或后台线程),Blazor无法感知到Elements属性的变更;
  2. 普通私有属性的变更默认不会触发自动重渲染——Blazor的自动更新主要针对标记[Parameter]的组件参数,或通过双向绑定的属性。

解决方案(无需手动调用StateHasChanged)

方案1:将Elements改为组件参数,由父组件传递更新

修改GenericGridComponent,把Elements标记为组件参数:

@code {
    [Parameter]
    public HashSet<T>? Elements { get; set; }
}

主页面监听服务事件,更新传递给子组件的参数:

@inject DataExchange DataExchange
@rendermode InteractiveServer

<GenericGridComponent T="fcPersonen" Elements="_persons"></GenericGridComponent>

@code {
    private HashSet<fcPersonen>? _persons;

    protected override void OnInitialized()
    {
        DataExchange.DataChanged += OnDataChanged;
        base.OnInitialized();
    }

    private void OnDataChanged()
    {
        _persons = DataExchange.GetData<fcPersonen>();
        // 在组件上下文内修改字段,Blazor自动检测变更并重渲染
    }

    protected async override Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await Task.Run(() => DataExchange.AccessData<fcPersonen>());
        }
        await base.OnAfterRenderAsync(firstRender);
    }

    public void Dispose()
    {
        DataExchange.DataChanged -= OnDataChanged;
    }
}

方案2:用ObservableCollection替代HashSet

将服务中的HashSet替换为ObservableCollection<T>,它会在集合内容变更时自动触发通知,Blazor组件(尤其是Virtualize)能感知并更新:
修改DataExchange服务存储类型:

public class DataExchange
{
    private object? _data;
    public event Action? DataChanged;

    public async Task AccessData<T>() where T : class, new()
    {
        var dbData = await _dbContext.Set<T>().ToListAsync();
        _data = new ObservableCollection<T>(dbData);
        DataChanged?.Invoke();
    }

    public ObservableCollection<T>? GetData<T>() where T : class, new()
    {
        return _data as ObservableCollection<T>;
    }
}

组件中直接绑定该集合:

@code {
    private ObservableCollection<T>? Elements { get; set; }

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            DataExchange.DataChanged += DataChanged;
            Elements = DataExchange.GetData<T>();
        }
    }

    private void DataChanged()
    {
        Elements = DataExchange.GetData<T>();
        // ObservableCollection的变更通知会让Virtualize自动更新UI
    }
}

方案3:用InvokeAsync确保在渲染上下文内更新

如果必须保留HashSet和现有事件逻辑,通过InvokeAsync将赋值操作调度到组件渲染上下文,Blazor会自动检测状态变更:

@code {
    private void DataChanged()
    {
        InvokeAsync(() => 
        {
            Elements = DataExchange.GetData<T>();
            // 调度到渲染上下文后,Blazor自动触发重渲染
        });
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 19:23:18