New to Telerik UI for WinFormsStart a free 30-day trial

Bind to ICustomTypeProvider in .NET Framework 4.8 and .NET

Updated over 6 months ago

The ICustomTypeProvider interface comes with .NET Framework 4.5 and is also available in .NET. As of R3 2023 RadPivotGrid can use it in the NET48 distribution.

Implementing the ICustomTypeProvider interface allows dynamic generation of properties. This solution is applicable for cases when the schema of the data that the pivot will be working with is not known in advance. For example, it may be necessary to generate properties at runtime and show them inside the pivot control.

This tutorial demonstrates how to accomplish such a requirement:

1. Define the Customer class

C#

public class Customer : ICustomTypeProvider
{
    private CustomTypeHelper<Customer> helper = new CustomTypeHelper<Customer>();

    public string FirstName { get; set; }

    public string LastName { get; set; }

    Type ICustomTypeProvider.GetCustomType()
    {
        return this.helper.GetCustomType();
    }

    public static void AddProperty(string name)
    {
        CustomTypeHelper<Customer>.AddProperty(name);
    }

    public static void AddProperty(string name, Type propertyType)
    {
        CustomTypeHelper<Customer>.AddProperty(name, propertyType);
    }

    public static void AddProperty(string name, Type propertyType, List<Attribute> attributes)
    {
        CustomTypeHelper<Customer>.AddProperty(name, propertyType, attributes);
    }

    public void SetPropertyValue(string propertyName, object value)
    {
        this.helper.SetPropertyValue(propertyName, value);
    }

    public object GetPropertyValue(string propertyName)
    {
        return this.helper.GetPropertyValue(propertyName);
    }

    public PropertyInfo[] GetProperties()
    {
        return this.helper.GetProperties();
    }
}

2. Define the CustomTypeHelper class

C#
internal class CustomTypeHelper<T> : ICustomTypeProvider, INotifyPropertyChanged
{
    private static List<CustomPropertyInfoHelper> customProperties = new List<CustomPropertyInfoHelper>();
    private Dictionary<string, object> customPropertyValues;
    private CustomType ctype;

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    public CustomTypeHelper()
    {
        customPropertyValues = new Dictionary<string, object>();
        foreach (var property in this.GetCustomType().GetProperties())
        {
            customPropertyValues.Add(property.Name, null);
        }
    }

    public static void AddProperty(string name)
    {
        if (!CheckIfNameExists(name))
            customProperties.Add(new CustomPropertyInfoHelper(name, typeof(string)));
    }

    public static void AddProperty(string name, Type propertyType)
    {
        if (!CheckIfNameExists(name))
            customProperties.Add(new CustomPropertyInfoHelper(name, propertyType));
    }

    public static void AddProperty(string name, Type propertyType, List<Attribute> attributes)
    {
        if (!CheckIfNameExists(name))
            customProperties.Add(new CustomPropertyInfoHelper(name, propertyType, attributes));
    }

    private static bool CheckIfNameExists(string name)
    {
        if ((from p in customProperties select p.Name).Contains(name) || (from p in typeof(T).GetProperties() select p.Name).Contains(name))
            throw new Exception("The property with this name already exists: " + name);
        else return false;
    }

    public void SetPropertyValue(string propertyName, object value)
    {
        CustomPropertyInfoHelper propertyInfo = (from prop in customProperties where prop.Name == propertyName select prop).FirstOrDefault();
        if (!customPropertyValues.ContainsKey(propertyName))
            customPropertyValues.Add(propertyName, null);
        if (ValidateValueType(value, propertyInfo._type))
        {
            if (customPropertyValues[propertyName] != value)
            {
                customPropertyValues[propertyName] = value;
                NotifyPropertyChanged(propertyName);
            }
        }
        else throw new Exception("Value is of the wrong type or null for a non-nullable type.");
    }

    private bool ValidateValueType(object value, Type type)
    {
        if (value == null || value is System.DBNull)
            // Non-value types can be assigned null.
            if (!type.IsValueType)
                return true;
            else
                // Check if the type if a Nullable type.
                return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
        else
            return type.IsAssignableFrom(value.GetType());
    }

    public object GetPropertyValue(string propertyName)
    {
        if (customPropertyValues.ContainsKey(propertyName))
            return customPropertyValues[propertyName];
        else
            throw new Exception("There is no property " + propertyName);
    }

