我正在尝试使用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;
}
}
为了总结我最终得到的解决方案:
-
我创建了索引属性类包装器,允许我访问值(注意,我创建了另外两个只读和写的包装器,以防有帮助(
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); } } }
-
我创建了一个";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 }
如果性能对您很重要,请检查此链接!
-
在我的表单中,我使用索引属性进行绑定,如下所示:
@foreach (var propertyName in FieldsList) { <div> <InputText id="name" @bind-Value="@myContextObject.StrValues[propertyName]" /> </div> }
感谢@copycar_am和此链接。