TheIntern
TheIntern

Reputation: 374

Data binding to dictionary with Tuple key in C#

I have a Dictionary<Tuple<SomeEnum, double, string>, double> that I'm trying to bind to in Xamarin. I'm just trying to bind the value (the double) to a label, nothing terribly fancy. The problem I'm having is that I can't figure out the path syntax to bind to a Tuple key. I know that with a Dictionary<string, double> I would just let Path="[" + keyString + "]", for example, but I can't figure out what to do if I want to use a Tuple as the key. Even Microsoft's page on data binding path syntax in C# doesn't seem to explicitly handle this case, although it does note that

Inside indexers you can have multiple indexer parameters separated by commas (,). The type of each parameter can be specified with parentheses. For example, you can have Path="[(sys:Int32)42,(sys:Int32)24]", where sys is mapped to the System namespace.

Now, I'd assume that this holds for single indexer objects too. So in my dictionary case, I suppose I would have something like Path="[(sys:Tuple<SomeEnum, sys:double, sys:string>)???]", but I'm not sure what goes where ??? is. And I'm not sure if I'd need a namespace before SomeEnum. And finally, I can't seem to find any more detailed documentation. So I'll take help on any of these fronts.

Ultimately, I fear I may have to just give up, switch to a Dictionary<string, double>, and let the string be the .ToString() concatenation of the types in the Tuple, e.g. by letting each key be string key = SomeEnum.SomeValue + "," + someDouble + "," + someString, but I'd really just prefer to do this correctly. I'm open to changing the Tuple to another non-string type if that helps though. Perhaps some other immutable type? I'm not terribly proficient with C# data binding beyond the basics, so any help or ideas are greatly appreciated.

Upvotes: 1

Views: 973

Answers (1)

Xiaoy312
Xiaoy312

Reputation: 14477

Actually, multi-indexer is supported by data-binding. The syntax is the following:

<object Path="[index1,index2...]" .../>  
or  
<object Path="propertyName[index,index2...]" .../>

-- PropertyPath XAML Syntax

However, creating a Tuple within a XAML path can be quite difficult. There is 2 solutions to this:

  1. Create a custom markup extension to generate the Tuple
  2. Create a dictionary that support multi-indexer

The easiest and cleanest is probably solution#2. (PathToDictionary[{ns:Tuple a,b,c}] vs PathToDictionary[a,b,c]).


Use this as the dictionary:

public class TripleKeyedDictionary<TKey1, TKey2, TKey3, TValue> : Dictionary<Tuple<TKey1, TKey2, TKey3>, TValue>
{
    public TValue this [TKey1 key1, TKey2 key2, TKey3 key3]
    {
        get { return this[Tuple.Create(key1, key2, key3)]; }
        set { this[Tuple.Create(key1, key2, key3)] = value; }
    }
}

Example of usage: PathToDictionary[Banana,b,c]

Full example for LINQPad:

void Main()
{
    var data = new
    {
        Lookup = new TripleKeyedDictionary<Fruit, string, string, string>
        {
            { Tuple.Create(Fruit.Banana, "b", "c"), "value" }
        }
    };

    var binding = new Binding("Lookup[Banana,b,c]");
    binding.Mode = BindingMode.OneWay;
    binding.Source = data;
    binding.FallbackValue = "fallback";
    binding.TargetNullValue = "null";

    var block = new TextBlock().Dump();
    block.SetBinding(TextBlock.TextProperty, binding);
}

// Define other methods and classes here
public enum Fruit
{
    Apple, Banana, Cactus
}
public class TripleKeyedDictionary<TKey1, TKey2, TKey3, TValue> : Dictionary<Tuple<TKey1, TKey2, TKey3>, TValue>
{
    public TValue this [TKey1 key1, TKey2 key2, TKey3 key3]
    {
        get { return this[Tuple.Create(key1, key2, key3)]; }
        set { this[Tuple.Create(key1, key2, key3)] = value; }
    }
}

Upvotes: 1

Related Questions