RadGrid for ASP.NET

Tips when binding to custom collections Send comments on this topic.
Populating the control with data > Tips when binding to custom collections

Glossary Item Box

AdditionalDataFieldNames/RetrieveAllDataFields

By default Telerik RadGrid will fetch all bindable property values from the data source you use for control structure generation. They will be further used in sorting, filtering, grouping or other operations. In some cases, however, you may not need your grid instance to iterate through all the fields in your data source upon binding. Generally, this can be a requirement when you have custom collection of business objects which you pass to the DataSource property of Telerik RadGrid to generate its content.

To prevent the grid from traversing all data fields in that collection you just have to set the property RadGridInstance.MasterTableView.RetrieveAllDataFields to false. In this case, the additional fields that you might use when sorting, grouping or used in DataKeyNames should be included in the AdditionalDataFieldNames array. With the settings specified in this paragraph, only the properties that are used as column DataFields or those specified in the AdditionalDataFieldNames will be extracted.

Alternative binding to custom collections

Currently Telerik RadGrid supports sorting, filtering and grouping natively. This is represented by three collections of corresponding expressions:

GridTableView.SortExpressions
GridTableView.GroupByExpressions
GridTableView.FilterExpression

All these features are not tightly coupled with the column declarations of the grid but with the data that the grid is bound to. 

To clarify the idea, let us consider this scenario:
For example, you have a grid instance on webform with 2 columns - ProductName and ProductCategory. In the DataSource, you have these fields: ProductID, Units, TotalOrders, TotalCost, etc. Imagine that this is a table (or business object collection) describing orders of products.

Then you can display the grid with two columns and a group by expression: ProductCategory, ProductID, Sum(TotalCost), Sum(TotalOrders), Group By ProductID, ProductCategory. It is obvious that columns declaration and grouping, sorting, etc. can have different meanings. That is why, by default Telerik RadGrid processes all bindable properties of your business object collection.

When it comes to designing the business objects collections, Telerik RadGrid would be smart enough to comply with .NET standards when binding to ITypedList, IListSource, IEnumerable and so on.

The example web page below has 2 RadGrid and 2 GridView controls, binding to an object data source, with corresponding auto-generated or pre-defined column sets. The business object implements ICustomTypeDescriptor with modified presentation structure using attributes.

The properties that are not visible for presentation have the [PresentationHidden] attribute. Here is the code-behind:

BusinesObject

C# Copy Code
using System;
using System.Web;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web.UI;

using BusinessObjects;

public class BusinessObjectList : BindingList<BusinessObject>, ITypedList
{
   
public BusinessObjectList()
{
       
this.Add( new BusinessObject(null, null, new SubObject("Four")));
       
this.Add( new BusinessObject(1, "1", new SubObject("One")));
       
this.Add( new BusinessObject(2, "3", new SubObject("Two")));
       
this.Add( new BusinessObject(3, "2", new SubObject("Three")));
       
this.Add( new BusinessObject(4, "1", new SubObject("Four")));
}

   
public BindingList<BusinessObject> GetList()
   {
        
return this;
   }

#region ITypedList Members

public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
        
return CustomTypeDescriptorHelper.GetExtendedProperties( typeof(BusinessObject));
}

public string GetListName(PropertyDescriptor[] listAccessors)
{
 
return "DefaultView";
}

#endregion
}

public class SubObject
{
private string _name;

   
public SubObject(string name)
{
       
this._name = name;
}

public string Name
{
        get {
return _name; }
        set { _name = value; }
}
}

public class BusinessObject: ICustomTypeDescriptor
{
private long? _objectId;
private string _stringProperty;
   
private SubObject _subObject;

   
public BusinessObject(int? objectId, string stringProperty, SubObject subObject)
{
       
this._objectId = objectId;
       
this._stringProperty = stringProperty;
 
this._subObject = subObject;
}

public long? ObjectId
{
        get {
return _objectId; }
        set { _objectId = value; }
}

   
public string StringProperty
{
        get {
return _stringProperty; }
        set { _stringProperty = value; }
}

   
//The PresentationHidden attribute would "hide" the property from GridView and RadGrid
   
[PresentationHidden]
   
public string ShouldNotBeAccesseed
   {
        get
       {
           
throw new NotSupportedException();
       }
        set
       {
           
throw new NotSupportedException();
       }
   }

//using the IntroduceObject attribute the custom type descriptor helper would
//recognize the objects to introduce sub-properties
[IntroduceObject]
public SubObject SubObjectProperty
{
        get {
return _subObject; }
        set { _subObject = value; }
}

//-----------------------------------------------------------------------------------------
//This implementation of ICustomTypeDescriptor is general for all types of objects
//that should serve as data-items in a data-list controls including RadGrid, DataGrid,
//GridView, DataList, etc.
//Note that the columns should be defined for the propertes of sub objects - no
//auto-generated columns can be used, unless the collection oif object bound to the data-list
//control implements the ITypedList intereface.
//The DataField of such columns showing sub-properties should be constructed
//from the name of the property + _ + name of the sub-property:
//if sub-property is accessed using "SubProperty.Name" the DataField should be "SubProperty_Name".
//Copy-paste the implementation to other business objects or implement in their base class.
//Do not forget to mark the object, which contain subproperties as [IntroduceObject].
//-----------------------------------------------------------------------------------------
#region ICustomTypeDescriptor Members

System.ComponentModel.AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
 
return new System.ComponentModel.AttributeCollection(null);
}

