none
DataGrid with dynamic columns

    Question

  • Hello,

    i want to automatically populate a DataGrid with dynamic properties in my collection. I've read that the row class must implement ICustomTypeDescriptor and GetProperties.

    But the DataGrid just shows the properties of the Dictionary (Comparer, Count, Keys, Values) as columns instead of the Person properties.

    My Code:

            <DataGrid x:Name="dataGrid" Grid.Row="1" ItemsSource="{Binding}" AutoGenerateColumns="True" SelectionUnit="CellOrRowHeader" IsReadOnly="True"/>
    
    
                People people = new People();
                people.Add(new Person("FirstName", "Joe"));
                dataGrid.DataContext = people;
    
    
        public class People : List<Person>
        {
        }
    
        public class Person : Dictionary<string, string>, INotifyPropertyChanged, ICustomTypeDescriptor
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            public Person(string key, string value)
            {
                this.Add(key, value);
            }
    
            private void NotifyPropertyChanged(string property)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(property));
                }
            }
    
            #region ICustomTypeDescriptor
    
            public AttributeCollection GetAttributes()
            {
                return TypeDescriptor.GetAttributes(this, true);
            }
    
            public string GetClassName()
            {
                return TypeDescriptor.GetClassName(this, true);
            }
    
            public string GetComponentName()
            {
                return null;
            }
    
            public TypeConverter GetConverter()
            {
                return null;
            }
    
            public EventDescriptor GetDefaultEvent()
            {
                return null;
            }
    
            public PropertyDescriptor GetDefaultProperty()
            {
                return null;
            }
    
            public object GetEditor(Type editorBaseType)
            {
                return null;
            }
    
            public EventDescriptorCollection GetEvents(Attribute[] attributes)
            {
                return new EventDescriptorCollection(null);
            }
    
            public EventDescriptorCollection GetEvents()
            {
                return new EventDescriptorCollection(null);
            }
    
            public PropertyDescriptorCollection GetProperties()
            {
                return ((ICustomTypeDescriptor)this).GetProperties(null);
            }
    
            public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
            {
                PropertyDescriptorCollection props = new PropertyDescriptorCollection(null);
                foreach(string key in this.Keys)
                {
                    props.Add(new PersonPropertyDescriptor(key));
                }
                return props;
            }
    
            public object GetPropertyOwner(PropertyDescriptor pd)
            {
                return this;
            }
    
            #endregion ICustomTypeDescriptor
        }
    
        public class PersonPropertyDescriptor : PropertyDescriptor
        {
            public PersonPropertyDescriptor(string name)
                : base(name, null)
            {
            }
    
            public override bool CanResetValue(object component)
            {
                return false;
            }
    
            public override object GetValue(object component)
            {
                return (component as Person)[Name];
            }
    
            public override void ResetValue(object component)
            {
    
            }
    
            public override void SetValue(object component, object value)
            {
                (component as Person)[Name] = value as string;
            }
    
            public override bool ShouldSerializeValue(object component)
            {
                return false;
            }
    
            public override Type ComponentType
            {
                get { return typeof(Person); }
            }
    
            public override bool IsReadOnly
            {
                get { return false; }
            }
    
            public override Type PropertyType
            {
                get
                {
                    return typeof(string);
                }
            }
    
            public override bool Equals(object obj)
            {
                if (obj is PersonPropertyDescriptor)
                {
                    return ((PersonPropertyDescriptor)obj).Name == Name;
                }
                return base.Equals(obj);
            }
    
            public override int GetHashCode()
            {
                return Name.GetHashCode();
            }
        }


    Thursday, May 03, 2012 1:43 PM

