Simplifying ASP.NET ListView Control Templates

I've been working with the new ListView control in ASP.NET 3.5 combining it with LINQ and Lambda expressions and was finding myself duplicating a lot of code between ItemTemplate and AlternatingItemTemplate templates (I'll be posting the sample application that demonstrates using LINQ, Lambdas and Stored Procedures soon).  The AlternatingItemTemplate contained the same code as the ItemTemplate except for a CSS class added to the first <tr> element to change the background color.  Here's an example of both templates that were used initially:

<ItemTemplate>
    <tr class="even">
        <td class="tdControls">
            <asp:LinkButton ID="EditButton" CommandName="Edit" runat="server" 
Text="Edit"></asp:LinkButton> <asp:LinkButton ID="DeleteButton"
OnClientClick="return confirm('Delete Record?');"
CommandName="Delete" CommandArgument='<%# Eval("CustomerID")%>'
runat="server" Text="Delete"></asp:LinkButton> </td> <td> <%# Eval("CustomerID") %> </td> <td> <%# Eval("CompanyName") %> </td> <td> <%# Eval("ContactName") %> </td> <td> <%# Eval("ContactTitle") %> </td> <td> <%# Eval("Address") %> </td> <td> <%# Eval("City") %> </td> <td> <%# Eval("Country") %> </td> <td> <asp:LinkButton ID="lbOrders" runat="server" Text="Orders"
CommandName="ViewOrders"
CommandArgument='<%# Eval("CustomerID") %>' /> </td> </tr> <tr id="trOrders" runat="server" visible="false"> <td>&nbsp;</td> <td colspan="8"> <asp:GridView id="gvOrders" runat="server" AutoGenerateColumns="False" BackColor="White" BorderColor="#999999" BorderStyle="Solid"
BorderWidth="1px" CellPadding="3" ForeColor="Black" GridLines="Vertical" Width="500px" EnableViewState="false"> <FooterStyle BackColor="#CCCCCC" /> <Columns> <asp:BoundField DataField="OrderID" HeaderText="OrderID" SortExpression="OrderID" /> <asp:BoundField DataField="OrderDate" HeaderText="OrderDate" SortExpression="OrderDate" HtmlEncode="false"
DataFormatString="{0:d}" /> <asp:BoundField DataField="RequiredDate"
HeaderText="RequiredDate" SortExpression="RequiredDate" HtmlEncode="false"
DataFormatString="{0:d}" /> <asp:BoundField DataField="ShippedDate"
HeaderText="ShippedDate" SortExpression="ShippedDate" HtmlEncode="false"
DataFormatString="{0:d}" /> </Columns> <AlternatingRowStyle BackColor="#eaeaea" /> </asp:GridView> </td> </tr> </ItemTemplate> <AlternatingItemTemplate> <tr class="odd"> <td class="tdControls"> <asp:LinkButton ID="EditButton" CommandName="Edit" runat="server"
Text="Edit"></asp:LinkButton> <asp:LinkButton ID="DeleteButton"
OnClientClick="return confirm('Delete Record?');"
CommandName="Delete" CommandArgument='<%# Eval("CustomerID")%>'
runat="server" Text="Delete"></asp:LinkButton> </td> <td> <%# Eval("CustomerID") %> </td> <td> <%# Eval("CompanyName") %> </td> <td> <%# Eval("ContactName") %> </td> <td> <%# Eval("ContactTitle") %> </td> <td> <%# Eval("Address") %> </td> <td> <%# Eval("City") %> </td> <td> <%# Eval("Country") %> </td> <td> <asp:LinkButton ID="lbOrders" runat="server" Text="Orders"
CommandName="ViewOrders"
CommandArgument='<%# Eval("CustomerID") %>' /> </td> </tr> <tr id="trOrders" runat="server" visible="false"> <td>&nbsp;</td> <td colspan="8"> <asp:GridView id="gvOrders" runat="server" AutoGenerateColumns="False" BackColor="White" BorderColor="#999999" BorderStyle="Solid"
BorderWidth="1px" CellPadding="3" ForeColor="Black" GridLines="Vertical" Width="500px" EnableViewState="false"> <FooterStyle BackColor="#CCCCCC" /> <Columns> <asp:BoundField DataField="OrderID" HeaderText="OrderID" SortExpression="OrderID" /> <asp:BoundField DataField="OrderDate" HeaderText="OrderDate" SortExpression="OrderDate" HtmlEncode="false"
DataFormatString="{0:d}" /> <asp:BoundField DataField="RequiredDate"
HeaderText="RequiredDate" SortExpression="RequiredDate" HtmlEncode="false"
DataFormatString="{0:d}" /> <asp:BoundField DataField="ShippedDate"
HeaderText="ShippedDate" SortExpression="ShippedDate" HtmlEncode="false"
DataFormatString="{0:d}" /> </Columns> <AlternatingRowStyle BackColor="#eaeaea" /> </asp:GridView> </td> </tr> </AlternatingItemTemplate>


