如何将动态生成对象的值绑定到DynamicComponent?
我有一个在运行时生成类型的对象,还有一个自定义的DynamicComponent组件,它有一个名为Value的参数,我想将生成对象的属性绑定到该参数。如果在运行前知道类型,我会使用@bind-Value,或者Value="@MyObject.Value"和ValueChanged="@(v => MyObject.Value = v)"。
目前遇到的问题:DynamicComponent的Value初始值设置正确,组件修改值时onChangeEvent能成功触发,但外部修改form的Value后,组件不会随之更新。怀疑是因为GetValue(Instance)返回的是Value的副本。
实验代码(Razor部分)
@foreach (FormInput input in form.Inputs) { <DynamicComponent Type="@input.InputType" Parameters="@input.Parameters" /> } @code { private Form form; private string inputName = string.Empty; protected override void OnInitialized() { InputAttribute reqAttribute = new() { Type = typeof(RequiredAttribute), ConstructorArguments = Array.Empty<object>() }; FormInput input = new() { InputType = typeof(TsInputText), ReturnType = typeof(string), Attributes = new() { reqAttribute } }; inputName = input.Id; Dictionary<string, object> parameters = new(); parameters.Add("Label", "Test"); parameters.Add("PropertyName", input.Id); input.Parameters = parameters; List<FormInput> inputs = new() { input }; form = new(inputs); form.BindProperty(input, "Value"); } }
Form类代码(C#)
public Type Model { get; private set; } public object Instance { get; private set; } public List<FormInput> Inputs { get; set; } public Form(IEnumerable<FormInput> inputs) { // Model is generated here. // Model has a property named input.Id for each input in the inputs argument. // Instance of the Model type is instantiated here } public void BindProperty(FormInput input, string inputParameter) { input.Parameters.Add(inputParameter, GetPropertyInfo(Model, input.Id).GetValue(Instance)); EventCallback<string> onChangeEvent = EventCallback.Factory.Create<string>(this, s => SetProperty(input.Id, s)); input.Parameters.Add($"{inputParameter}Changed", onChangeEvent); }
问题核心是值类型的副本传递:GetValue(Instance)返回的是值类型的副本(比如string、int),存入Parameters字典后,外部修改form.Instance的属性时,Parameters里的旧值不会自动同步,DynamicComponent也就不会更新。以下是两种可行方案:
方案一:手动同步Parameters(简单直接)
每次外部修改form.Instance的属性后,手动更新对应FormInput的Parameters字典,并触发组件重新渲染。
给Form类添加同步方法:
public void SyncInputValue(FormInput input, string inputParameter) { // 更新Parameters中的Value为最新值 input.Parameters[inputParameter] = GetPropertyInfo(Model, input.Id).GetValue(Instance); }
外部修改属性后,执行以下操作:
// 假设获取到对应的FormInput实例 var targetInput = form.Inputs.First(i => i.Id == inputName); form.SyncInputValue(targetInput, "Value"); // 触发组件重新渲染,让DynamicComponent读取新值 StateHasChanged();
方案二:用包装类实现引用传递(自动同步)
通过引用类型的包装类,让Parameters存储引用而非值类型副本,外部修改包装类内部值时,组件能直接读取最新值。
- 定义通用包装类:
public class ValueWrapper<T> { public T Value { get; set; } }
修改
Form类的模型生成逻辑:
将原本的属性类型(如string)替换为ValueWrapper<string>,确保生成的Model中每个属性都是包装类类型。调整
BindProperty方法:
public void BindProperty(FormInput input, string inputParameter) { var propertyInfo = GetPropertyInfo(Model, input.Id); var wrapper = propertyInfo.GetValue(Instance) as ValueWrapper<string>; // 存入包装类的Value(后续可通过引用直接更新) input.Parameters.Add(inputParameter, wrapper.Value); // 修改回调,直接更新包装类的内部值 EventCallback<string> onChangeEvent = EventCallback.Factory.Create<string>(this, s => { wrapper.Value = s; SetProperty(input.Id, s); // 保留原有业务逻辑 }); input.Parameters.Add($"{inputParameter}Changed", onChangeEvent); }
- 外部修改属性时:
var propertyInfo = form.GetPropertyInfo(form.Model, inputName); var wrapper = propertyInfo.GetValue(form.Instance) as ValueWrapper<string>; wrapper.Value = "外部修改的新值"; // 触发组件渲染,让UI更新 StateHasChanged();
注意事项
- 两种方案都需要调用
StateHasChanged()触发组件重新渲染,否则UI不会更新。 - 方案一适合简单场景,实现成本低;方案二更适合频繁同步的场景,减少手动同步的代码量。
内容的提问来源于stack exchange,提问作者Torin

