gds03
gds03

Reputation: 1379

Exist some way to return PropertyInfo[] for my Dynamic Type?

I want to bind my custom type (with properties, or data lets say, that are filled in runtime). As I don't know the number of properties at compile time, I need to create a list that contain them and they value.

Later, I want to bind a list with this objects to some grid or DropDownList.

I know that those controls receive a DataSource (object) through the DataSource property, and when I call the method DataBind(), the default implementation use reflection to get properties and data and write to that control.

I have a type, lets imagine:

class MyDynamicDataObject {
Dictionary<string, object> _properties;

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

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

}

But I need know to "override" or lets say, return in abstract way, the properties of my type, that are contained in _properties Dictionary data-structure.

How can I do this to let controls use DataBind and use default implementation (use Type.GetProperties) - but instead of return my type properties that are none, return an abstraction that are contained in the dictionary?

Thanks

Upvotes: 2

Views: 460

Answers (2)

gds03
gds03

Reputation: 1379

 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;
    }
}

To see this working:

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();

Upvotes: 1

Marc Gravell
Marc Gravell

Reputation: 1063884

Most data-binding already supports dynamic data models, but not dynamic data models. By which I mean: there are existing mechanisms for this, including ICustomTypeDescriptor, (perhaps via TypeDescriptionProvider), ITypedList, and PropertyDescriptor. This is the mechanism used by DataTable and similar, so is well exercised. Since you are binding to a list, ITypedList is probably the most appropriate interface to implement, which means you just need to write a custom PropertyDescriptor that maps between your dictionary API and the descriptor API.

Here's a winforms example:

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;
        }
    }
}

Upvotes: 3

Related Questions