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

Blazor动态包装组件时如何正确捕获子组件引用?

如何捕获Blazor包装器内子组件的引用而非包装器本身?

我想实现编写 <Component/> 时,自动渲染为:

<Wrapper>
    <Component />
</Wrapper>

通过自定义IComponentActivator实现了该功能,但使用@ref捕获组件引用时抛出InvalidCastException——因为@ref试图获取ComponentIWantToBeInWrapper类型实例,但实际得到的是Wrapper实例。请问有没有办法让@ref捕获包装器内的子组件而非包装器本身?


最小复现代码示例

ComponentActivator.cs

public class ComponentActivator : IComponentActivator
{
    public IComponent CreateInstance(Type componentType)
    {
        if(componentType == typeof(Component))
        {
            return (IComponent)Activator.CreateInstance(typeof(DivWrapper<>).MakeGenericType(componentType))!;
        }

        if (componentType.IsGenericType && componentType.GetGenericTypeDefinition() == typeof(Dummy<>))
        {
            var innerType = componentType.GetGenericArguments()[0];
            return (IComponent)Activator.CreateInstance(innerType)!;
        }

        return (IComponent)Activator.CreateInstance(componentType)!;
    }
}

DivWrapper.razor

@typeparam T where T : ComponentBase

<div style="border: 1px solid red">
    <Dummy T="T" @attributes=Attrs />
</div>
@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> Attrs { get; set; } = default!;
}

Dummy.cs

public class Dummy<T> : ComponentBase where T : ComponentBase
{
}

Page.razor

@page "/"

<Component />
@* <Component @ref=_ref /> 这行代码会导致应用崩溃 *@

解决方案

核心思路

问题根源是IComponentActivator返回的是DivWrapper实例,Blazor的@ref会直接绑定这个顶层实例。要让@ref指向内部子组件,需要让包装器拦截@ref参数,将内部子组件的引用传递给绑定委托。

具体实现

  1. 修改DivWrapper组件,拦截@ref参数并暴露内部子组件引用
@typeparam T where T : ComponentBase

<div style="border: 1px solid red">
    <Dummy T="T" @attributes=Attrs @ref="_innerComponentRef" />
</div>
@code {
    private T? _innerComponentRef;

    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> Attrs { get; set; } = default!;

    // 暴露内部子组件引用,供外部获取
    public T? InnerComponent => _innerComponentRef;

    public override async Task SetParametersAsync(ParameterView parameters)
    {
        // 拦截Blazor内部的@ref参数标识
        if (parameters.TryGetValue("__internal__ref", out var refValue) && refValue is Action<object?> setRef)
        {
            // 将内部子组件引用传递给@ref绑定委托
            setRef(_innerComponentRef);
            // 移除该参数,避免传递给子组件
            var newParams = parameters.Remove("__internal__ref");
            await base.SetParametersAsync(newParams);
        }
        else
        {
            await base.SetParametersAsync(parameters);
        }
    }
}
  1. 修改Dummy组件,传递@ref捕获委托到真实子组件
public class Dummy<T> : ComponentBase where T : ComponentBase
{
    [Parameter]
    public object? __internal__ref { get; set; }

    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object>? AdditionalAttributes { get; set; }

    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        builder.OpenComponent<T>(0);
        
        // 传递所有属性
        if (AdditionalAttributes != null)
        {
            foreach (var attr in AdditionalAttributes)
            {
                builder.AddAttribute(1, attr.Key, attr.Value);
            }
        }
        
        // 传递@ref捕获委托到真实子组件
        if (__internal__ref is Action<object?> setRef)
        {
            builder.AddComponentReferenceCapture(2, setRef);
        }
        
        builder.CloseComponent();
    }
}
  1. 页面中正常使用@ref
@page "/"

<Component @ref="_componentRef" />

@code {
    private Component? _componentRef;

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender && _componentRef != null)
        {
            // 此时_componentRef是内部的Component实例,而非DivWrapper
            Console.WriteLine($"获取到子组件引用:{_componentRef.GetType().Name}");
        }
    }
}

关键说明

  • Blazor编译@ref时会生成__internal__ref参数,携带设置引用的委托
  • 包装器通过拦截该参数,将内部子组件的引用传递给委托,让@ref绑定到真实子组件
  • Dummy组件负责构建真实子组件并传递@ref捕获逻辑,确保引用指向正确实例

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 19:30:56