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

使用Router时从布局订阅页面的EventCallback

解决Blazor布局订阅页面EventCallback的问题

因为@Body是动态渲染的无固定类型页面,直接引用EventCallback行不通,这里用约定接口的方案实现布局对页面回调的订阅:

步骤1:定义统一回调接口

创建一个接口,约定需要暴露的回调方法,让所有需要触发选择事件的页面实现它:

public interface ISelectionCallbackProvider
{
    EventCallback<IEnumerable<string>> SelectionChanged { get; }
}

步骤2:页面实现接口

在需要触发选择操作的页面中,实现上述接口并暴露EventCallback:

@page "/products"
@implements ISelectionCallbackProvider

<!-- 页面的选择UI,比如复选框列表 -->
@foreach (var product in Products)
{
    <input type="checkbox" @bind="SelectedProductIds" value="@product.Id" /> @product.Name
}

@code {
    [CascadingParameter(Name = "selectionIds")]
    public IEnumerable<string> SelectionIds { get; set; } = new List<string>();
    
    private List<string> SelectedProductIds { get; set; } = new();
    private List<Product> Products { get; set; } = new();

    // 实现接口的回调属性
    public EventCallback<IEnumerable<string>> SelectionChanged => 
        EventCallback.Factory.Create(this, () => OnSelectionChanged(SelectedProductIds));

    private void OnSelectionChanged(IEnumerable<string> newSelections)
    {
        // 页面内部处理选择逻辑,同时触发布局的回调
        SelectionIds = newSelections;
        StateHasChanged();
    }

    // 模拟加载产品数据
    protected override void OnInitialized()
    {
        Products = new List<Product>
        {
            new Product { Id = "1", Name = "产品A" },
            new Product { Id = "2", Name = "产品B" }
        };
    }

    public class Product
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }
}

步骤3:布局页面订阅回调

修改布局页面,在渲染完成后获取当前页面组件实例,判断是否实现了约定接口,然后订阅回调:

<main>
    <CascadingValue Value="@selectionIds" Name="selectionIds" >
        @Body 
        <DetailView />
    </CascadingValue>
</main>

@code {
    private IEnumerable<string> selectionIds = new List<string>();
    private ISelectionCallbackProvider _currentPageCallback;

    protected override void OnAfterRender(bool firstRender)
    {
        base.OnAfterRender(firstRender);

        // 获取当前@Body对应的组件实例
        var currentPage = GetCurrentBodyComponent();
        if (currentPage is ISelectionCallbackProvider callbackProvider)
        {
            // 清理之前页面的订阅,避免内存泄漏
            if (_currentPageCallback != null)
            {
                // EventCallback无法直接取消,通过替换实例标记旧订阅失效
                _currentPageCallback = null;
            }
            _currentPageCallback = callbackProvider;
            // 订阅页面的选择变化回调
            _currentPageCallback.SelectionChanged += HandleSelectionUpdate;
        }
    }

    private void HandleSelectionUpdate(IEnumerable<string> newSelections)
    {
        // 布局中处理选择更新,比如同步级联参数
        selectionIds = newSelections;
        StateHasChanged();
    }

    // 查找当前渲染的Body组件
    private ComponentBase GetCurrentBodyComponent()
    {
        // 遍历组件集合,找到页面级组件(可根据实际场景调整判断逻辑)
        foreach (var component in ViewComponentCollection)
        {
            if (component is ComponentBase pageComponent)
            {
                return pageComponent;
            }
        }
        return null;
    }
}

补充说明

  • 该方案基于约定编程,只有实现了ISelectionCallbackProvider的页面才会被布局订阅,符合多页面共享布局的场景
  • 页面切换时务必清理旧订阅,示例中通过替换_currentPageCallback标记旧实例失效,也可以结合弱引用进一步优化内存管理
  • 如果需要更灵活的事件订阅/取消,可以将接口中的EventCallback改为C#标准事件(event Action<IEnumerable<string>> SelectionChanged),操作会更直观

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 19:05:24