string ICustomTypeDescriptor.GetClassName()
{
 
return null;
}

string ICustomTypeDescriptor.GetComponentName()
{
 
return null;
}

TypeConverter ICustomTypeDescriptor.GetConverter()
{
 
return null;
}

EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
 
return null;
}

PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
 
return null;
}

object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
 
return null;
}

EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
 
return new EventDescriptorCollection(null);
}

EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
 
return new EventDescriptorCollection(null);
}

PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
 
return ((ICustomTypeDescriptor)this).GetProperties(null);
}

PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
 
return CustomTypeDescriptorHelper.GetExtendedProperties( TypeDescriptor.GetProperties(this, true) );
}

object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
 
return this;
}

#endregion
}
VB.NET Copy Code
Imports System
Imports System.ComponentModel
Imports System.Web.UI



Namespace BusinessObjects


   Friend Class IntroduceObjectAttribute
      Inherits Attribute
   End Class 'IntroduceObjectAttribute


   Friend Class PresentationHidden
      Inherits Attribute
   End Class 'PresentationHidden


   Friend Class CustomTypeDescriptorHelper

      Overloads Public Shared Function GetExtendedProperties(originalProperties As PropertyDescriptorCollection) As PropertyDescriptorCollection
         Dim originalArray(originalProperties.Count) As PropertyDescriptor
         originalProperties.CopyTo(originalArray, 0)
         Dim res As New PropertyDescriptorCollection(originalArray, False)

         Dim aggObjectDescriptor As PropertyDescriptor
         For Each aggObjectDescriptor In originalProperties
            If Not (aggObjectDescriptor.Attributes(GetType(IntroduceObjectAttribute)) Is Nothing) Then
               Try
                  'This can be a recursive method to extract property values even deeper.
                  Dim subObjectType As Type = aggObjectDescriptor.PropertyType
                  Dim desc As PropertyDescriptor
                  For Each desc In TypeDescriptor.GetProperties(subObjectType)
                     If Telerik.WebControls.RadGrid.IsBindableType(desc.PropertyType) Then
                        Dim descriptor As New AggregatedObjectPropertyDescriptor(aggObjectDescriptor.Name, desc)
                        res.Add(descriptor)
                     End If
                  Next desc
               Catch Else
               End Try
            'Is object's getter throws, just ignore
            ElseIf Not (aggObjectDescriptor.Attributes(GetType(PresentationHidden)) Is Nothing) Then
               res.Remove(aggObjectDescriptor)
            End If
         Next aggObjectDescriptor

         Return res
      End Function 'GetExtendedProperties


      Overloads Public Shared Function GetExtendedProperties(sourceType As Type) As PropertyDescriptorCollection
         Dim originalProperties As PropertyDescriptorCollection = TypeDescriptor.GetProperties(sourceType)
         Return GetExtendedProperties(originalProperties)
      End Function 'GetExtendedProperties
   End Class 'CustomTypeDescriptorHelper


   Friend Class AggregatedObjectPropertyDescriptor
      Inherits PropertyDescriptor
      Private parentPropertyName As String
      Private subPropertyDesc As PropertyDescriptor

      Private Const SubPropertySplitter As String = "_"


      Friend Sub New(parentPropertyName As String, subPropertyDesc As PropertyDescriptor)
         MyBase.New(parentPropertyName + SubPropertySplitter + subPropertyDesc.Name, CType(Nothing, Attribute()))
         Me.subPropertyDesc = subPropertyDesc
         Me.parentPropertyName = parentPropertyName
      End Sub 'New


      Public Overrides Function CanResetValue(component As Object) As Boolean
         Throw New Exception("The method or operation is not implemented.")
      End Function 'CanResetValue


      Public Overrides ReadOnly Property ComponentType() As Type
         Get
            Throw New Exception("The method or operation is not implemented.")
         End Get
      End Property

      Public Overrides Function GetValue(component As Object) As Object
         Return subPropertyDesc.GetValue(DataBinder.GetPropertyValue(component, parentPropertyName))
      End Function 'GetValue


      Public Overrides ReadOnly Property IsReadOnly() As Boolean
         Get
            Return subPropertyDesc.IsReadOnly
         End Get
      End Property


      Public Overrides ReadOnly Property PropertyType() As Type
         Get
            Return subPropertyDesc.PropertyType
         End Get
      End Property


      Public Overrides Sub ResetValue(component As Object)
         Throw New Exception("The method or operation is not implemented.")
      End Sub 'ResetValue


      Public Overrides Sub SetValue(component As Object, value As Object)
         subPropertyDesc.SetValue(DataBinder.GetPropertyValue(component, parentPropertyName), value)
      End Sub 'SetValue


      Public Overrides Function ShouldSerializeValue(component As Object) As Boolean
         Throw New Exception("The method or operation is not implemented.")
      End Function 'ShouldSerializeValue
   End Class 'AggregatedObjectPropertyDescriptor
