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?属性... } }
为什么这个方案可行?
- 直接绑定原模型:所有操作都基于
MyModel的原始属性,不会脱离模型上下文 - EditContext跟踪:通过
NotifyFieldChanged手动通知属性变化,确保验证逻辑正常触发 - 原生验证支持:
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(); } }
为什么你之前的方案失效?
- 直接反射SetValue:
@bind-checked需要的是可绑定的表达式,而不是方法调用,无法实现双向绑定 - 字典存储值:字典的键值对脱离了原模型,
EditContext无法跟踪原模型的属性变化,验证逻辑失效 - 包装对象:包装类的属性和原模型属性无关联,验证规则是针对原模型的,所以无法触发验证
内容的提问来源于stack exchange,提问作者Daniel Parker
相关产品推荐
相关产品推荐

