绑定到WinForms DataGridView的通用ITypedList:所有行显示第一个项目

时间:2016-09-09 09:47:02

标签: c# winforms datagridview

我有一个包含动态属性的项目列表。

目标是让类具有已定义的属性,但也允许动态地为其实例设置新属性。一个示例是下面代码中的“MyDynamicObject”类。

然后将列表(下面代码中的'DynamicITypedList'实例)绑定到DataGridView,它将显示列表项的每个属性并将其绑定到列。

我面临的问题是:只要我使'DynamicITypedList'类实现'ITypedList',DataGridView中的所有行都会显示列表中第一项的属性!
如果我不这样做使列表类实现'ITypedList'(在下面的代码的最后注释掉'ITypedList'),不显示动态添加的属性值(因为没有调用特定的GetItemProperties()),但所有行都显示在DataGridView ......

请你帮我解决一下?

这是调用代码,准备在包含一个“datagridView1”控件的WinForm中运行:

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

namespace Tests
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void buttonITypedList_Click(object sender, EventArgs e)
        {
            MyDynamicObject myDynamicObject1 = new MyDynamicObject();
            myDynamicObject1.Id = 1;
            myDynamicObject1.AsDynamic.NewProperty = "NEWPROP1";
            MyDynamicObject myDynamicObject2 = new MyDynamicObject();
            myDynamicObject2.Id = 2;
            DynamicITypedList<MyDynamicObject> myDynamicObjectDynamicITypedList = new DynamicITypedList<MyDynamicObject>();
            myDynamicObjectDynamicITypedList.Add(myDynamicObject1);
            myDynamicObjectDynamicITypedList.Add(myDynamicObject2);
            dataGridView1.AutoGenerateColumns = false;
            BindingSource bindingSource = new BindingSource();
            bindingSource.DataSource = myDynamicObjectDynamicITypedList;
            bindingSource.AllowNew = true;
            dataGridView1.DataSource = bindingSource;
            foreach (KeyValuePair<string, object> property in myDynamicObject1.Data.Properties)
            {
                DataGridViewTextBoxColumn dynamicPropertyColumn = new DataGridViewTextBoxColumn();
                dynamicPropertyColumn.DataPropertyName = property.Key;
                dynamicPropertyColumn.HeaderText = property.Key;
                dynamicPropertyColumn.Name = property.Key;
                dataGridView1.Columns.Add(dynamicPropertyColumn);
            }
        }
    }
}

以下是课程:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Dynamic;
using System.Linq;
using System.Reflection;

namespace Tests
{
    public interface IMyDynamic
    {
        MyDynamic Data { get; set; }
        IEnumerable<string> GetDynamicMemberNames();
    }

    public class MyDynamic : DynamicObject, IDynamicMetaObjectProvider
    {
        object Instance { get; set; }
        Type InstanceType { get; set; }
        Dictionary<string, MemberInfo> MemberInfos { get; set; }
        PropertyInfo[] instancePropertyInfo;

        PropertyInfo[] InstancePropertyInfo
        {
            get
            {
                if (instancePropertyInfo == null && Instance != null)
                    instancePropertyInfo = Instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
                return instancePropertyInfo;
            }
        }

        public MyDynamic()
        {
            Initialize(this);
        }

        public MyDynamic(object instance)
        {
            Initialize(instance);
        }

        public IEnumerable<KeyValuePair<string, object>> GetProperties(bool includeInstanceProperties = false)
        {
            if (includeInstanceProperties && Instance != null)
            {
                foreach (var prop in this.InstancePropertyInfo)
                    yield return new KeyValuePair<string, object>(prop.Name, prop.GetValue(Instance, null));
            }

            foreach (var key in this.Properties.Keys)
                yield return new KeyValuePair<string, object>(key, this.Properties[key]);

        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            foreach (var prop in GetProperties(true))
                yield return prop.Key;
        }

        public Dictionary<string, object> Properties { get; private set; }

