1

我想绑定我的自定义类型(使用运行时填充的属性或数据)。由于我在编译时不知道属性的数量,因此我需要创建一个包含它们和它们的值的列表。

稍后,我想将包含此对象的列表绑定到某个网格或 DropDownList。

我知道那些控件通过 DataSource 属性接收一个 DataSource(对象),当我调用 DataBind() 方法时,默认实现使用反射来获取属性和数据并写入该控件。

我有一个类型,让我们想象一下:

class MyDynamicDataObject {
Dictionary<string, object> _properties;

public MyDynamicDataObject() {
    _properties = new Dictionary<string, object>();
}

public void Add(string property, object value) {
    _properties.Add(property, value);   
}

}

但是我需要知道“覆盖”或者说,以抽象的方式返回我的类型的属性,这些属性包含在 _properties 字典数据结构中。

我怎样才能让控件使用 DataBind 并使用默认实现(使用 Type.GetProperties) - 但不是返回我没有的类型属性,而是返回字典中包含的抽象?

谢谢

4

2 回答 2

2

大多数数据绑定已经支持动态数据模型,但不支持dynamic数据模型。我的意思是:有这方面的现有机制,包括ICustomTypeDescriptor,(可能通过TypeDescriptionProviderITypedList,,和PropertyDescriptor。这是使用的机制DataTable和类似的机制,因此得到了很好的锻炼。由于您要绑定到一个列表,ITypedList因此可能是最适合实现的接口,这意味着您只需要编写一个自定义PropertyDescriptor来映射您的字典 API 和描述符 API。

这是一个winforms示例:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;

static class Program
{
    [STAThread]
    static void Main()
    {
        using(var form = new Form())
        using(var grid = new DataGridView())
        {
            var list = new MySpecialList();
            var obj = new MyDynamicDataObject();
            obj["Foo"] = 123;
            obj["Bar"] = "def";
            list.Add(obj);
            obj = new MyDynamicDataObject();
            obj["Bar"] = "abc";
            obj["Blap"] = 123.4F;
            list.Add(obj);

            grid.DataSource = list;
            grid.Dock = DockStyle.Fill;
            form.Controls.Add(grid);
            Application.Run(form);
        }
    }
}
class MySpecialList : List<MyDynamicDataObject>, ITypedList
{
    PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
    {
        // don't worry about sub-property access unless you need to
        if(listAccessors != null && listAccessors.Length != 0) throw new NotSupportedException();

        var allKeys = new HashSet<string>();
        foreach(var item in this)
        {
            foreach (string key in item.GetKeys()) allKeys.Add(key);
        }
        var props = allKeys.Select(key => new MyDynamicDataObjectDescriptor(key));
        return new PropertyDescriptorCollection(props.ToArray());
    }
    private class MyDynamicDataObjectDescriptor : PropertyDescriptor
    {
        public MyDynamicDataObjectDescriptor(string name) : base(name, new Attribute[0]) { }

        public MyDynamicDataObject GetObject(object component)
        {
            return (MyDynamicDataObject) component;
        }
        public override object GetValue(object component)
        {
            return GetObject(component)[Name];
        }
        public override void SetValue(object component, object value)
        {
            GetObject(component)[Name] = value;
        }
        public override bool CanResetValue(object component)
        {
            return false;
        }
        public override void ResetValue(object component)
        {
            throw new NotSupportedException();
        }
        public override bool ShouldSerializeValue(object component)
        {
            return false;
        }
        public override bool IsReadOnly
        {
            get { return false; }
        }
        public override Type ComponentType
        {
            get { return typeof (MyDynamicDataObject); }
        }
        public override Type PropertyType
        {
            get { return typeof (object); }
        }
    }

    string ITypedList.GetListName(PropertyDescriptor[] listAccessors)
    {
        throw new System.NotImplementedException();
    }
}
class MyDynamicDataObject
{
    // don't recommend inheriting this; confuses matters a lot...
    private Dictionary<string,object> props = new Dictionary<string, object>();
    public string[] GetKeys()
    {
        return props.Keys.ToArray();
    }
    public void Clear(string key)
    {
        props.Remove(key);
    }
    public object this[string key]
    {
        get
        {
            object value;
            if (!props.TryGetValue(key, out value)) value = null;
            return value;
        }
        set
        {
            props[key] = value;
        }
    }
}
于 2012-06-29T09:23:35.667 回答
1
 public interface IDynamicObject
{
    string[] Properties { get; }
    void Clear();
    object this[string property] { get; set; }
}



public class DynamicObject : IDynamicObject
{
    readonly Dictionary<string, object> _dynamicProperties = new Dictionary<string, object>();

    public string[] Properties
    {
        get { return _dynamicProperties.Keys.ToArray(); }
    }

    public void Clear() { _dynamicProperties.Clear(); }

    public object this[string property]
    {
        get
        {
            object value;

            if (!_dynamicProperties.TryGetValue(property, out value))
                value = null;

            return value;
        }

        set
        {
            _dynamicProperties[property] = value;
        }
    }
}



public class DynamicObjectPropertyDescriptor<T> : PropertyDescriptor
    where T : IDynamicObject
{
    public DynamicObjectPropertyDescriptor(string name) : base(name, new Attribute[0])
    {

    }



    T Get(object component)
    {
        return (T)component;
    }



    public override bool IsReadOnly                                  { get { return false; } }
    public override bool CanResetValue(object component)             { return true; }
    public override void ResetValue(object component)                { Get(component)[Name] = null; }

    public override Type ComponentType                               { get { return typeof(T); } }
    public override Type PropertyType                                { get { return typeof(object); } }

    public override object  GetValue(object component)               { 
        return Get(component)[Name];
    }
    public override void    SetValue(object component, object value) { 
        Get(component)[Name] = value; 
    }

    public override bool ShouldSerializeValue(object component)      { return false; }
}



public class DynamicList<T> : List<T>, ITypedList 
    where T : IDynamicObject
{
    int bindingIndex = 0;


    public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
    {
        if (listAccessors != null && listAccessors.Length != 0)
            throw new NotSupportedException();

        if (Count == 0)
            return new PropertyDescriptorCollection(new[] { new DynamicObjectPropertyDescriptor<T>("-") });

        PropertyDescriptorCollection result = new PropertyDescriptorCollection(this[bindingIndex].Properties.Select(prop => new DynamicObjectPropertyDescriptor<T>(prop)).ToArray());

        if (++bindingIndex == Count)
            bindingIndex = 0;

        return result;
    }

    public string GetListName(PropertyDescriptor[] listAccessors)
    {
        return typeof(DynamicList<T>).Name;
    }
}

要查看此工作:

DynamicList<DynamicObject> list = new DynamicList<DynamicObject>();

        DynamicObject a1 = new DynamicObject();

        a1["nome"] = "Goncalo Dias";
        a1["numero"] = 30337;


        DynamicObject a2 = new DynamicObject();

        a2["nome"] = "Carlos Antunes";
        a2["numero"] = 10222;


        DynamicObject a3 = new DynamicObject();

        a3["nome"] = "Tiago Rodrigues";
        a3["numero"] = 4040;

        DynamicObject a4 = new DynamicObject();

        a4["nome"] = "Digoo Martins";
        a4["numero"] = 1220;
        a4["morada"] = "Rua da esquina";

        list.Add(a1);
        list.Add(a2);
        list.Add(a3);
        list.Add(a4);


        ultraGrid1.DataSource = list;
        ultraGrid1.DataBind();
于 2012-06-29T14:13:18.210 回答