【问题标题】:List of Blazor components that implement an interface实现接口的 Blazor 组件列表
【发布时间】:2020-03-17 07:38:55
【问题描述】:

我正在为 Blazor 组件构建一个可重用的 Razor 类库。

我有一个任何 Blazor 组件都可以实现的接口:

public interface IControllableComponent
{
    void SetSomeValue(int someValue);
}

如何获得实现该接口的所有组件的列表?

public interface IControllableComponentManager
{
    void SetSomeValueForAllControllableComponents(int someValue);
}

public class ControllableComponentManager : IControllableComponentManager
{
    IList<IControllableComponent> _controllableComponentList;

    public ControllableComponentManager()
    {
        _controllableComponentList = ??? // how to populate this list?
    }

    public void SetSomeValueForAllControllableComponents(int someValue)
    {
        foreach (var controllableComponent in _controllableComponentList)
        {
            controllableComponent.SetSomeValue(someValue);
        }
    }
}

我希望我的可重用 Razor 类库在不同项目的代码中使用:

public class MyControllableComponentBase : ComponentBase, IControllableComponent
{
    protected int _someValue;

    public void SetSomeValue(int someValue)
    {
        _someValue = someValue;
    }
}

有没有办法在 ControllableComponentManager 中填充 _controllableComponentList?

我想以一种干净、适当的方式完成此操作,而不使用反射。最好采用带有依赖注入的 ASP.NET Core 风格。

问题是 Blazor 组件在 Razor 标记中被实例化为 &lt;Component&gt;&lt;/Component&gt;,所以我不知道如何获取它们的引用以将它们添加到 List&lt;&gt;

【问题讨论】:

  • 您可以使用反射和Assembly.GetTypes() 搜索类型。然后,您可以通过它们正在实现的接口过滤这些类型。请注意,这通常是一项昂贵的操作,因此您应该缓存结果。
  • @poke 那将是最后的手段。我真的更愿意以一种干净、适当的方式来做这件事,而不使用反射。
  • 唯一的其他方法是主动创建所有这些组件类型的列表。但如果你想检测它们,那就是反射的用途。
  • @poke 不,我不想检测它们。我正在寻求一种创建列表的方法。但由于 Blazor 组件在 Razor 标记中实例化为 &lt;Component&gt;&lt;/Component&gt; 我不知道如何获取它们的引用以将它们添加到列表中。

标签: c# asp.net-core razor blazor


【解决方案1】:

我猜你可以这样做:

  1. 定义一个存储接口类型集合的服务
  2. 公开添加组件的方法、删除组件的方法、通知添加、删除等的事件委托。
  3. 将服务注入到您想要将自身添加到服务的组件中,并在 OnInitialized 方法中执行以下操作:

    protected override void OnInitialized()
    {
        MyComponents.AddComponent(this);
        this.MyProperty = "I was born with the sun...";
    }  
    

【讨论】:

  • 但我的回答非常详细 ;) 包含所有实现 :) 来吧,20 秒 :)
  • 实现这个不是问题。我可以做得很好。问题在于使用 this 关键字从 OnInitialized 方法将组件添加到服务中。但是,嘿,我不坚持。
  • 嗯,好吧,公平就是公平:)
【解决方案2】:

您可以使用 @ref 获取 Blazor 组件引用,但您只能在 Razor 标记中执行此操作。

要在 Razor 标记之外使用对 Blazor 组件的引用,该组件必须自行注册。

为此,您必须在后面的代码中为组件使用分部类:

public partial class ComponentContainer : ComponentBase, IComponentContainer
{
    public Type ComponentType { get; protected set; }

    public RenderFragment Component { get; set; }

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

    [Inject]
    protected IComponentContainerManager ComponentContainerManager { get; set; }

    public void SetComponentType(Type componentType)
    {
        ComponentType = componentType;

        StateHasChanged();
    }

