-1

I want to generate a datagrid dynamically using mvvm. In each cell of the datagrid i have to display an object. Column name is one of the properties of the object. Itemsource of the datagrid will be list of object . How to generate the datagrid dynamically using mvvm?

Update

I have created a custom class which has been extended usiing ICustomTypeDescriptor.

    public class MyCustomType : ICustomTypeDescriptor
    {
    // This is instance data.
    private readonly BindingList<PropertyDescriptor> _propertyDescriptors = new BindingList<PropertyDescriptor>();

    // The data is stored on the type instance.
    private readonly IDictionary<string, object> _propertyValues = new Dictionary<string, object>();

    // The property descriptor now takes an extra argument.
    public void AddProperty(string name, Type type)
    {
        _propertyDescriptors.Add(new MyPropertyDescriptor(name, type));
    }

    public PropertyDescriptorCollection GetProperties()
    {
        return new PropertyDescriptorCollection(_propertyDescriptors.ToArray());
    }

    public PropertyDescriptorCollection GetProperties(Type type)
    {
        return new PropertyDescriptorCollection(_propertyDescriptors.ToArray());
    }

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return GetProperties();
    }

    public object GetPropertyOwner(PropertyDescriptor pd)
    {
        throw new NotImplementedException();
    }

    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()
    {
        return null;
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes)
    {
        return null;
    }

    private class MyPropertyDescriptor : PropertyDescriptor
    {
        // This data is here to indicate that different instances of the type
        // object may have properties of the same name, but with different
        // characteristics.
        private readonly Type _type;

        public MyPropertyDescriptor(string name, Type type)
            : base(name, null)
        {
            _type = type;
        }

        public override bool CanResetValue(object component)
        {
            throw new NotImplementedException();
        }

        public override Type ComponentType
        {
            get { throw new NotImplementedException(); }
        }

        public override object GetValue(object component)
        {
            MyCustomType obj = (MyCustomType) component;
            object value = null;
            obj._propertyValues.TryGetValue(Name, out value);
            return value;
        }

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

        public override Type PropertyType
        {
            get { return _type; }
        }

        public override void ResetValue(object component)
        {
            throw new NotImplementedException();
        }

        public override void SetValue(object component, object value)
        {
            var oldValue = GetValue(component);

            if (oldValue != value)
            {
                MyCustomType obj = (MyCustomType) component;
                obj._propertyValues[Name] = value;
                OnValueChanged(component, new PropertyChangedEventArgs(Name));
            }
        }

        public override bool ShouldSerializeValue(object component)
        {
            throw new NotImplementedException();
        }

        public override void AddValueChanged(object component, EventHandler handler)
        {
            // set a breakpoint here to see WPF attaching a value changed handler
            base.AddValueChanged(component, handler);
        }
    }
}

I m binding a list of this customtype objects to datagrid itemsource. But datagrid is not showing any content?

Rony
  • 1
  • 2

1 Answers1

0

The way of using the DataGrid is to have an IEnumerable<T> as its ItemsSource where T is a class or interface that has the properties, which you would like to generate your columns for. You should use the types DataGrid supports. Most of the value types are supported. You can find a list HERE about the supported types as well as the usage.

So your list should contain the objects for the rows, not for the cells. The cells will be automatically generated by DataGrid for each public property of the type of the generic argument of the list.

You can then use MVVM to bind the list as follows:

<DataGrid ItemsSource="{Binding MyList}" AutoGenerateColumns="True"></DataGrid>

Certainly the DataContext of the DataGrid (either inherited or explicit) should contain an public IEnumerable<T> MyList { get; } with the items.

UPDATE

Actually each row object in turn has list of objects for the cells. Each cell object has multiple properties. I want to display 1 of the property value as column name and each cell object will have different cell style based on the type of the object. How to dynamically create the datagrid with theses many conditions?

In WPF the DataGrid has the same column set for every row. So, in your case even you could define different column data for every cell, you can use only the first row (or the one you need) to define the columns. I would rather suggest creating Custom Attributes in order to declare the column properties (data type, header..etc), since it is a declarative information not a dynamic (should not change from row to row or form time to time). The style information still can have some dynamism by using triggers as described in THIS thread. This is more of a WPF way.

Anyway, if you want to stick to your schema, you should implement the column exploration logic in a class derived from DataGrid. In the ItemsSourceChanged handler you check the type of the ItemsSource. If that is an IEnumerable you get out the generic argument (T) with reflection and check if that type supports your column describing schema. Something like:

protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
    base.OnItemsSourceChanged(oldValue, newValue);

    if (AutoGenerateColumns && newValue != null && newValue.GetType().IsGenericType)
    {
        this.Columns.Clear();

        var type = newValue.GetType().GetGenericArguments().First();

        if (typeof(IMyColumnDescriptionStructure).IsAssignableFrom(type))
        {
            var rows = newValue as IEnumerable<IMyColumnDescriptionStructure>;
            var firstRow = rows.First() as IMyColumnDescriptionStructure;

            // TODO: explore your structure and add column definitions
            ///this.Columns.Add(...)
        }
    }
}
Community
  • 1
  • 1
Daniel Leiszen
  • 1,827
  • 20
  • 39
  • Thanks Daniel, Actually each row object in turn has list of objects for the cells. Each cell object has multiple properties. I want to display 1 of the property value as column name and each cell object will have different cell style based on the type of the object. How to dynamically create the datagrid with theses many conditions? – Rony Nov 23 '16 at 12:29