A. I bound a radGrid control to a custom collection called FactoryList(of Tkey).
B. The FactoryList control derived from BindingList (of Tkey) and implements ITypedList as follow:
1. The GetItemProperties return a collection of ColumnPropertyDescriptor objects, each is derived from PropertyDescriptor class
2. The ColumnPropertyDescriptor class exposes information based on DataColumn meta-data i.e. ColumnName, ColumnType, and also handle SetValue and Get Value methods.
C. A FactoryList instance contains objects that are derived from a base class called BaseEntity. The BaseEntity class implements
ICustomTypeDescriptor, where the GetItemProperties retruns a collection of ColumnPropertyDescriptor same as the FactoryList class does.
D. I use the NeedDataSource event to bind the FactoryList to the radGrid
All works fine, till I sort the grid rows by clicking on one of the grid column headers. In that case I got the following error:
No property or field 'LST_NAME' exists in type 'Object'
- 'LST_NAME' is one the columns I was trying to sort
radGrid:
<
telerik:RadGrid ID="grdCustomLists" runat="server" AllowPaging="True"
AllowSorting="True" AutoGenerateColumns="False" AutoGenerateDeleteColumn="True"
AutoGenerateEditColumn="True" GridLines="None"
DataMember="esmBOA.SystemList">
<
MasterTableView CommandItemDisplay="Top" DataKeyNames="LST_ID" EditMode="InPlace"
DataMember="esmBOA.SystemList">
<CommandItemSettings AddNewRecordText="New List" />
<
RowIndicatorColumn>
<
HeaderStyle Width="20px"></HeaderStyle>
</
RowIndicatorColumn>
<
ExpandCollapseColumn>
<
HeaderStyle Width="20px"></HeaderStyle>
</
ExpandCollapseColumn>
<Columns>
<telerik:GridBoundColumn DataField="LST_ID" HeaderText="LST_ID" ReadOnly="True"
SortExpression="LST_ID" UniqueName="LST_ID" Visible="False"
Display="False">
</telerik:GridBoundColumn>
<telerik:GridBoundColumn DataField="LST_NAME" HeaderText="Name"
SortExpression="LST_NAME" UniqueName="LST_NAME">
</telerik:GridBoundColumn>
<telerik:GridBoundColumn DataField="LST_DSC" HeaderText="Description"
SortExpression="LST_DSC" UniqueName="LST_DSC">
</telerik:GridBoundColumn>
<telerik:GridCheckBoxColumn DataField="LST_IS_SYSTEM" DataType="System.Boolean"
HeaderText="LST_IS_SYSTEM" SortExpression="LST_IS_SYSTEM"
UniqueName="LST_IS_SYSTEM" Visible="False" Display="False" ReadOnly="True">
</telerik:GridCheckBoxColumn>
<telerik:GridCheckBoxColumn DataField="LST_CAN_EDIT" DataType="System.Boolean"
HeaderText="LST_CAN_EDIT" SortExpression="LST_CAN_EDIT"
UniqueName="LST_CAN_EDIT" Visible="False" Display="False" ReadOnly="True">
</telerik:GridCheckBoxColumn>
<telerik:GridCheckBoxColumn DataField="LST_CAN_DELETE"
DataType="System.Boolean" HeaderText="LST_CAN_DELETE"
SortExpression="LST_CAN_DELETE" UniqueName="LST_CAN_DELETE"
Visible="False" Display="False" ReadOnly="True">
</telerik:GridCheckBoxColumn>
<telerik:GridCheckBoxColumn DataField="LST_IS_DELETED"
DataType="System.Boolean" HeaderText="LST_IS_DELETED"
SortExpression="LST_IS_DELETED" UniqueName="LST_IS_DELETED"
Visible="False" Display="False" ReadOnly="True">
</telerik:GridCheckBoxColumn>
</Columns>
</
MasterTableView>
<ClientSettings>
<Selecting AllowRowSelect="True" />
</ClientSettings>
</telerik:RadGrid>
ColumnPropertyDescriptor Class (VB) - SystemField class stores the DataColumn meta-data
Public
Class ColumnPropertyDescriptor
Inherits System.ComponentModel.PropertyDescriptor
#Region
"Attributes"
Private _column As SystemField
#End
Region
#Region
"Constractor"
Public Sub New(ByVal column As SystemField)
MyBase.New(column.ColumnName, Nothing)
_column = column
End Sub
#End
Region
#Region
"Properties"
Public Property Column() As SystemField
Get
Return _column
End Get
Set(ByVal value As SystemField)
_column = value
End Set
End Property
#End
Region
#Region
"PropertyDescriptor Overrides"
#Region
"Properties"
Public Overrides ReadOnly Property Converter() As System.ComponentModel.TypeConverter
Get
Return ComponentModel.TypeDescriptor.GetConverter(Me.PropertyType)
End Get
End Property
Public Overrides ReadOnly Property DisplayName() As String
Get
Return _column.Properties.Caption
End Get
End Property
Public Overrides ReadOnly Property Name() As String
Get
Return _column.ColumnName
End Get
End Property
Public Overrides ReadOnly Property ComponentType() As System.Type
Get
Return Me.PropertyType
End Get
End Property
Public Overrides ReadOnly Property IsReadOnly() As Boolean
Get
Return _column.Properties.IsEdit
End Get
End Property
Public Overrides ReadOnly Property PropertyType() As System.Type
Get
Return _column.SystemColumnType
End Get
End Property
#End
Region
#Region
"Public Methods"
Public Overrides Function CanResetValue(ByVal component As Object) As Boolean
Return False
End Function
Public Overrides Function GetValue(ByVal component As Object) As Object
Dim item As BaseEntity = CType(component, BaseEntity)
Return item.Field(Me._column.ColumnName)
End Function
Public Overrides Sub ResetValue(ByVal component As Object)
Throw New NotImplementedException
End Sub
Public Overrides Sub SetValue(ByVal component As Object, ByVal value As Object)
Dim item As BaseEntity = CType(component, BaseEntity)
item.Field(
Me._column.ColumnName) = value ' value is stored in a key dictionary
End Sub
Public Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean
Return False
End Function
#End
Region
#End
Region
End
Class
BaseEntity Class:
Imports
System.ComponentModel
Public
Class BaseEntity
Implements ICustomTypeDescriptor
#Region
"Data Members"
Private _dataItem As Connectivity.DataItem
Private _values As New Dictionary(Of String, Object)
Private _originalID As String
Private _originalVTS As Date
Private _isNew As Boolean = False
Private _isModified As Boolean = False
Private _PropertyCollection As PropertyDescriptorCollection = New PropertyDescriptorCollection(Nothing)
#End
Region
#Region
"Constractor"
Public Sub New(ByRef DataItem As esmOPM.Connectivity.DataItem, ByVal fetchLevel As esmOPM.FetchLevel, ByVal entityDataRow As DataRow)
_dataItem = DataItem
If IsNothing(entityDataRow) Then
_isNew =
True
' create empty dictionary
For Each sf In _dataItem.Fields(fetchLevel)
_values.Add(sf.ColumnName,
Nothing)
_PropertyCollection.Add(
New ColumnPropertyDescriptor(sf))
Next sf
Else
_isNew =
False
' parse row-dara values
For Each sf In _dataItem.Fields(fetchLevel)
_values(sf.ColumnName) = entityDataRow(sf.ColumnName)
_PropertyCollection.Add(
New ColumnPropertyDescriptor(sf))
Next
_originalID = entityDataRow(_dataItem.PKFieldName).ToString
_originalVTS = entityDataRow(_dataItem.VTSFieldName)
End If
End Sub
#End
Region
#Region
"Public Data Properties"
Public Property Field(ByVal columnName As Object) As Object
Get
Return _values(columnName)
End Get
Set(ByVal value As Object)
_setFieldValue(columnName, value)
End Set
End Property
Public ReadOnly Property ID() As String
Get
Return _values(_dataItem.PKFieldName).ToString
End Get
End Property
Public Property IsNew() As Boolean
Get
Return _isNew
End Get
Set(ByVal value As Boolean)
_isNew = value
End Set
End Property
Public Property IsModified() As Boolean
Get
Return _isModified
End Get
Set(ByVal value As Boolean)
_isModified = value
End Set
End Property
#End
Region
#Region
"Internal Properties"
Friend ReadOnly Property Original_ID() As String
Get
Return _originalID
End Get
End Property
Friend ReadOnly Property Original_VTS() As Date
Get
Return _originalVTS
End Get
End Property
#End
Region
#Region
"Public Methods"
Public Sub ParseValues(ByVal valuesDisctionary As Dictionary(Of String, Object))
For Each de In valuesDisctionary
Me.Field(de.Key) = de.Value
Next de
End Sub
#End
Region
#Region
"Private Methods"
Private Sub _setFieldValue(ByVal columnName As String, ByVal value As Object)
' store old values
If Not (_isNew) AndAlso columnName = _dataItem.PKFieldName Then
_originalID =
Me.Field(columnName).ToString
End If
If Not (_isNew) AndAlso columnName = _dataItem.VTSFieldName Then
_originalVTS =
CDate(Me.Field(columnName))
End If
' set current value
_values(columnName) = value
' mark this object as being modified
_isModified =
True
' set new timestamp value
_values(_dataItem.VTSFieldName) = Now()
End Sub
#End
Region
Public Function GetAttributes() As System.ComponentModel.AttributeCollection Implements System.ComponentModel.ICustomTypeDescriptor.GetAttributes
Return New System.ComponentModel.AttributeCollection(Nothing)
End Function
Public Function GetClassName() As String Implements System.ComponentModel.ICustomTypeDescriptor.GetClassName
Return Nothing
End Function
Public Function GetComponentName() As String Implements System.ComponentModel.ICustomTypeDescriptor.GetComponentName
Return Nothing
End Function
Public Function GetConverter() As System.ComponentModel.TypeConverter Implements System.ComponentModel.ICustomTypeDescriptor.GetConverter
Return Nothing
End Function
Public Function GetDefaultEvent() As System.ComponentModel.EventDescriptor Implements System.ComponentModel.ICustomTypeDescriptor.GetDefaultEvent
Return Nothing
End Function
Public Function GetDefaultProperty() As System.ComponentModel.PropertyDescriptor Implements System.ComponentModel.ICustomTypeDescriptor.GetDefaultProperty
Return Nothing
End Function
Public Function GetEditor(ByVal editorBaseType As System.Type) As Object Implements System.ComponentModel.ICustomTypeDescriptor.GetEditor
Return Nothing
End Function
Public Function GetEvents() As System.ComponentModel.EventDescriptorCollection Implements System.ComponentModel.ICustomTypeDescriptor.GetEvents
Return New EventDescriptorCollection(Nothing)
End Function
Public Function GetEvents(ByVal attributes() As System.Attribute) As System.ComponentModel.EventDescriptorCollection Implements System.ComponentModel.ICustomTypeDescriptor.GetEvents
Return New EventDescriptorCollection(Nothing)
End Function
Public Function GetProperties() As System.ComponentModel.PropertyDescriptorCollection Implements System.ComponentModel.ICustomTypeDescriptor.GetProperties
Return _PropertyCollection
End Function
Public Function GetProperties(ByVal attributes() As System.Attribute) As System.ComponentModel.PropertyDescriptorCollection Implements System.ComponentModel.ICustomTypeDescriptor.GetProperties
Return _PropertyCollection
End Function
Public Function GetPropertyOwner(ByVal pd As System.ComponentModel.PropertyDescriptor) As Object Implements System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner
Return Me
End Function
End
Class
and finally ... FactoryList Class:
Imports
System.ComponentModel
Imports
esmOPM.Connectivity
Public
Class esmList(Of Tkey)
Inherits BindingList(Of Tkey)
Implements ITypedList
#Region
"Data Members"
Private _PropertyCollection As PropertyDescriptorCollection = New PropertyDescriptorCollection(Nothing)
#End
Region
#Region
"Properties"
Friend WriteOnly Property Fields() As List(Of SystemField)
Set(ByVal value As List(Of SystemField))
End Set
End Property
#End
Region
#Region
"Constractors"
Public Sub New(ByVal fields As List(Of SystemField))
MyBase.New()
' init propertyCollection
_PropertyCollection.Clear()
For Each sf In fields
_PropertyCollection.Add(
New ColumnPropertyDescriptor(sf))
Next sf
End Sub
#End
Region
Public Function GetItemProperties(ByVal listAccessors() As System.ComponentModel.PropertyDescriptor) As System.ComponentModel.PropertyDescriptorCollection Implements System.ComponentModel.ITypedList.GetItemProperties
Return _PropertyCollection
End Function
Public Function GetListName(ByVal listAccessors() As System.ComponentModel.PropertyDescriptor) As String Implements System.ComponentModel.ITypedList.GetListName
Return "DefaultView"
End Function
End
Class