Adding SortedList or Dictionary<int, string> to ResourceDictionary

c#, wpf, xaml

Solution

`SortedList` is easy as it is not generic.

If a class implements `IDictionary` you can add values by defining them as the child nodes using `x:Key` to set the key by which they should be added to the dictionary.

xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
<col:SortedList x:Key="list">
    <sys:String x:Key="0">Lorem</sys:String>
    <sys:String x:Key="1">Ipsum</sys:String>
    <sys:String x:Key="2">Dolor</sys:String>
    <sys:String x:Key="3">Sit</sys:String>
</col:SortedList>
<!-- Usage: -->
<ContentControl Content="{Binding [0], Source={StaticResource list}}" />

The item keys are strings here, to get actual ints you could use a custom markup extension which parses the string to int, or by defining the keys as resource first:

<sys:Int32 x:Key="key1">0</sys:Int32>
<sys:Int32 x:Key="key2">1</sys:Int32>
<sys:Int32 x:Key="key3">2</sys:Int32>
<sys:Int32 x:Key="key4">3</sys:Int32>

<col:SortedList x:Key="list">
    <sys:String x:Key="{StaticResource key1}">Lorem</sys:String>
    <sys:String x:Key="{StaticResource key2}">Ipsum</sys:String>
    <sys:String x:Key="{StaticResource key3}">Dolor</sys:String>
    <sys:String x:Key="{StaticResource key4}">Sit</sys:String>
</col:SortedList>

The binding then becomes more complex as the indexer value needs to be cast to int explicitly as it otherwise would be interpreted as string.

<ContentControl Content="{Binding Path=[(sys:Int32)0],
                                  Source={StaticResource list}}"/>

You cannot omit the `Path=` because of an implementation detail.

Dictionaries are not so easy because they are generic and there (currently) is no simple built-in way to create generic objects in XAML. Using markup extensions however you can create generic objects via reflection.

Implementing `IDictionary` on such an extension also allows you to fill that newly created instance. Here is a very sketchy example:

public class DictionaryFactoryExtension : MarkupExtension, IDictionary
{
    public Type KeyType { get; set; }
    public Type ValueType { get; set; }

    private IDictionary _dictionary;
    private IDictionary Dictionary
    {
        get
        {
            if (_dictionary == null)
            {
                var type = typeof(Dictionary<,>);
                var dictType = type.MakeGenericType(KeyType, ValueType);
                _dictionary = (IDictionary)Activator.CreateInstance(dictType);
            }
            return _dictionary;
        }
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return Dictionary;
    }

    public void Add(object key, object value)
    {
        if (!KeyType.IsAssignableFrom(key.GetType()))
            key = TypeDescriptor.GetConverter(KeyType).ConvertFrom(key);
        Dictionary.Add(key, value);
    }

    #region Other Interface Members
    public void Clear()
    {
        throw new NotSupportedException();
    }
    public bool Contains(object key)
    {
        throw new NotSupportedException();
    }
    // <Many more members that do not matter one bit...>
    #endregion
}
<me:DictionaryFactory x:Key="dict" KeyType="sys:Int32" ValueType="sys:String">
    <sys:String x:Key="0">Lorem</sys:String>
    <sys:String x:Key="1">Ipsum</sys:String>
    <sys:String x:Key="2">Dolor</sys:String>
    <sys:String x:Key="3">Sit</sys:String>
</me:DictionaryFactory>

As passing in a typed instance as key is a bit of a pain i chose to do the conversion in `IDictionary.Add` before the value is added to the internal dictionary instead (this may cause problems with certain types).

Since the dictionary itself is typed the binding should not require a cast.

<ContentControl Content="{Binding [0], Source={StaticResource dict}}" />

Problem

Is there a way to add a SortedList or a Dictionary to a ResourceDictionary and use (and bind!) it to a control via XAML? I've tried this, but I couldn't figure out how to do it: ``` <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:coll="clr-namespace:System.Collections.Generic;assembly=mscorlib"> <x:Array x:Key="test" Type="sys:Object"> <coll:KeyValuePair>***</coll:KeyValuePair> </x:Array> ```

Original source

Related problems