    public PropertyInfo[] GetProperties()
    {
        return this.GetCustomType().GetProperties();
    }

    public Type GetCustomType()
    {
        if (ctype == null)
        {
            ctype = new CustomType(typeof(T));
        }

        return ctype;
    }

    private class CustomType : Type
    {
        Type _baseType;

        public CustomType(Type delegatingType)
        {
            _baseType = delegatingType;
        }

        public override Assembly Assembly
        {
            get { return _baseType.Assembly; }
        }

        public override string AssemblyQualifiedName
        {
            get { return _baseType.AssemblyQualifiedName; }
        }

        public override Type BaseType
        {
            get { return _baseType.BaseType; }
        }

        public override string FullName
        {
            get { return _baseType.FullName; }
        }

        public override Guid GUID
        {
            get { return _baseType.GUID; }
        }

        protected override TypeAttributes GetAttributeFlagsImpl()
        {
            throw new NotImplementedException();
        }

        protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
        {

            throw new NotImplementedException();
        }

        public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr)
        {
            return _baseType.GetConstructors(bindingAttr);
        }

        public override Type GetElementType()
        {
            return _baseType.GetElementType();
        }

        public override EventInfo GetEvent(string name, BindingFlags bindingAttr)
        {
            return _baseType.GetEvent(name, bindingAttr);
        }

        public override EventInfo[] GetEvents(BindingFlags bindingAttr)
        {
            return _baseType.GetEvents(bindingAttr);
        }

        public override FieldInfo GetField(string name, BindingFlags bindingAttr)
        {
            return _baseType.GetField(name, bindingAttr);
        }

        public override FieldInfo[] GetFields(BindingFlags bindingAttr)
        {
            return _baseType.GetFields(bindingAttr);
        }

        public override Type GetInterface(string name, bool ignoreCase)
        {
            return _baseType.GetInterface(name, ignoreCase);
        }

        public override Type[] GetInterfaces()
        {
            return _baseType.GetInterfaces();
        }

        public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
        {
            return _baseType.GetMembers(bindingAttr);
        }

        protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
        {
            throw new NotImplementedException();
        }

        public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
        {
            return _baseType.GetMethods(bindingAttr);
        }

        public override Type GetNestedType(string name, BindingFlags bindingAttr)
        {
            return _baseType.GetNestedType(name, bindingAttr);
        }

        public override Type[] GetNestedTypes(BindingFlags bindingAttr)
        {
            return _baseType.GetNestedTypes(bindingAttr);
        }

        public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
        {
            PropertyInfo[] clrProperties = _baseType.GetProperties(bindingAttr);
            if (clrProperties != null)
            {
                return clrProperties.Concat(customProperties).ToArray();
            }
            else
                return customProperties.ToArray();
        }

        protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
        {
            // Look for the CLR property with this name first.
            PropertyInfo propertyInfo = (from prop in GetProperties(bindingAttr) where prop.Name == name select prop).FirstOrDefault();
            if (propertyInfo == null)
            {
                // If the CLR property was not found, return a custom property
                propertyInfo = (from prop in customProperties where prop.Name == name select prop).FirstOrDefault();
            }
            return propertyInfo;
        }

        protected override bool HasElementTypeImpl()
        {
            throw new NotImplementedException();
        }

