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}}"
    />