Answers

  • Hi freundblase,

    I have uploaded two samples demonstrate you this topic, you could download it, and adapt it to your specific needs:

    http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/c7c2d6fb-e62c-4bb1-9bed-973294bf1f25

    http://code.msdn.microsoft.com/How-to-add-the-Column-into-2ad31c47

    Best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, May 04, 2012 6:33 AM
  • I found a solution. This works but isnt ideal.

    You can add new columns by adding new properties to the DynamicObjects.

    In practice you may want to use existing data classes and just show hide columns by adding/removing PropertyDescriptors like in the DynamicDataGrid-Example.

    <DataGrid x:Name="dataGrid" Grid.Row="1" ItemsSource="{Binding Employees}" AutoGenerateColumns="True" SelectionUnit="CellOrRowHeader" IsReadOnly="True"/>
    
            Employees employees = new Employees();
            public Employees Employees
            {
                get { return employees; }
                set { employees = value; NotifyPropertyChanged("Employees"); }
            }
    
                dynamic employee = new Employee();
                employee.FirstName = "Michael";
                employee.LastName = "Jordan";
                Employees.Add(employee);
                dynamic employee2 = new Employee();
                employee2.FirstName = "Max";
                employee2.LastName = "Headroom";
                Employees.Add(employee2);
                DataContext = this;
    
            private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                foreach (dynamic employee in Employees)
                {
                    employee.Gender = "Male";
                }
    
                //to trigger GetItemProperties()
                var coll = Employees;
                Employees = null;
                Employees = coll;
            }
    
            private void Grid_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
            {
                foreach (dynamic employee in Employees)
                {
                    employee.RemoveProperty("Gender");
                }
                var coll = Employees;
                Employees = null;
                Employees = coll;
            }
    
        public class Employees : ObservableCollection<dynamic>, ITypedList
        {
            #region ITypedList
    
            public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
            {
                ICustomTypeDescriptor emp = this.First();
                return emp.GetProperties();
            }
    
            public string GetListName(PropertyDescriptor[] listAccessors)
            {
                return null;
            }
    
            #endregion ITypedList
        }
    
        public class Employee : DynamicObject, ICustomTypeDescriptor, INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            private void NotifyPropertyChanged(string property)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(property));
                }
            }
    
            #region DynamicObject
    
            public Dictionary<string, object> properties = new Dictionary<string, object>();
    
            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                if (properties.ContainsKey(binder.Name))
                {
                    result = properties[binder.Name];
                    return true;
                }
                else
                {
                    result = "Invalid Property!";
                    return false;
                }
            }
    
            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                properties[binder.Name] = value;
                NotifyPropertyChanged(binder.Name);
                return true;
            }
    
            public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
            {
                dynamic method = properties[binder.Name];
                result = method(args[0].ToString(), args[1].ToString());
                return true;
            }
    
            #endregion DynamicObject
    
            public bool RemoveProperty(string property)
            {
                return properties.Remove(property);
            }
    
            #region ICustomTypeDescriptor
    
            public AttributeCollection GetAttributes()
            {
                throw new NotImplementedException();
            }
    
            public string GetClassName()
            {
                throw new NotImplementedException();
            }
    
            public string GetComponentName()
            {
                throw new NotImplementedException();
            }
    
            public TypeConverter GetConverter()
            {
                throw new NotImplementedException();
            }
    
            public EventDescriptor GetDefaultEvent()
            {
                throw new NotImplementedException();
            }
    
            public PropertyDescriptor GetDefaultProperty()
            {
                throw new NotImplementedException();
            }
    
            public object GetEditor(Type editorBaseType)
            {
                throw new NotImplementedException();
            }
    
            public EventDescriptorCollection GetEvents(Attribute[] attributes)
            {
                throw new NotImplementedException();
            }
    
            public EventDescriptorCollection GetEvents()
            {
                throw new NotImplementedException();
            }
    
            public PropertyDescriptorCollection GetProperties()
            {
                return ((ICustomTypeDescriptor)this).GetProperties(null);
            }
    
            public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
            {
                PropertyDescriptorCollection props = new PropertyDescriptorCollection(null);
                foreach (KeyValuePair<string, object> property in properties)
                {
                    props.Add(new DynamicPropertyDescriptor(property.Key));
                }
                return props;
            }
    
            public object GetPropertyOwner(PropertyDescriptor pd)
            {
                throw new NotImplementedException();
            }
    
            #endregion ICustomTypeDescriptor
        }
    
        public class DynamicPropertyDescriptor : PropertyDescriptor
        {
            public DynamicPropertyDescriptor(string name)
                : base(name, null)
            {
            }
    
            public override bool CanResetValue(object component)
            {
                return false;
            }
    
            public override object GetValue(object component)
            {
                return Impromptu.InvokeGet(component, Name);
            }
    
            public override void ResetValue(object component)
            {
    
            }
    
            public override void SetValue(object component, object value)
            {
                Impromptu.InvokeSet(component, Name, value);
            }
    
            public override bool ShouldSerializeValue(object component)
            {
                return false;
            }
    
            public override Type ComponentType
            {
                get { return typeof(object); }
            }
    
            public override bool IsReadOnly
            {
                get { return false; }
            }
    
            public override Type PropertyType
            {
                get
                {
                    return typeof(object);
                }
            }
        }
    Monday, May 07, 2012 7:53 AM

