使用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
相关产品推荐
相关产品推荐

