添加SortedList或Dictionary< int,string>到ResourceDictionary

问题描述:

有没有一种方法可以将SortedList或Dictionary添加到ResourceDictionary并通过XAML将其使用(并绑定!)到控件上?

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 很容易,因为它不是通用的。

SortedList is easy as it is not generic.

如果类实现了 IDictionary 可以通过使用 x:Key 设置将其添加到字典的密钥。

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

此处的项目关键字是字符串,要获取实际的整数,可以使用自定义标记扩展来解析要转换为int的字符串,或者首先将键定义为资源:

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>

然后,绑定变得更加复杂,因为需要将索引器值显式转换为int,否则

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

不能省略 Path = 是因为实现细节

字典不是那么容易,因为它们是通用的,并且(目前)还没有一种简单的内置方法可以在XAML中创建通用对象。但是,可以使用标记扩展来通过反射来创建通用对象。

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.

在这样的扩展上实现 IDictionary 也可以使您填充该新创建的实例。这是一个非常粗略的示例

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>

由于将类型化实例作为键传递,所以我选择在其中进行转换有点痛苦 IDictionary.Add ,然后将值添加到内部字典中(这可能会导致某些类型的问题)。

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