It seemed like a waste (and a maintenance headache) to have both templates duplicating the same code while only needing background color changes so I decided to adjust it by adding a little code into the first <tr> tag to dynamically assign the CSS class based upon odd or even rows. It calls the ListViewDataItem class's DataItemIndex property and applies a modulus operation to it.  This isn't anything new of course (I've done the same type of thing for years with GridView and DataGrid controls), but it's a nice trick that can save a lot of duplicated code if you haven't seen it.


<ItemTemplate>
    <tr class='<%# (Container.DataItemIndex % 2 == 0)?"even":"odd" %>'>    
<td>
<asp:LinkButton ID="EditButton" CommandName="Edit" runat="server"
Text="Edit"></asp:LinkButton> <asp:LinkButton ID="DeleteButton"
OnClientClick="return confirm('Delete Record?');"
CommandName="Delete" CommandArgument='<%# Eval("CustomerID")%>'
runat="server" Text="Delete"></asp:LinkButton> </td> <td> <%# Eval("CustomerID") %> </td> <td> <%# Eval("CompanyName") %> </td> <td> <%# Eval("ContactName") %> </td> <td> <%# Eval("ContactTitle") %> </td> <td> <%# Eval("Address") %> </td> <td> <%# Eval("City") %> </td> <td> <%# Eval("Country") %> </td> <td> <asp:LinkButton ID="lbOrders" runat="server" Text="Orders"
CommandName="ViewOrders"
CommandArgument='<%# Eval("CustomerID") %>' /> </td> </tr> <tr id="trOrders" runat="server" visible="false"> <td>&nbsp;</td> <td colspan="8"> <asp:GridView id="gvOrders" runat="server" AutoGenerateColumns="False" BackColor="White" BorderColor="#999999" BorderStyle="Solid"
BorderWidth="1px" CellPadding="3" ForeColor="Black" GridLines="Vertical" Width="500px" EnableViewState="false"> <FooterStyle BackColor="#CCCCCC" /> <Columns> <asp:BoundField DataField="OrderID" HeaderText="OrderID" SortExpression="OrderID" /> <asp:BoundField DataField="OrderDate" HeaderText="OrderDate" SortExpression="OrderDate" HtmlEncode="false"
DataFormatString="{0:d}" /> <asp:BoundField DataField="RequiredDate"
HeaderText="RequiredDate" SortExpression="RequiredDate" HtmlEncode="false"
DataFormatString="{0:d}" /> <asp:BoundField DataField="ShippedDate"
HeaderText="ShippedDate" SortExpression="ShippedDate" HtmlEncode="false"
DataFormatString="{0:d}" /> </Columns> <AlternatingRowStyle BackColor="#eaeaea" /> </asp:GridView> </td> </tr> </ItemTemplate>
del.icio.us Tags: ,,
Published Friday, February 15, 2008 10:00 PM by dwahlin
Filed under: ,

Comments

# re: Simplifying ASP.NET ListView Control Templates

Saturday, February 16, 2008 3:08 AM by Joe Chung

<RowStyle CssClass="even" />

<AlternatingRowStyle CssClass="odd" />

# re: Simplifying ASP.NET ListView Control Templates

Saturday, February 16, 2008 3:29 AM by Rick Strahl

Yeah the alternating item template is kind of a waste - I mean when have you really needed alternate content other than the styling in the thing?

I've used this same trick countless times as well. The hard part for me is remembering exactly what to access - Container, DataContainer etc. I can never remember which it is.

# re: Simplifying ASP.NET ListView Control Templates

Saturday, February 16, 2008 11:12 AM by Tyler

You have to love the efficiency of a simple ternary expression used to eliminate so many lines of static template. Thanks for the reminder that simplicity can be so valuable.

# re: Simplifying ASP.NET ListView Control Templates

Saturday, February 16, 2008 1:47 PM by dwahlin

Joe,

That certainly works for a GridView when fixed styles are used, but the ListView doesn't have that type of functionality available unfortunately.

# re: Simplifying ASP.NET ListView Control Templates

Saturday, February 16, 2008 1:50 PM by dwahlin

Rick,

Yeah...I'm with you there.  I initially wrote a code-behind method that tracked the rows because I couldn't use Container.ItemIndex (which is the only one I seem to remember).  After looking into it more I finally figured out that the ListViewDataItem had a DataItemIndex property as opposed to ItemIndex.  I wish it was all kept a little more consistent.  I also expected to get intellisense for that stuff but didn't in VS 2008.