C# XAML绑定到具有多个参数的索引器
我正在尝试将某些属性绑定到具有多个参数的索引器C# XAML绑定到具有多个参数的索引器,c#,wpf,xaml,C#,Wpf,Xaml,我正在尝试将某些属性绑定到具有多个参数的索引器 public decimal this[CalculationType calcType, AmountSource source] { get { var foundRemittance = this.RemittanceAmounts.FirstOrDefault(a => a.CalculationType == calcType); if (foundRemittance != null
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}]}"
无论我做什么,值都不会显示
索引器从手表返回值:
我对此进行了测试,发现绑定可以使用带有两个int参数的索引属性(例如,
this[int x,int y]
),但不能使用枚举。我将PresentationTraceSources.TraceLevel=High
放在绑定上,对于枚举索引器,它告诉我级别1-对于AmountDetails[]found访问器
——而附近的绑定在同一类的同一实例上查找[int,int]索引器重载没有问题
如果我在[object a,object b]中添加一个索引器
公共字符串this[object a,object b]
,它将通过字符串“{x:Static local:CalculationType.RRQRPC}
”和“{x:Static local:AmountSource.applicative}”来调用这两个索引器参数。因此XAML解析器没有解析那些x:Static
东西
我会这样做的。它不像你想要的那么干净,但它很管用
若需要绑定两个索引器参数的不同值,可以编写一个类似的多值转换器并使用多绑定
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();
}
}
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);
}
}
更新
这是使用反射的通用版本
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);
}
}
公共类索引器:MarkupExtension,IValueConverter
{
公共索引器(对象a0)
{
_参数。添加(a0);
}
公共索引器(对象a0、对象a1)
{
_参数。添加(a0);
_添加(a1);
}
公共索引器(对象a0、对象a1、对象a2)
{
_参数。添加(a0);
_添加(a1);
_添加(a2);
}
公共索引器(对象a0、对象a1、对象a2、对象a3)
{
_参数。添加(a0);
_添加(a1);
_添加(a2);
_增加(a3);
}
私有列表_参数=新列表();
公共覆盖对象ProviderValue(IServiceProvider服务提供程序)
{
归还这个;
}
公共对象转换(对象值、类型、对象转换参数、System.Globalization.CultureInfo CultureInfo)
{
var argTypes=_arguments.Select(p=>p.GetType()).ToList();
//具有正确数量的参数的索引器
var sameAryIndexers=value.GetType().GetProperties()
.其中(prop=>
项目名称==“项目”
//必须具有相同数量的参数
&&prop.GetIndexParameters().Length==argTypes.Count)
.ToList();
变量索引器属性=
sameAryIndexers
.其中(prop=>
prop.GetIndexParameters()
.Select(pi=>pi.ParameterType)
.Zip(argTypes,(paramType,argType)=>paramType.Equals(argType))
.All(b=>b)
).ToList();
//如果没有精确匹配,请尝试重载。如果你问我,这是粗略的。
if(indexerProperties.Count!=1)
{
索引器属性=
sameAryIndexers
.其中(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));
抛出新异常($“无法解析重载:输入参数{argTypeNames},{indexerProperties.Count}匹配索引器。”);
}
尝试
{
var x=indexerProperties.First().GetValue(value,_arguments.ToArray());
返回x;
}
捕获(例外情况除外)
{
返回null;
}
}
公共对象转换回(对象值、类型、对象转换参数、System.Globalization.CultureInfo CultureInfo)
{
抛出新的NotImplementedException();
}
受保护的布尔类型为可分配自(IEnumerable到,IEnumerable从)
{
返回到.Zip(from,(tt,tf)=>tt.IsAssignableFrom(tf)).All(b=>b);
}
}
XAML:
我对它进行了测试,发现它可以使用带有两个int
参数的索引属性(例如this[int x,int y]
),但它不能使用枚举。我将PresentationTraceSources.TraceLevel=High
放在绑定上,对于枚举索引器,它告诉我级别1-对于AmountDetails[]found accessor
,而它在同一类的同一实例上找到[int,int]accessor重载并没有问题。@EdPlunkett Cool infos。你知道为什么会这样吗?除了使用幻数,我如何解决这个问题呢?如果我在[object a,object b]
中添加一个索引器公共字符串this[object a,object b]
,它将被字符串“{x:Static local:CalculationType.RRQRPC}”调用和“{x:Static local:AmountSource.applicative}”
。所以XAML解析器没有解析那些x:静态的东西。有趣的是。。。所以我可能不得不使用一个转换器。我恐怕这将是一个转换器。虽然objectdataprovider是这种情况下的预期解决方案,但这一个肯定更重要readable@Dbl使用ObjectDataProvider
,您将如何做到这一点?20@Dbl调用索引器吗?@EdPlunkett显然不是-但是你可以编写一个通用版本来快速完成你在这里做的事情
<Label
Content="{Binding AmountDetails,
Converter={local:Indexer
{x:Static local:CalculationType.RRQRPC},
{x:Static local:AmountSource.Applicable}}}"
/>
<!--
BEWARE
As far as XAML is concerned, we're passing it the strings "123" and "345".
But {Binding AmountDetails[123, 345]} already works, so hey.
-->
<Label
Content="{Binding AmountDetails,
Converter={local:Indexer 123, 345}}"
/>