我试图将一些属性绑定到具有多个参数的索引器。
public decimal this[CalculationType calcType, AmountSource source]
{
get
{
var foundRemittance = this.RemittanceAmounts.FirstOrDefault(a => a.CalculationType == calcType);
if (foundRemittance != null)
{
return foundRemittance[source];
}
return 0.0m;
}
}
我的约束力:
Value="{Binding Path=WorkingEntity.AmountDetails[{x:Static edm:CalculationType.RRQRPC}\,{x:Static edm:AmountSource.Applicable}]}"
无论我做什么,价值都不会出现。
索引器从Watch:
返回值答案 0 :(得分:1)
我对此进行了测试,发现绑定使用带有两个int参数的索引属性(例如this[int x, int y]
),但它不能与枚举一起使用。我将PresentationTraceSources.TraceLevel=High
放在绑定上,对于枚举索引器,它告诉我At level 1 - for AmountDetails[] found accessor <null>
- 而附近的绑定在同一个类的同一个实例上找到[int,int]索引器重载没有问题。
如果我添加一个索引器public String this[object a, object b]
,它将使用字符串"{x:Static local:CalculationType.RRQRPC}
“和"{x:Static local:AmountSource.Applicable}"
调用两个索引器参数。因此XAML解析器不会解析那些{{1}事情。
我是这样做的。它不像你想要的那么干净,但它有效。
如果需要绑定两个索引器参数的不同值,可以编写一个类似的多值转换器并使用多重绑定。
x:Static
XAML:
public class AmountDetailsIndexer : MarkupExtension, IValueConverter
{
public AmountDetailsIndexer()
{
}
public AmountDetailsIndexer(CalculationType ctype, AmountSource asource)
{
CalculationType = ctype;
AmountSource = asource;
}
public CalculationType CalculationType { get; set; }
public AmountSource AmountSource { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(Object value, Type type, Object converterParameter, System.Globalization.CultureInfo cultureInfo)
{
var details = value as AmountDetails;
return details[CalculationType, AmountSource];
}
public object ConvertBack(Object value, Type type, Object converterParameter, System.Globalization.CultureInfo cultureInfo)
{
throw new NotImplementedException();
}
}
使用这种类型的东西,或者使用多值转换器,我认为编写使用反射的广义多索引器应该是相对微不足道的。
这是使用反射的通用版本。
<!-- Constructor with parameters -->
<Label
Content="{Binding AmountDetails,
Converter={local:AmountDetailsIndexer RRQRPC, Applicable}}"
/>
<!--
Parameterless constructor. More typing, but here you get intellisense
when you set the properties
-->
<Label
Content="{Binding AmountDetails,
Converter={local:AmountDetailsIndexer CalculationType=RRQRPC, AmountSource=Applicable}}"
/>
XAML:
public class Indexer : MarkupExtension, IValueConverter
{
public Indexer(object a0)
{
_arguments.Add(a0);
}
public Indexer(object a0, object a1)
{
_arguments.Add(a0);
_arguments.Add(a1);
}
public Indexer(object a0, object a1, object a2)
{
_arguments.Add(a0);
_arguments.Add(a1);
_arguments.Add(a2);
}
public Indexer(object a0, object a1, object a2, object a3)
{
_arguments.Add(a0);
_arguments.Add(a1);
_arguments.Add(a2);
_arguments.Add(a3);
}
private List<object> _arguments = new List<object>();
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(Object value, Type type, Object converterParameter, System.Globalization.CultureInfo cultureInfo)
{
var argTypes = _arguments.Select(p => p.GetType()).ToList();
// Indexers with the correct number of parameters
var sameAryIndexers = value.GetType().GetProperties()
.Where(prop =>
prop.Name == "Item"
// Must have same number of parameters
&& prop.GetIndexParameters().Length == argTypes.Count)
.ToList();
var indexerProperties =
sameAryIndexers
.Where(prop =>
prop.GetIndexParameters()
.Select(pi => pi.ParameterType)
.Zip(argTypes, (paramType, argType) => paramType.Equals(argType))
.All(b => b)
).ToList();
// If no exact match, try overloads. This is sketchy, if you ask me.
if (indexerProperties.Count != 1)
{
indexerProperties =
sameAryIndexers
.Where(prop =>
prop.GetIndexParameters()
.Select(pi => pi.ParameterType)
.Zip(argTypes, (paramType, argType) => paramType.IsAssignableFrom(argType))
.All(b => b)
).ToList();
}
if (indexerProperties.Count != 1)
{
var argTypeNames = String.Join(", ", argTypes.Select(t => t.Name));
throw new Exception($"Unable to resolve overload: Input arguments {argTypeNames}, {indexerProperties.Count} matching indexers.");
}
try
{
var x = indexerProperties.First().GetValue(value, _arguments.ToArray());
return x;
}
catch (Exception ex)
{
return null;
}
}
public object ConvertBack(Object value, Type type, Object converterParameter, System.Globalization.CultureInfo cultureInfo)
{
throw new NotImplementedException();
}
protected bool IsTypesAssignableFrom(IEnumerable<Type> to, IEnumerable<Type> from)
{
return to.Zip(from, (tt, tf) => tt.IsAssignableFrom(tf)).All(b => b);
}
}