    protected override void OnInitialized()
    {
        ComponentContainerManager.RegisterComponentContainer(Name, this);

        Component = builder =>
        {
            if (ComponentType != null)
            {
                builder.OpenComponent(0, ComponentType);
                builder.CloseComponent();
            }
        };
    }
}

使用[Inject]属性使用依赖注入注入组件可以注册的服务。

使用ComponentBaseprotected override void OnInitialized()在注册服务中注册组件。

public interface IComponentContainer
{
    Type ComponentType { get; }
    void SetComponentType(Type componentType);
}

public interface IComponentContainerManager
{
    void RegisterComponentContainer(string componentContainerName, IComponentContainer componentContainer);
    void SetComponentType(string componentContainerName, Type componentType);
    Type GetComponentType(string componentContainerName);
}

public class ComponentContainerManager : IComponentContainerManager
{
    readonly IDictionary<string, IComponentContainer> _componentContainerDict = new Dictionary<string, IComponentContainer>();

    public void RegisterComponentContainer(string componentContainerName, IComponentContainer componentContainer)
    {
        _componentContainerDict[componentContainerName] = componentContainer;
    }

    public void SetComponentType(string componentContainerName, Type componentType)
    {
        _componentContainerDict[componentContainerName].SetComponentType(componentType);
    }

    public Type GetComponentType(string componentContainerName)
    {
        return _componentContainerDict[componentContainerName].ComponentType;
    }
}

现在您可以在代码中的任何位置使用IComponentContainerManager

【讨论】:

    【解决方案3】:

    如果您想动态创建组件,那么您可以为此使用RenderFragment 概念。渲染片段本质上是一个使用RenderTreeBuilder 创建组件的函数。这是您在创作 .razor 文件时编译的 Razor 组件。

    例如,以下代码创建了一个RenderFragment,它只呈现单个组件:

    Type componentType = typeof(MyControllableComponentBase);
    RenderFragment fragment = builder =>
    {
        builder.OpenComponent(0, componentType);
        builder.CloseComponent();
    };
    

    然后您可以将该片段分配给一个属性并在您的 Razor 组件中动态呈现它:

    <div>
       @Fragment
    </div>
    
    @code {
        public RenderFragment Fragment
        { get; set; }
    }
    

    这将解决当您只知道组件类型时如何动态创建组件的问题。但是,它不会解决您要在组件实例上调用SetSomeValue 的问题。为此,您必须了解您并不能真正控制组件实例:渲染树构建器负责从该虚拟标记创建组件,因此您永远不会调用new ComponentType()。相反,您依赖渲染器为您创建实例,然后您可以引用使用的实例并使用它。

    您可以使用@ref 指令对capture references to rendered component instances

    <MyControllableComponent @ref="controllableComponent" />
    
    @code {
        private MyControllableComponent controllableComponent;
    
        private void OnSomething()
        {
            controllableComponent.SetSomeValue(123);
        }
    }
    

    【讨论】:

    • 感谢您的尝试,我真的很感激,但我已经知道了这一切。您答案中的所有内容都已在 Microsoft 文档中。 @ref 仅适用于 Razor 标记,因此我不能使用它来填充我的 List。目前,我正在尝试让组件在protected override void OnInitialized() 上注册到IControllableComponentManager,它们通过[Inject] 获得
    • 正如我所说,您不能按照您希望的方式填充列表。您不能自己创建组件实例。您将需要重塑您的方法以适应 Blazor 基于组件的性质。
    • 我不想自己创建组件实例。我正在尝试让他们将自己在protected override void OnInitialized() 注册到IControllableComponentManager,他们通过[Inject] 获得:) 是的,你的猜测是正确的,在我的情况下,“一些价值”是RenderFragment :) 我我正在尝试创建一个可以控制所有这些的管理器类:)
    • 事实证明,您可以填充列表 :) 感谢您抽出宝贵时间! :)
    猜你喜欢
    • 1970-01-01
    • 2012-11-30
    • 2015-06-07
    • 2022-11-24
    • 2019-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多