Blazor:是否可以使用反射通过名称将泛型项绑定到属性



我正在尝试使用Blazor获得一个通用编辑器模板,该模板绑定到通过反射找到的属性,如下所示:

@typeparam TItem
@foreach (var propertyName in FieldsList) {
<div>
<InputText id="name" @bind-Value="@getBindProperty(propertyName)" />
</div>
}
@functions {
[Parameter]
public TItem ItemEditModel { get; set; }
public string[] FieldsList {
get {
// get all properties decorated with some custom attribute...
return typeof(TItem).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(MyCustomAttribute))).Select(x => x.Name).ToArray();
}
}
public string getBindProperty(string propName) {
return string.Format("{0}.{1}", nameof(ItemEditModel), propName);
}
}

以上内容不被接受为赋值的左侧必须是变量、属性或索引器

所以我无法通过名称绑定到属性,所以我继续尝试其他语法,如

<InputText id="name" @bind-Value="ItemEditModel.propertyName" />

这也不被接受,因为TItem不包含"propertyName"的定义

好吧,以上所有内容都有道理,但关键问题是,对此有什么需要做的吗?还是不可能通过名称将控件绑定到属性?

额外问题:如果确实可以做到这一点,是否有方法根据属性类型进行切换(通常只有诸如"string"、"DateTime"、"int"、"double"、"bool"等基本类型(?

如果使用InputText Blazor的组件,则应使用EditForm Blazor组件包装它。

另一种方法可能如下:

@typeparam TItem
@foreach (var propertyName in FieldsList)
{
<div>
<input @bind="PropertyVars[propertyName]" />
</div>
}
<button type="button" @onclick="OnSubmit">Reset</button>
@code {
[Parameter]
public TItem ItemEditModel { get; set; }
public Dictionary<string, string> PropertyVars { get; set; } = new 
Dictionary<string, string>();
protected override void OnInitialized()
{
foreach (var propertyName in FieldsList)
{
var propertyInfo = ItemEditModel.GetType().GetProperty(propertyName);
PropertyVars.Add(propertyName, 
propertyInfo?.GetValue(ItemEditModel).ToString());
}
}
public string[] FieldsList
{
get
{
return typeof(TItem).GetProperties().Select(x => x.Name).ToArray();
}
}
private void OnSubmit()
{
var tt = PropertyVars;
foreach (var propertyName in FieldsList)
{
var propertyInfo = ItemEditModel.GetType().GetProperty(propertyName);
var uu = PropertyVars[propertyName].GetType();
if (uu == propertyInfo?.PropertyType)
{
propertyInfo.SetValue(ItemEditModel, 
Convert.ChangeType(PropertyVars[propertyName], propertyInfo.PropertyType),
null);
}
}
var yy = ItemEditModel;
}
}

为了总结我最终得到的解决方案:

  1. 我创建了索引属性类包装器,允许我访问值(注意,我创建了另外两个只读和写的包装器,以防有帮助(

    public class IndexedProperty<TIndex, TValue> {
    readonly Action<TIndex, TValue> SetAction;
    readonly Func<TIndex, TValue> GetFunc;
    public IndexedProperty(Func<TIndex, TValue> getFunc, Action<TIndex, TValue> setAction) {
    this.GetFunc = getFunc;
    this.SetAction = setAction;
    }
    public TValue this[TIndex i] {
    get {
    return GetFunc(i);
    }
    set {
    SetAction(i, value);
    }
    }
    }
    
    public class ReadOnlyIndexedProperty<TIndex, TValue> {
    readonly Func<TIndex, TValue> GetFunc;
    public ReadOnlyIndexedProperty(Func<TIndex, TValue> getFunc) {
    this.GetFunc = getFunc;
    }
    public TValue this[TIndex i] {
    get {
    return GetFunc(i);
    }
    }
    }
    
    public class WriteOnlyIndexedProperty<TIndex, TValue> {
    readonly Action<TIndex, TValue> SetAction;
    public WriteOnlyIndexedProperty(Action<TIndex, TValue> setAction) {
    this.SetAction = setAction;
    }
    public TValue this[TIndex i] {
    set {
    SetAction(i, value);
    }
    }
    }
    
  2. 我创建了一个";BindableBase";我的所有对象都继承自的对象,在其中我添加了索引属性:

    IndexedProperty<string, string> _strValues;
    [JsonIgnore]
    public IndexedProperty<string, string> StrValues {
    get {
    if (_strValues == null) {
    _strValues = new IndexedProperty<string, string>(GetStrValue, SetStrValue);
    }
    return _strValues;
    }
    }
    private string GetStrValue(string propName) {
    // use reflection here to get the property value as string
    // --- NOTE: beware of performance here I suggest you take a look at related link
    }
    private void SetStrValue(string propName, string value) {
    // use reflection here to SET the property value from string
    // --- NOTE: beware of performance here I suggest you take a look at related link
    }
    

如果性能对您很重要,请检查此链接!

  1. 在我的表单中,我使用索引属性进行绑定,如下所示:

    @foreach (var propertyName in FieldsList) {
    <div>
    <InputText id="name" @bind-Value="@myContextObject.StrValues[propertyName]" />
    </div>
    }
    

感谢@copycar_am和此链接。

最新更新