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

Blazor中动态生成绑定bool?属性的单选按钮并实现表单验证

动态生成带验证的Bool?属性单选按钮解决方案

针对你的需求,核心问题是要让动态生成的单选按钮既能绑定到MyModel的原始属性,又能被EditContext跟踪以支持验证。以下是几种可行的实现思路:


方案一:反射+EditContext手动绑定(最直接)

直接基于MyModel的属性反射结果,配合EditContext的字段跟踪能力实现双向绑定和验证,完全保留原模型的验证逻辑。

完整组件代码

@using System.Reflection
@using System.ComponentModel.DataAnnotations

<EditForm EditContext="@_editContext" OnValidSubmit="HandleSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    @foreach (var prop in _boolProperties)
    {
        var fieldId = new FieldIdentifier(_model, prop.Name);
        var currentValue = (bool?)prop.GetValue(_model);

        <div class="mb-3">
            <label class="form-label">@prop.Name</label>
            <div class="form-check form-check-inline">
                <input type="radio" 
                       class="form-check-input"
                       id="@($"{prop.Name}-yes")"
                       name="@prop.Name"
                       checked="@(currentValue == true)"
                       @onchange="() => UpdateProp(prop, true)" />
                <label class="form-check-label" for="@($"{prop.Name}-yes")">Yes</label>
            </div>
            <div class="form-check form-check-inline">
                <input type="radio" 
                       class="form-check-input"
                       id="@($"{prop.Name}-no")"
                       name="@prop.Name"
                       checked="@(currentValue == false)"
                       @onchange="() => UpdateProp(prop, false)" />
                <label class="form-check-label" for="@($"{prop.Name}-no")">No</label>
            </div>
            <ValidationMessage For="@(() => fieldId)" class="text-danger" />
        </div>
    }

    <button type="submit" class="btn btn-primary">提交</button>
</EditForm>

@code {
    [Parameter]
    public MyModel Model { get; set; } = new();

    private EditContext _editContext;
    private IEnumerable<PropertyInfo> _boolProperties;

    protected override void OnInitialized()
    {
        _editContext = new EditContext(Model);
        // 筛选出所有bool?类型的公开实例属性
        _boolProperties = typeof(MyModel)
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => p.PropertyType == typeof(bool?));
    }

    private void UpdateProp(PropertyInfo prop, bool value)
    {
        // 更新模型属性值
        prop.SetValue(Model, value);
        // 通知EditContext属性变化,触发验证
        _editContext.NotifyFieldChanged(new FieldIdentifier(Model, prop.Name));
    }

    private void HandleSubmit()
    {
        // 处理表单提交逻辑,比如保存数据
    }

    // 示例模型(带验证)
    public class MyModel
    {
        [Required(ErrorMessage = "请选择选项1")]
        public bool? Option1 { get; set; }

        [Required(ErrorMessage = "请选择选项2")]
        public bool? Option2 { get; set; }

        // 剩余100+ bool?属性...
    }
}

为什么这个方案可行?

  1. 直接绑定原模型:所有操作都基于MyModel的原始属性,不会脱离模型上下文
  2. EditContext跟踪:通过NotifyFieldChanged手动通知属性变化,确保验证逻辑正常触发
  3. 原生验证支持:ValidationMessage和ValidationSummary可以正确显示每个属性的验证错误

方案二:封装可复用的单选按钮组件

如果需要多次使用,可以把单个属性的单选按钮封装成独立组件,提升代码复用性:

子组件:BoolRadioGroup.razor

@using System.Reflection
@using System.ComponentModel.DataAnnotations

<div class="mb-3">
    <label class="form-label">@Property.Name</label>
    <div class="form-check form-check-inline">
        <input type="radio" 
               class="form-check-input"
               id="@($"{Property.Name}-yes")"
               name="@Property.Name"
               checked="@(CurrentValue == true)"
               @onchange="() => UpdateValue(true)" />
        <label class="form-check-label" for="@($"{Property.Name}-yes")">Yes</label>
    </div>
    <div class="form-check form-check-inline">
        <input type="radio" 
               class="form-check-input"
               id="@($"{Property.Name}-no")"
               name="@Property.Name"
               checked="@(CurrentValue == false)"
               @onchange="() => UpdateValue(false)" />
        <label class="form-check-label" for="@($"{Property.Name}-no")">No</label>
    </div>
    <ValidationMessage For="@(() => new FieldIdentifier(Model, Property.Name))" class="text-danger" />
</div>

@code {
    [Parameter, Required]
    public object Model { get; set; }

    [Parameter, Required]
    public PropertyInfo Property { get; set; }

    [Parameter, Required]
    public EditContext EditContext { get; set; }

    private bool? CurrentValue => (bool?)Property.GetValue(Model);

    private void UpdateValue(bool value)
    {
        Property.SetValue(Model, value);
        EditContext.NotifyFieldChanged(new FieldIdentifier(Model, Property.Name));
    }
}

父组件调用方式

<EditForm EditContext="@_editContext" OnValidSubmit="HandleSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    @foreach (var prop in _boolProperties)
    {
        <BoolRadioGroup Model="@Model" Property="@prop" EditContext="@_editContext" />
    }

    <button type="submit" class="btn btn-primary">提交</button>
</EditForm>

方案三:Syncfusion DataForm自定义编辑器

如果你坚持使用Syncfusion的DataForm组件,可以为bool?类型注册自定义编辑器,让DataForm自动生成单选按钮并保留验证:

protected override void OnInitialized()
{
    var dataForm = new SfDataForm();
    dataForm.DataObject = Model;
    // 为bool?类型注册自定义编辑器
    dataForm.RegisterEditor(typeof(bool?), new BoolRadioEditor());
}

// 自定义编辑器实现
public class BoolRadioEditor : DataFormEditorBase<bool?>
{
    protected override void BuildEditorView(DataFormEditorModel model)
    {
        // 构建Yes/No单选按钮的DOM元素
        var yesRadio = new InputRadio<bool> { Value = true, Name = model.FieldName };
        var noRadio = new InputRadio<bool> { Value = false, Name = model.FieldName };
        
        // 绑定值和变化事件
        yesRadio.CheckedChanged += (s, e) => UpdateValue(model, true);
        noRadio.CheckedChanged += (s, e) => UpdateValue(model, false);
        
        // 将控件添加到编辑器容器
        Container.Add(yesRadio);
        Container.Add(new Label { Content = "Yes" });
        Container.Add(noRadio);
        Container.Add(new Label { Content = "No" });
    }

    private void UpdateValue(DataFormEditorModel model, bool value)
    {
        model.Value = value;
        // 通知DataForm验证状态变化
        model.Validate();
    }
}

为什么你之前的方案失效?

  1. 直接反射SetValue:@bind-checked需要的是可绑定的表达式,而不是方法调用,无法实现双向绑定
  2. 字典存储值:字典的键值对脱离了原模型,EditContext无法跟踪原模型的属性变化,验证逻辑失效
  3. 包装对象:包装类的属性和原模型属性无关联,验证规则是针对原模型的,所以无法触发验证

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 08:41:00