        public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] namedParameters)
        {
            return _baseType.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters);
        }

        protected override bool IsArrayImpl()
        {
            throw new NotImplementedException();
        }

        protected override bool IsByRefImpl()
        {
            throw new NotImplementedException();
        }

        protected override bool IsCOMObjectImpl()
        {
            throw new NotImplementedException();
        }

        protected override bool IsPointerImpl()
        {
            throw new NotImplementedException();
        }

        protected override bool IsPrimitiveImpl()
        {
            return _baseType.IsPrimitive;
        }

        public override Module Module
        {
            get { return _baseType.Module; }
        }

        public override string Namespace
        {
            get { return _baseType.Namespace; }
        }

        public override Type UnderlyingSystemType
        {
            get { return _baseType.UnderlyingSystemType; }
        }

        public override object[] GetCustomAttributes(Type attributeType, bool inherit)
        {
            return _baseType.GetCustomAttributes(attributeType, inherit);
        }

        public override object[] GetCustomAttributes(bool inherit)
        {
            return _baseType.GetCustomAttributes(inherit);
        }

        public override bool IsDefined(Type attributeType, bool inherit)
        {
            return _baseType.IsDefined(attributeType, inherit);
        }

        public override string Name
        {
            get { return _baseType.Name; }
        }
    }

    // Custom implementation of the PropertyInfo
    private class CustomPropertyInfoHelper : PropertyInfo
    {
        public string _name;
        public Type _type;
        public List<Attribute> _attributes = new List<Attribute>();


        public CustomPropertyInfoHelper(string name, Type type)
        {
            _name = name;
            _type = type;
        }

        public CustomPropertyInfoHelper(string name, Type type, List<Attribute> attributes)
        {
            _name = name;
            _type = type;
            _attributes = attributes;
        }

        public override PropertyAttributes Attributes
        {
            get { throw new NotImplementedException(); }
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override MethodInfo[] GetAccessors(bool nonPublic)
        {
            throw new NotImplementedException();
        }

        public override MethodInfo GetGetMethod(bool nonPublic)
        {
            throw new NotImplementedException();
        }

        public override ParameterInfo[] GetIndexParameters()
        {
            throw new NotImplementedException();
        }

        public override MethodInfo GetSetMethod(bool nonPublic)
        {
            throw new NotImplementedException();
        }

        // Returns the value from the dictionary stored in the Customer's instance.
        public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture)
        {

            return obj.GetType().GetMethod("GetPropertyValue").Invoke(obj, new[] { _name });
        }

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

        // Sets the value in the dictionary stored in the Customer's instance.
        public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture)
        {
            obj.GetType().GetMethod("SetPropertyValue").Invoke(obj, new[] { _name, value });
        }

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

        public override object[] GetCustomAttributes(Type attributeType, bool inherit)
        {
            var attrs = from a in _attributes where a.GetType() == attributeType select a;
            return attrs.ToArray();
        }

        public override object[] GetCustomAttributes(bool inherit)
        {
            return _attributes.ToArray();
        }

        public override bool IsDefined(Type attributeType, bool inherit)
        {
            throw new NotImplementedException();
        }

        public override string Name
        {
            get { return _name; }
        }

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

        internal List<Attribute> CustomAttributesInternal
        {
            get { return _attributes; }
        }
    }
}
#endregion

3.Last, we will setup the RadPivotGrid control:

C#

private List<Customer> customers = new List<Customer>();
private LocalDataSourceProvider localDataProvider;

public PivotCustomTypeProvider()
{
    InitializeComponent();

    this.localDataProvider = new LocalDataSourceProvider();
    this.radPivotGrid1.DataProvider = this.localDataProvider;

    this.localDataProvider.RowGroupDescriptions.Add(new PropertyGroupDescription()
    {
        PropertyName = "FirstName"
    });

    this.localDataProvider.ColumnGroupDescriptions.Add(new PropertyGroupDescription()
    {
        PropertyName = "Married"
    });

    this.localDataProvider.ColumnGroupDescriptions.Add(new PropertyGroupDescription()
    {
        PropertyName = "LastName"
    });

    this.localDataProvider.AggregateDescriptions.Add(new PropertyAggregateDescription()
    {
        PropertyName = "Age",
        AggregateFunction = AggregateFunctions.Average
    });

    Customer.AddProperty("Address", typeof(string));
    Customer.AddProperty("Age", typeof(int?));
    Customer.AddProperty("Married", typeof(bool?));
    Customer.AddProperty("Income", typeof(double?));

    Customer customer1 = new Customer { FirstName = "Mary", LastName = "Smith" };
    customer1.SetPropertyValue("Address", "C1 address");
    customer1.SetPropertyValue("Age", 40);
    customer1.SetPropertyValue("Married", true);
    customer1.SetPropertyValue("Income", 100000.0);

    Customer customer2 = new Customer { FirstName = "John", LastName = "Smith" };
    customer2.SetPropertyValue("Address", "C2 address");
    customer2.SetPropertyValue("Age", 45);
    customer2.SetPropertyValue("Married", false);
    customer2.SetPropertyValue("Income", 75000.0);

    this.customers.Add(customer1);
    this.customers.Add(customer2);
    this.localDataProvider.ItemsSource = this.customers;
}

The achieved result is illustrated below:

WinForms RadPivotGrid RadPivot Data Binding

See Also

In this article
See Also
Not finding the help you need?
Contact Support