实现接口的Blazor组件列表



我正在为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;
}
}

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

我想以一种干净、恰当的方式来做这件事,不需要反思。最好是带有依赖项注入的ASP.NET Core风格。

问题是Blazor组件在Razor标记中被实例化为<Component></Component>,所以我不知道如何获得它们的引用来将它们添加到List<>中。

我想你可以按如下方式完成:

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

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

您可以使用@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

如果要动态创建组件,则可以使用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指令捕获对渲染组件实例的引用:

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

相关内容

  • 没有找到相关文章

最新更新