Предположим, родительский компонент должен иметь определённое содержимое и взаимодействовать с этим:
<Panel>
@foreach (var p in ChildPanels){
<SubPanel></SubPanel>
}
<SubPanel></SubPanel>
</Panel>
Компонент Panel принимает в содержимое определенный тип компонентов: SubPanel. И они могут динамически меняться в зависимости от наличия в коллекции ChildPanels.
Если просто выводить содержимое параметра ChildContent в Panel, то мы ничего не узнаем о типе того, что передаем.
[Parameter]
public RenderFragment ChildContent { get; set; }
<div>
@ChildContent
</div>
Чтобы контейнер знал о своем содержимом и было взаимодействие между ними, нужно передать объект контейнера через CascadingParameter:
Panel:
<div>
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
</div>
SubPanel:
[CascadingParameter]
public Panel Panel
{
get => _panel;
set
{
if (_panel != value)
{
_panel = value;
_panel.AddSubPane(this);
}
}
}
Метод AddSubPane из Panel добавляет в коллекцию панелей дочернюю панель SubPanel. Таким образом ими можно будет управлять и отрисовывать по нужным правилам.
Поскольку панели могут появляться и исчезать динамически, то так же важен порядок дочерних панелей. Это можно решить через делегат Func<int>:
SubPanel:
[Parameter]
public Func<int> GetIndex { get; set; }
Panel:
public void AddSubPane(Panel pane)
{
if (!pane.Visible)
{
if (Panes.Contains(pane))
Panes.Remove(pane);
return;
}
if (!Panes.Contains(pane))
Panes.Add(pane);
UpdateIndexesViaDelegate();
}
private void UpdateIndexesViaDelegate()
{
foreach (var p in Panes)
p.Index = p.GetIndex();
Panes = Panes.OrderBy(static a => a.Index).ToList();
}
Использование Panel:
<Panel>
@foreach (var p in ChildPanels){
<SubPanel GetIndex="() => Array.IndexOf(ChildPanels.ToArray(), p)"></SubPanel>
}
<SubPanel GetIndex="() => ChildPanels.Count"></SubPanel>
</Panel>
Логика тут простая: удаление невидимой панели и добавление новой, а затем перерасчет порядкового индекса.