End Namespace 'BusinessObjects

 

CustomTypeDescriptorHelper

C# Copy Code
using System;
using System.ComponentModel;
using System.Web.UI;


namespace BusinessObjects
{

internal class IntroduceObjectAttribute : Attribute
{
}

   
internal class PresentationHidden : Attribute
   {
   }

internal class CustomTypeDescriptorHelper
{
 
public static PropertyDescriptorCollection GetExtendedProperties(PropertyDescriptorCollection originalProperties)
 {
  PropertyDescriptor[] originalArray =
new PropertyDescriptor[originalProperties.Count];
  originalProperties.CopyTo(originalArray, 0);
  PropertyDescriptorCollection res =
new PropertyDescriptorCollection(originalArray, false);

  
foreach (PropertyDescriptor aggObjectDescriptor in originalProperties)
  {
   
if (aggObjectDescriptor.Attributes[typeof(IntroduceObjectAttribute)] != null)
   {
    
try
    {
                       
//This can be a recursive method to extract property values even deeper.
     
Type subObjectType = aggObjectDescriptor.PropertyType;
     
foreach (PropertyDescriptor desc in TypeDescriptor.GetProperties(subObjectType))
     {
       
if (Telerik.WebControls.RadGrid.IsBindableType(desc.PropertyType))
      {
       AggregatedObjectPropertyDescriptor descriptor =
new AggregatedObjectPropertyDescriptor(aggObjectDescriptor.Name, desc);
       res.Add(descriptor);
      }
     }
    }
    
catch
    {
     
//Is object's getter throws, just ignore
    }
   }
                
else
                    
if (aggObjectDescriptor.Attributes[typeof(PresentationHidden)] != null)
               {
                   res.Remove(aggObjectDescriptor);
               }
  }

  
return res;
 }

 
public static PropertyDescriptorCollection GetExtendedProperties(Type sourceType)
 {
  PropertyDescriptorCollection originalProperties = TypeDescriptor.GetProperties(sourceType);
  
return GetExtendedProperties(originalProperties);
 }
}

internal class AggregatedObjectPropertyDescriptor : PropertyDescriptor
{
 
private string parentPropertyName;
 
private PropertyDescriptor subPropertyDesc;

        
private const string SubPropertySplitter = "_";

 
internal AggregatedObjectPropertyDescriptor( string parentPropertyName, PropertyDescriptor subPropertyDesc)
           :
base(parentPropertyName + SubPropertySplitter + subPropertyDesc.Name, (Attribute[])null)
 {
  
this.subPropertyDesc = subPropertyDesc;
  
this.parentPropertyName = parentPropertyName;
 }

 
public override bool CanResetValue(object component)
 {
  
throw new Exception("The method or operation is not implemented.");
 }

 
public override Type ComponentType
 {
  get {
throw new Exception("The method or operation is not implemented."); }
 }

 
public override object GetValue(object component)
 {
  
return subPropertyDesc.GetValue(DataBinder.GetPropertyValue(component, parentPropertyName));
 }

 
public override bool IsReadOnly
 {
  get
  {
   
return subPropertyDesc.IsReadOnly;
  }
 }

 
public override Type PropertyType
 {
  get
  {
   
return subPropertyDesc.PropertyType;
  }
 }

 
public override void ResetValue(object component)
 {
  
throw new Exception("The method or operation is not implemented.");
 }

 
public override void SetValue(object component, object value)
 {
  subPropertyDesc.SetValue(DataBinder.GetPropertyValue(component, parentPropertyName), value);
 }

 
public override bool ShouldSerializeValue(object component)
 {
  
throw new Exception("The method or operation is not implemented.");
 }
}
}

 

VB.NET Copy Code
Imports System
Imports System.ComponentModel
Imports System.Web.UI