All replies

  • Hi freundblase,

    I have uploaded two samples demonstrate you this topic, you could download it, and adapt it to your specific needs:

    http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/c7c2d6fb-e62c-4bb1-9bed-973294bf1f25

    http://code.msdn.microsoft.com/How-to-add-the-Column-into-2ad31c47

    Best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, May 04, 2012 6:33 AM
  • I found a solution. This works but isnt ideal.

    You can add new columns by adding new properties to the DynamicObjects.

    In practice you may want to use existing data classes and just show hide columns by adding/removing PropertyDescriptors like in the DynamicDataGrid-Example.

    <DataGrid x:Name="dataGrid" Grid.Row="1" ItemsSource="{Binding Employees}" AutoGenerateColumns="True" SelectionUnit="CellOrRowHeader" IsReadOnly="True"/>
    
            Employees employees = new Employees();
            public Employees Employees
            {
                get { return employees; }
                set { employees = value; NotifyPropertyChanged("Employees"); }
            }
    
                dynamic employee = new Employee();
                employee.FirstName = "Michael";
                employee.LastName = "Jordan";
                Employees.Add(employee);
                dynamic employee2 = new Employee();
                employee2.FirstName = "Max";
                employee2.LastName = "Headroom";
                Employees.Add(employee2);
                DataContext = this;
    
            private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                foreach (dynamic employee in Employees)
                {
                    employee.Gender = "Male";
                }
    
                //to trigger GetItemProperties()
                var coll = Employees;
                Employees = null;
                Employees = coll;
            }
    
            private void Grid_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
            {
                foreach (dynamic employee in Employees)
                {
                    employee.RemoveProperty("Gender");
                }
                var coll = Employees;
                Employees = null;
                Employees = coll;
            }
    
        public class Employees : ObservableCollection<dynamic>, ITypedList
        {
            #region ITypedList
    
            public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
            {
                ICustomTypeDescriptor emp = this.First();
                return emp.GetProperties();
            }
    
            public string GetListName(PropertyDescriptor[] listAccessors)
            {
                return null;
            }
    
            #endregion ITypedList
        }
    
        public class Employee : DynamicObject, ICustomTypeDescriptor, INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            private void NotifyPropertyChanged(string property)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(property));
                }
            }
    
            #region DynamicObject
    
            public Dictionary<string, object> properties = new Dictionary<string, object>();
    
            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                if (properties.ContainsKey(binder.Name))
                {
                    result = properties[binder.Name];
                    return true;
                }
                else
                {
                    result = "Invalid Property!";
                    return false;
                }
            }
    
            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                properties[binder.Name] = value;
                NotifyPropertyChanged(binder.Name);
                return true;
            }
    
            public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
            {
                dynamic method = properties[binder.Name];
                result = method(args[0].ToString(), args[1].ToString());
                return true;
            }
    
            #endregion DynamicObject
    
            public bool RemoveProperty(string property)
            {
                return properties.Remove(property);
            }
    
            #region ICustomTypeDescriptor
    
            public AttributeCollection GetAttributes()
            {
                throw new NotImplementedException();
            }
    
            public string GetClassName()
            {
                throw new NotImplementedException();
            }
    
            public string GetComponentName()
            {
                throw new NotImplementedException();
            }
    
            public TypeConverter GetConverter()
            {
                throw new NotImplementedException();
            }
    
            public EventDescriptor GetDefaultEvent()
            {
                throw new NotImplementedException();
            }
    
            public PropertyDescriptor GetDefaultProperty()
            {
                throw new NotImplementedException();
            }
    
            public object GetEditor(Type editorBaseType)
            {
                throw new NotImplementedException();
            }
    
            public EventDescriptorCollection GetEvents(Attribute[] attributes)
            {
                throw new NotImplementedException();
            }
    
            public EventDescriptorCollection GetEvents()
            {
                throw new NotImplementedException();
            }
    
            public PropertyDescriptorCollection GetProperties()
            {
                return ((ICustomTypeDescriptor)this).GetProperties(null);
            }
    
            public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
            {
                PropertyDescriptorCollection props = new PropertyDescriptorCollection(null);
                foreach (KeyValuePair<string, object> property in properties)
                {
                    props.Add(new DynamicPropertyDescriptor(property.Key));
                }
                return props;
            }
    
            public object GetPropertyOwner(PropertyDescriptor pd)
            {
                throw new NotImplementedException();
            }
    
            #endregion ICustomTypeDescriptor
        }
    
        public class DynamicPropertyDescriptor : PropertyDescriptor
        {
            public DynamicPropertyDescriptor(string name)
                : base(name, null)
            {
            }
    
            public override bool CanResetValue(object component)
            {
                return false;
            }
    
            public override object GetValue(object component)
            {
                return Impromptu.InvokeGet(component, Name);
            }
    
            public override void ResetValue(object component)
            {
    
            }
    
            public override void SetValue(object component, object value)
            {
                Impromptu.InvokeSet(component, Name, value);
            }
    
            public override bool ShouldSerializeValue(object component)
            {
                return false;
            }
    
            public override Type ComponentType
            {
                get { return typeof(object); }
            }
    
            public override bool IsReadOnly
            {
                get { return false; }
            }
    
            public override Type PropertyType
            {
                get
                {
                    return typeof(object);
                }
            }
        }
    Monday, May 07, 2012 7:53 AM