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>
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}}" />
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With