        public object this[string key]
        {
            get
            {
                try
                {
                    //Try to get from properties collection first
                    return Properties[key];
                }
                catch (KeyNotFoundException)
                {
                    //Try reflection on instanceType
                    object result = null;
                    if (GetProperty(Instance, key, out result))
                        return result;
                    //Nope doesn't exist
                    //throw;  //Preserve the stack trace
                    return null;
                }
            }
            set
            {
                if (Properties.ContainsKey(key))
                {
                    Properties[key] = value;
                    return;
                }
                //Check instance for existance of type first
                var miArray = InstanceType.GetMember(key, BindingFlags.Public | BindingFlags.GetProperty);
                if (miArray != null && miArray.Length > 0)
                    SetProperty(Instance, key, value);
                else
                    Properties[key] = value;
            }
        }

        protected virtual void Initialize(object instance)
        {
            Instance = instance;
            if (instance != null)
                InstanceType = instance.GetType();
            Properties = new Dictionary<string, object>();
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = null;
            //First check the Properties collection for member
            if (Properties.Keys.Contains(binder.Name))
            {
                result = Properties[binder.Name];
                return true;
            }
            //Next check for Public properties via Reflection
            if (Instance != null)
            {
                try
                {
                    return GetProperty(Instance, binder.Name, out result);
                }
                catch { }
            }
            //Failed to retrieve a property
            result = null;
            return false;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            //First check to see if there's a native property to set
            if (Instance != null)
            {
                try
                {
                    if (SetProperty(Instance, binder.Name, value))
                        return true;
                }
                catch { }
            }
            //No match - set or add to dictionary
            Properties[binder.Name] = value;
            return true;
        }

        protected bool GetProperty(object instance, string name, out object result)
        {
            if (instance == null)
                instance = this;
            if (MemberInfos == null)
            {
                MemberInfos = new Dictionary<string, MemberInfo>();
                MemberInfo[] memberInfos = InstanceType.GetMember(name, BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance);
                foreach (MemberInfo instanceMemberInfo in InstanceType.GetMembers())
                    MemberInfos.Add(instanceMemberInfo.Name, instanceMemberInfo);
            }
            result = null;
            MemberInfo propertyMemberInfo;
            if (!MemberInfos.TryGetValue(name, out propertyMemberInfo))
                return false;
            if (propertyMemberInfo.MemberType == MemberTypes.Property)
            {
                result = ((PropertyInfo)propertyMemberInfo).GetValue(instance, null);
                return true;
            }
            return false;
        }

        protected bool SetProperty(object instance, string name, object value)
        {
            if (instance == null)
                instance = this;
            if (MemberInfos == null)
            {
                MemberInfos = new Dictionary<string, MemberInfo>();
                MemberInfo[] memberInfos = InstanceType.GetMember(name, BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance);
                foreach (MemberInfo instanceMemberInfo in InstanceType.GetMembers())
                    MemberInfos.Add(instanceMemberInfo.Name, instanceMemberInfo);
            }
            MemberInfo propertyMemberInfo;
            if (!MemberInfos.TryGetValue(name, out propertyMemberInfo))
                return false;
            if (propertyMemberInfo.MemberType == MemberTypes.Property)
            {
                ((PropertyInfo)propertyMemberInfo).SetValue(Instance, value, null);
                return true;
            }
            return false;
        }
    }

    public /*abstract*/ class MyDynamicObject : MyDynamic, IMyDynamic
    {
        public MyDynamic Data
        {
            get { return data; }
            set { data = dynamicData = value; }
        }

        public dynamic AsDynamic
        {
            get { return dynamicData; }
        }

        public Int64 Id { get { return AsDynamic.Id; } set { AsDynamic.Id = value; } }

        MyDynamic data;
        dynamic dynamicData;

        public MyDynamicObject() : this(new MyDynamic())
        { }

        public MyDynamicObject(MyDynamic data)
        {
            Data = data;
            TypeDescriptor.AddProvider(new MyDynamicTypeDescriptionProvider(), this); //Data);
        }
    }

    public class IMyDynamicTypeDescriptor : ICustomTypeDescriptor
    {
        private readonly IMyDynamic m_Instance;

