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

如何在Blazor中遍历实现IOldValue接口的直接子组件

实现Blazor父组件批量调用子组件接口方法

我基于input元素实现了一个自定义文本框组件MyTextBox,它实现了IOldValue接口,该接口的SetOldValue()方法可以把当前值保存到OldValue变量里。现在想在父组件里写一个SetAllOldValues()方法,自动遍历所有实现了IOldValue接口的直接子组件,调用它们的SetOldValue()方法,代替现在每个组件都声明引用、逐个调用的繁琐写法(页面组件多的时候特别麻烦)。

想要实现的伪代码逻辑如下:

foreach (var control in this.Controls.OfType<IOldValue>())
{
    control.SetOldValue();
}

一、现有代码展示

1. IOldValue接口代码

namespace WebUI.Components.Controls;

public interface IOldValue
{
  object OldValue { get; set; }

  object CurrentValue { get; }

  string FriendlyName { get; set; }

  bool HasChanges { get; set; }

  string ReportDifs();

  void SetOldValue();
}

2. 自定义MyTextBox组件代码

@implements IOldValue

@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Web

<input @bind="Value" @bind:event="oninput" />

@code {
    [Parameter]
    public string Value { get; set; }

    [Parameter]
    public EventCallback<object> ValueChanged { get; set; }

    private async Task OnInput(ChangeEventArgs e)
    {
        Value = e.Value!.ToString()!;
        await ValueChanged.InvokeAsync(Value);
    }

    public object OldValue { get; set; }
    public object CurrentValue
    {
        get { return _currentValue; }
    }
    public string FriendlyName { get; set; } // 暂不使用
    public bool HasChanges { get; set; } // 暂不使用

    private string _currentValue;

    public string ReportDifs()
    {
        return String.Empty;
    }

    public void SetOldValue()
    {
        OldValue = Value;
    }
}

3. 现有父组件代码(繁琐的逐个调用写法)

<br />

<div class="container col-12">

    <div class="row">
        <div class="col-2">
            <label>前缀</label>
        </div>
        <div class="col-4">
            <MyTextBox @ref="@_prefix" @bind-Value="@_person.Prefix" />
        </div>
    </div>

    <div class="row">
        <div class="col-2">
            <label>名字</label>
        </div>
        <div class="col-4">
            <MyTextBox @ref="@_firstName" @bind-Value="@_person.FirstName" />
        </div>
    </div>

    <div class="row">
        <div class="col-2">
            <label>中间名</label>
        </div>
        <div class="col-4">
            <MyTextBox @ref="@_middleName" @bind-Value="@_person.MiddleName" />
        </div>
    </div>

    <div class="row">
        <div class="col-2">
            <label>姓氏</label>
        </div>
        <div class="col-4">
            <MyTextBox @ref="@_lastName" @bind-Value="@_person.LastName" />
        </div>
    </div>

    <div class="row">
        <div class="col-2">
            <label>后缀</label>
        </div>
        <div class="col-4">
            <MyTextBox @ref="@_suffix" @bind-Value="@_person.Suffix" />
        </div>
    </div>
</div>

<br />

<button class="btn btn-primary" type="button" @onclick="ButtonClicked">设置初始值</button>

@code {

    private MyTextBox _prefix = new();
    private MyTextBox _firstName = new();
    private MyTextBox _middleName = new();
    private MyTextBox _lastName = new();
    private MyTextBox _suffix = new();

    private Person _person = new();

    protected override void OnInitialized()
    {
        _person = new Person
            {
                Prefix = "Mr.",
                FirstName = "John",
                MiddleName = "Q",
                LastName = "Public",
                Suffix = "Sr."
            };

        //SetAllOldValues();
    }

    private void ButtonClicked(MouseEventArgs e)
    {
        SetAllOldValues();
    }

    private void SetAllOldValues()
    {
        // 想要实现的伪代码(无法直接运行)
        // foreach (var control in this.Controls.OfType<IOldValue>())
        // {
        //     control.SetOldValue();
        // }

        // 当前可行但繁琐的写法
        _prefix.SetOldValue();
        _firstName.SetOldValue();
        _middleName.SetOldValue();
        _lastName.SetOldValue();
        _suffix.SetOldValue();
    }

    private class Person
    {
        public string Prefix { get; set; }
        public string FirstName { get; set; }
        public string MiddleName { get; set; }
        public string LastName { get; set; }
        public string Suffix { get; set; }
    }
}

二、解决方案:批量调用实现方式

Blazor没有WinForms那样直接的Controls集合,但可以通过收集子组件引用的方式实现批量操作,以下提供两种方案:

方案1:手动收集组件引用(适合组件数量不多的场景)

  1. 在父组件的@code块中定义集合存储子组件:
private List<IOldValue> _oldValueControls = new();
  1. 给每个MyTextBox组件添加引用注册逻辑(Blazor 6+支持@ref:after):
<MyTextBox @ref="component" @ref:after="() => RegisterControl(component)" @bind-Value="@_person.Prefix" />
  1. 实现注册/注销方法:
private void RegisterControl(IOldValue component)
{
    if (component != null && !_oldValueControls.Contains(component))
    {
        _oldValueControls.Add(component);
    }
}

private void UnregisterControl(IOldValue component)
{
    if (component != null)
    {
        _oldValueControls.Remove(component);
    }
}
  1. 修改SetAllOldValues方法遍历集合调用:
private void SetAllOldValues()
{
    foreach (var control in _oldValueControls)
    {
        control.SetOldValue();
    }
}

方案2:封装容器组件自动收集(适合大量组件的场景)

通过CascadingValue让子组件自动注册到容器,无需手动处理每个组件的引用:

  1. 创建OldValueContainer容器组件:
@using WebUI.Components.Controls

<CascadingValue Value="this">
    @ChildContent
</CascadingValue>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public List<IOldValue> Controls { get; } = new();

    public void Register(IOldValue control)
    {
        if (!Controls.Contains(control))
        {
            Controls.Add(control);
        }
    }

    public void Unregister(IOldValue control)
    {
        Controls.Remove(control);
    }

    public void SetAllOldValues()
    {
        foreach (var control in Controls)
        {
            control.SetOldValue();
        }
    }
}
  1. 修改MyTextBox组件,自动向容器注册:
@implements IOldValue
@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Web
@using WebUI.Components.Controls

<input @bind="Value" @bind:event="oninput" />

@code {
    [CascadingParameter]
    private OldValueContainer Container { get; set; }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        Container?.Register(this);
    }

    public void Dispose()
    {
        Container?.Unregister(this);
    }

    // 原有组件代码...
}
  1. 父组件使用容器包裹所有MyTextBox:
<OldValueContainer @ref="container">
    <div class="container col-12">
        <!-- 所有MyTextBox组件放在这里 -->
    </div>
</OldValueContainer>

<button class="btn btn-primary" @onclick="() => container.SetAllOldValues()">设置初始值</button>

@code {
    private OldValueContainer container;
    private Person _person = new();

    // 其他原有代码...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 19:07:32