.aspx
files avoiding code-behind files because in my opinion it increases readability and manageability. One of the main drawbacks when using the Repeater in conjunction with databinding syntax is the need for casting the
DataItem
property to it's actual type:I would like to share my take on a strongly typed Repeater which removes the need for casting objects, increases productivity and readability and unlocks intellisense!
The solution
The solution requires you to create two base classes which includes the core functionality and then for every typed Repeater create a simple inherited class which defines the type. As you might have noticed in the above screenshot I introduced a new property called
TypedItem
. You are free to create a name which suits your needs e.g. Container.I
for improved readability.Setting up the base classes:
1. Create a generic Repeater base class
This is the main workhorse of the solution. It was neccessary to override the
InitializeItem
method of the Repeater
class because the default Repeater works with the private variables for the templates. This implementation uses the properties for each template which is required because we will be overriding the ItemTemplate
and AlternatingItemTemplate
for our typed Repeaters.Also the
CreateItem
method is overriden to actually create a generic RepeaterItem<T>
which will be created in step 2.using System.Web.UI; using System.Web.UI.WebControls; public class GenericRepeaterBase<T> : Repeater where T : class { protected override RepeaterItem CreateItem(int itemIndex, ListItemType itemType) { return CreateRepeaterItem(itemIndex, itemType); } private static GenericRepeaterItem<T> CreateRepeaterItem(int itemIndex, ListItemType itemType) { return new GenericRepeaterItem<T>(itemIndex, itemType); } protected override void InitializeItem(RepeaterItem item) { ITemplate template = (ITemplate)null; switch (item.ItemType) { case ListItemType.Header: template = HeaderTemplate; break; case ListItemType.Footer: template = FooterTemplate; break; case ListItemType.Item: template = ItemTemplate; break; case ListItemType.AlternatingItem: template = AlternatingItemTemplate; if (template != null) break; else goto case 2; case ListItemType.Separator: template = SeparatorTemplate; break; } if (template == null) return; template.InstantiateIn((Control)item); } }
2. Create a generic RepeaterItem base class
This class contains the property that you use while working with the Repeater. You can rename the
TypedItem
property as you wish.using System.Web.UI.WebControls; public class GenericRepeaterItem<T> : RepeaterItem where T : class { public GenericRepeaterItem(int itemIndex, ListItemType itemType) : base(itemIndex, itemType) { } public T TypedItem { get { return (T)this.DataItem; } } }
Usage:
3. Create a typed Repeater
Now for every Repeater that you would like to use strongly typed you must create an easy implementation of the
GenericRepeaterBase<T>
class. The example below creates the PersonRepeater
mentioned before.The
PersistenceMode
attribute needs to be redefined because it is not automatically inherited. The TemplateContainer
attribute and the generic type definition on the class make the neccessary definitions for the Repeater to work.public class PersonRepeater : GenericRepeaterBase<GenericRepeaterItem<Person>> { [PersistenceMode(PersistenceMode.InnerProperty)] [TemplateContainer(typeof(GenericRepeaterItem<Person>))] public override ITemplate ItemTemplate { get; set; } [PersistenceMode(PersistenceMode.InnerProperty)] [TemplateContainer(typeof(GenericRepeaterItem<Person>))] public override ITemplate AlternatingItemTemplate { get; set; } }
4. Use it!
public class Person { public string Name { get; set; } public DateTime DateOfBirth { get; set; } }
<%@ Register TagPrefix="asp" Namespace="TypedRepeater" Assembly="TypedRepeater" %> <asp:PersonRepeater runat="server"> <ItemTemplate> Name: <% #Container.TypedItem.Name %><br /> Date of birth: <%#Container.TypedItem.DateOfBirth.ToShortDateString() %> </ItemTemplate> </asp:PersonRepeater>
I hope this code brings you lots of joy! ;)
Well, i had to say that this approach is good.
BeantwoordenVerwijderenBut you still need to define a class for each repeater, which is very annoying. Here is an article which takes one step
further and eliminates the need for derived PersonRepeater class. http://www.codeproject.com/Articles/18049/A-Typed-Repeater-in-ASP-NET
I've used it in my project and it works perfectly.
@Alexey:
BeantwoordenVerwijderenIt's a matter of preference. From that approach I don't like the way it's implemented. I'd rather create a simple strongly typed definition in code then through an error-prone attribute property.
This is besides the fact that I didn't get that solution working :)
I love it!
BeantwoordenVerwijderenThis is one of the things I have been trying to figure out. I get an odd error, though. I have translated it to VB and changed it over to one of my own custom objects.
Everything seems to work as expected until I actual run the page and try to render. On the databinding of the repeater, I get the following error:
System.InvalidCastException was unhandled by user code
HResult=-2147467262
Message=Unable to cast object of type 'SDCore.GenericRepeaterItem`1[SDCore.GenericRepeaterItem`1[SDCore.StateEntity]]' to type 'SDCore.GenericRepeaterItem`1[SDCore.StateEntity]'.
Imports System.Web.UI
Imports System.Web.UI.WebControls
Public Class GenericRepeaterBase(Of T As Class)
Inherits Repeater
Protected Overrides Function CreateItem(itemIndex As Integer, itemType As ListItemType) As RepeaterItem
Return CreateRepeaterItem(itemIndex, itemType)
End Function
Private Shared Function CreateRepeaterItem(itemIndex As Integer, itemType As ListItemType) As GenericRepeaterItem(Of T)
Return New GenericRepeaterItem(Of T)(itemIndex, itemType)
End Function
Protected Overrides Sub InitializeItem(item As RepeaterItem)
Dim template As ITemplate = DirectCast(Nothing, ITemplate)
Select Case item.ItemType
Case ListItemType.Header
template = HeaderTemplate
Exit Select
Case ListItemType.Footer
template = FooterTemplate
Exit Select
Case ListItemType.Item
template = ItemTemplate
Exit Select
Case ListItemType.AlternatingItem
template = AlternatingItemTemplate
If template IsNot Nothing Then
Exit Select
Else
template = FooterTemplate
Exit Select
End If
Case ListItemType.Separator
template = SeparatorTemplate
Exit Select
End Select
If template Is Nothing Then
Return
End If
template.InstantiateIn(DirectCast(item, Control))
End Sub
End Class
Public Class GenericRepeaterItem(Of T As Class)
Inherits RepeaterItem
Public Sub New(itemIndex As Integer, itemType As ListItemType)
MyBase.New(itemIndex, itemType)
End Sub
Public ReadOnly Property TypedItem() As T
Get
Return DirectCast(Me.DataItem, T)
End Get
End Property
End Class
Public Class StateRepeater
Inherits GenericRepeaterBase(Of GenericRepeaterItem(Of StateEntity))
_
_
Public Overrides Property ItemTemplate As ITemplate
Get
Return MyBase.ItemTemplate
End Get
Set(value As ITemplate)
MyBase.ItemTemplate = value
End Set
End Property
_
_
Public Overrides Property AlternatingItemTemplate As ITemplate
Get
Return MyBase.AlternatingItemTemplate
End Get
Set(value As ITemplate)
MyBase.AlternatingItemTemplate = value
End Set
End Property
End Class