        public IMyDynamicTypeDescriptor(object instance)
        {
            if (instance is MyDynamicObject)
                m_Instance = (MyDynamicObject)instance;
            else if (instance is IMyDynamic)
                m_Instance = (IMyDynamic)instance;
        }

        public string GetComponentName()
        {
            return TypeDescriptor.GetComponentName(this, true);
        }

        public EventDescriptor GetDefaultEvent()
        {
            return TypeDescriptor.GetDefaultEvent(this, true);
        }

        public string GetClassName()
        {
            return TypeDescriptor.GetClassName(this, true);
        }

        public EventDescriptorCollection GetEvents(Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes, true);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
        {
            return TypeDescriptor.GetEvents(this, true);
        }

        public TypeConverter GetConverter()
        {
            return TypeDescriptor.GetConverter(this, true);
        }

        public object GetPropertyOwner(PropertyDescriptor pd)
        {
            return m_Instance;
        }

        public AttributeCollection GetAttributes()
        {
            return TypeDescriptor.GetAttributes(this, true);
        }

        public object GetEditor(Type editorBaseType)
        {
            return TypeDescriptor.GetEditor(this, editorBaseType, true);
        }

        public PropertyDescriptor GetDefaultProperty()
        {
            return null;
        }

        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
        {
            return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]);
        }

        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            return new PropertyDescriptorCollection(
                m_Instance.Data.Properties.Keys
                          .Select(x => new MyDynamicPropertyDescriptor(m_Instance, x))
                          .ToArray<PropertyDescriptor>());
        }

    }

    public class MyDynamicPropertyDescriptor : PropertyDescriptor
    {
        private readonly IMyDynamic m_Instance;
        private readonly string m_Name;

        public MyDynamicPropertyDescriptor(IMyDynamic instance, string name)
            : base(name, null)
        {
            m_Instance = instance;
            m_Name = name;
        }

        public override Type PropertyType
        {
            get
            {
                if (m_Instance.Data[m_Name] != null)
                    return m_Instance.Data[m_Name].GetType();
                else
                {
                    //...
                    return typeof(string);
                }
            }
        }

        public override void SetValue(object component, object value)
        {
            m_Instance.Data[m_Name] = value;
        }

        public override object GetValue(object component)
        {
            return m_Instance.Data[m_Name];
        }

        public override bool IsReadOnly
        {
            get { return false; }
        }

        public override Type ComponentType
        {
            get { return null; }
        }

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

        public override void ResetValue(object component)
        { }

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

        public override string Category
        {
            get { return string.Empty; }
        }

        public override string Description
        {
            get { return string.Empty; }
        }
    }

    public class MyDynamicTypeDescriptionProvider : TypeDescriptionProvider
    {
        private static readonly TypeDescriptionProvider m_Default = TypeDescriptor.GetProvider(typeof(ExpandoObject));

        public MyDynamicTypeDescriptionProvider() : base(m_Default)
        { }

        public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
        {
            var defaultDescriptor = base.GetTypeDescriptor(objectType, instance);
            return (instance == null ? defaultDescriptor : new IMyDynamicTypeDescriptor(instance));
        }
    }

    public class DynamicITypedList<T> : ObservableCollection<T>, /*List<T>, IList<T>,*/ ITypedList where T : IMyDynamic, new()
    {
        public string GetListName(PropertyDescriptor[] listAccessors)
        {
            return null;
        }

        public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
        {
            return TypeDescriptor.GetProperties(this.FirstOrDefault());
        }
    }
}

1 个答案:

答案 0 :(得分:-1)

这不是直接回答问题的答案,而是......

前几天我完成了同样的任务。将动态对象绑定到数据网格。

我的解决方案是:

  • 手动将列添加到数据网格
  • 手动设置数据绑定属性
  • 使用BindingSource,将此BindingSource绑定到列表然后将网格绑定到bindingsource

请务必按照正确的顺序执行所有操作,因为如果您不这样做,您的动态属性对于grid / bindingsource是未知的,因此无法显示。

我不记得所有这些的确切顺序,也许你应该尝试一下我的提示并回来再询问一下?