Namespace BusinessObjects
    _

   Friend Class IntroduceObjectAttribute
      Inherits Attribute
   End Class 'IntroduceObjectAttribute
    _

   Friend Class PresentationHidden
      Inherits Attribute
   End Class 'PresentationHidden
    _

   Friend Class CustomTypeDescriptorHelper

      Overloads Public Shared Function GetExtendedProperties(originalProperties As PropertyDescriptorCollection) As PropertyDescriptorCollection
          Dim originalArray(originalProperties.Count) As PropertyDescriptor
         originalProperties.CopyTo(originalArray, 0)
          Dim res As New PropertyDescriptorCollection(originalArray, False)

          Dim aggObjectDescriptor As PropertyDescriptor
          For Each aggObjectDescriptor In originalProperties
             If Not (aggObjectDescriptor.Attributes(GetType(IntroduceObjectAttribute)) Is Nothing) Then
               Try
                   'This can be a recursive method to extract property values even deeper.
                   Dim subObjectType As Type = aggObjectDescriptor.PropertyType
                   Dim desc As PropertyDescriptor
                   For Each desc In TypeDescriptor.GetProperties(subObjectType)
                      If Telerik.WebControls.RadGrid.IsBindableType(desc.PropertyType) Then
                         Dim descriptor As New AggregatedObjectPropertyDescriptor(aggObjectDescriptor.Name, desc)
                        res.Add(descriptor)
                      End If
                   Next desc
               Catch
                End Try
             'Is object's getter throws, just ignore
             Else
                If Not (aggObjectDescriptor.Attributes(GetType(PresentationHidden)) Is Nothing) Then
                  res.Remove(aggObjectDescriptor)
                End If
             End If
          Next aggObjectDescriptor
          Return res
      End Function 'GetExtendedProperties


      Overloads Public Shared Function GetExtendedProperties(sourceType As Type) As PropertyDescriptorCollection
          Dim originalProperties As PropertyDescriptorCollection = TypeDescriptor.GetProperties(sourceType)
          Return GetExtendedProperties(originalProperties)
      End Function 'GetExtendedProperties
   End Class 'CustomTypeDescriptorHelper
    _

   Friend Class AggregatedObjectPropertyDescriptor
      Inherits PropertyDescriptor
      Private parentPropertyName As String
      Private subPropertyDesc As PropertyDescriptor

      Private SubPropertySplitter As String = "_"


      Friend Sub New(parentPropertyName As String, subPropertyDesc As PropertyDescriptor)
         MyBase. New(parentPropertyName + SubPropertySplitter + subPropertyDesc.Name, CType(Nothing, Attribute()))
          Me.subPropertyDesc = subPropertyDesc
          Me.parentPropertyName = parentPropertyName
      End Sub 'New


      Public Overrides Function CanResetValue(component As Object) As Boolean
         Throw New Exception("The method or operation is not implemented.")
      End Function 'CanResetValue


      Public Overrides ReadOnly Property ComponentType() As Type
          Get
            Throw New Exception("The method or operation is not implemented.")
          End Get
      End Property

      Public Overrides Function GetValue(component As Object) As Object
          Return subPropertyDesc.GetValue(DataBinder.GetPropertyValue(component, parentPropertyName))
      End Function 'GetValue


      Public Overrides ReadOnly Property IsReadOnly() As Boolean
          Get
             Return subPropertyDesc.IsReadOnly
          End Get
      End Property


      Public Overrides ReadOnly Property PropertyType() As Type
          Get
             Return subPropertyDesc.PropertyType
          End Get
      End Property


      Public Overrides Sub ResetValue(component As Object)
         Throw New Exception("The method or operation is not implemented.")
      End Sub 'ResetValue


      Public Overrides Sub SetValue(component As Object, value As Object)
         subPropertyDesc.SetValue(DataBinder.GetPropertyValue(component, parentPropertyName), value)
      End Sub 'SetValue


      Public Overrides Function ShouldSerializeValue(component As Object) As Boolean
         Throw New Exception("The method or operation is not implemented.")
      End Function 'ShouldSerializeValue
   End Class 'AggregatedObjectPropertyDescriptor
End Namespace 'BusinessObjects

This implementation also allows both RadGrid and GridView (and other similar) controls to bind to properties of "sub-objects", which appear as properties of the business object, by just specifying the [IntroduceObject] attribute. The implementation of the ICustomTypeDescriptor interface for your business object constructs programmatically a new set of properties for the type, containing the properties of the sub-objects in a "flat" manner. This is similar to what the DataRowView generally does, for instance.
The implementation of ITypedList for the business-objects collection allows RadGrid and GridView to enumerate the properties of the business objects in the collection. This also allows Telerik RadGrid to automatically sort, group-by and filter these extended properties.