Issue with Grid data binding

前端 未结 1 1525
萌比男神i
萌比男神i 2021-01-16 09:47

Our application generate a huge list of result in a specific structure. The problem is that if I want to show it in a DataGrid, I have to create a DataTable and set it as th

1条回答
  •  暗喜
    暗喜 (楼主)
    2021-01-16 10:14

    i think this can be done by using a TypeDescriptionProvider.

    the BAD news is: i have never done this before and won't be able to provide much help

    the GOOD news is: i've found an example here: DataGridView not showing properites of objects which implement ICustomTypeDescriptor

    //edit

    i used the code (see link above) to build an example how to avoid the dictionary per object ...

    public class myRow
    {
        //your data storage class ... 
        public string txt { get; set; }
        public int id { get; set; }
    }
    public class MyView:ICustomTypeDescriptor
    {//your extendable view class ...
        private static PropertyDescriptorCollection props = null;
        static MyView()
        {
            TypeDescriptionProvider defaultProvider = TypeDescriptor.GetProvider(typeof(MyView));
            props = new PropertyDescriptorCollection(defaultProvider.GetTypeDescriptor(typeof(MyView)).GetProperties().Cast().ToArray(), true);
        }
    
        public static void addProperty(string name, DataTable dt, Func getter, Action setter, Func rowSelector, Type PropType)
        {
            List tmp;
            if (props != null) tmp = props.Cast().ToList();
            else tmp = new List();
            PropertyDescriptor pd = TypeDescriptor.CreateProperty(typeof(MyView), name, PropType, null);
            pd = new MyViewPropertyDescriptor(pd, dt, getter, setter, rowSelector, PropType);
            tmp.Add(pd);
            props = new PropertyDescriptorCollection(tmp.ToArray(), true);
        }
    
        //the data storage obj this view is referencing
        public myRow obj;
    
        public string TXT { // view-member known at compile time
            get { return obj.txt; }
            set { obj.txt = value; }
        }
    
        internal class MyViewPropertyDescriptor : PropertyDescriptor
        {   // an example property descriptor that can link to data in a DataTable ... 
            DataTable dt;
            Func getter;
            Action setter;
            Func rowSelector;
            Type type;
            public MyViewPropertyDescriptor(PropertyDescriptor descr, DataTable dt, Func getter, Action setter, Func rowSelector, Type PropType)
                : base(descr)
            {
                this.dt = dt; // storage for additional data referenced by this property
                this.getter = getter; //a getter that will take a DataRow, and extract the property value
                this.setter = setter; //a setter that will take a DataRow and a value
                this.rowSelector = rowSelector;//a row selector ... takes a dataset and the view object and has to return the assiciated datarow
                this.type = PropType; // the type of this property
            }
    
            public override object GetValue(object component)
            {
                // using row selector and getter to return the current value ... you should add errorhandling here
                return getter(rowSelector(dt, (MyView)component));
            }
            public override void SetValue(object component, object value)
            {   // the setter ... needs errorhandling too
                setter(rowSelector(dt, (MyView)component), value);
            }
            public override void ResetValue(object component)
            {
    
            }
            public override bool CanResetValue(object component)
            {
                return false;
            }
            public override bool ShouldSerializeValue(object component)
            {
                return false;
            }
            public override Type PropertyType
            {
                get { return type; }
            }
            public override bool IsReadOnly
            {
                get { return false; }
            }
            public override Type ComponentType
            {
                get { return typeof(MyView); }
            }
    
        }
    
        ICustomTypeDescriptor defaultDescriptor = TypeDescriptor.GetProvider(typeof(MyView)).GetTypeDescriptor(typeof(MyView));
        AttributeCollection ICustomTypeDescriptor.GetAttributes()
        {
            return defaultDescriptor.GetAttributes();
        }
        string ICustomTypeDescriptor.GetClassName()
        {
            return defaultDescriptor.GetClassName();
        }
        string ICustomTypeDescriptor.GetComponentName()
        {
            return defaultDescriptor.GetComponentName();
        }
        TypeConverter ICustomTypeDescriptor.GetConverter()
        {
            return defaultDescriptor.GetConverter();
        }
        EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
        {
            return defaultDescriptor.GetDefaultEvent();
        }
        PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
        {
            return defaultDescriptor.GetDefaultProperty();
        }
        object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
        {
            return defaultDescriptor.GetEditor(editorBaseType);
        }
        EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
        {
            return defaultDescriptor.GetEvents(attributes);
        }
        EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
        {
            return defaultDescriptor.GetEvents();
        }
        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
        {
            return props; // should really be filtered, but meh!
        }
        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
        {
            return props;
        }
        object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
        {
            return this;
        }
    
    }
    

    and a little example that makes use of this ...

    private void button1_Click(object sender, EventArgs e)
    {
        if (dataGridView1.DataSource == null)
        {
            List data = new List();
            data.Add(new myRow { id = 1, txt = "test 1" });
            data.Add(new myRow { id = 2, txt = "test 2" });
            data.Add(new myRow { id = 3, txt = "test 3" });
            DataTable dt = new DataTable();
            dt.Columns.Add("id", typeof(int));
            dt.Columns.Add("additionalData1", typeof(int));
            dt.Columns.Add("additionalData2", typeof(int));
            Random rnd = new Random();
            foreach (var item in data)
            {
                dt.Rows.Add(new object[] { item.id, rnd.Next(), rnd.Next() });
            }
            MyView.addProperty("additionalData1", dt, row => row["additionalData1"], (row, val) => row["additionalData1"] = val, (tab, v) => tab.Rows.OfType().First(x => x["id"].Equals(v.obj.id)), typeof(int));
            MyView.addProperty("additionalData2", dt, row => row["additionalData2"], (row, val) => row["additionalData2"] = val, (tab, v) => tab.Rows.OfType().First(x => x["id"].Equals(v.obj.id)), typeof(int));
    
            dataGridView1.DataSource = new BindingList(data.Select(x => new MyView { obj = x }).ToList());
        }
    }
    

    of course you will want to provide a better rowSelector or replace the DataTable with a Hashtable or whatever datastructure you want ... just an example

    0 讨论(0)
提交回复
热议问题