Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I vary my ItemTemplate inside an asp:Repeater?

Tags:

c#

asp.net

I have a user control which is used to display search results. The HTML for each result displayed will vary based on the type of result being displayed: "contacts" are displayed in one way, "news articles" are displayed in another, etc. There are around 10 different types of results that are all marked up differently when they get to HTML — so I need around 10 or so different templates for individual results that I can choose between based on the current item being displayed.

I'm using an asp:Repeater to display the results, but I don't know how to select the appropriate template within the asp:Repeater <ItemTemplate>. Ideally I'd like the ASP to select the appropriate template to use based upon the object type being passed in via the searchResultsRepeater.DataSource — but unfortunately I can't use switch on type (see this blog entry for C# switch on type). I can however just pass through an enum value for the type of result being displayed.

In the backend C# code I have an abstract inline SearchResult class, and children of that class like ContactSearchResult, NewsArticleSearchResult, etc. The searchResultsRepeater.DataSource would then be bound to a List<SearchResult>. Each SearchResult contains a ResultListingType type field which gives the type of the listing to be displayed.

Attempt 1: using control flow inside the ASP itself

My first attempt was something like this:

        <asp:Repeater ID="searchResultsRepeater" runat="server">
        <ItemTemplate>
        <div class="item">

        <% switch (DataBinder.Eval(Container.DataItem, "type")) { %>

        <% case ResultListingType.CONTACT: %>

            <p><%# DataBinder.Eval(Container.DataItem, "firstName") %></p>
            <p><%# DataBinder.Eval(Container.DataItem, "lastName") %></p>

            <% break; %>

        <% case ResultListingType.NEWS: %>

            <p><%# DataBinder.Eval(Container.DataItem, "newsHeadline") %></p>
            <p><%# DataBinder.Eval(Container.DataItem, "newsDate") %></p>

            <% break; %>

        <% Case AnotherTypeOfListing1: %>
        <% Case AnotherTypeOfListing2: %>
        <% Case AnotherTypeOfListing3: %>
        <% Case AnotherTypeOfListing4: %>
        <% Case AnotherTypeOfListing5: %>
        <% etc... %>

        <% } %>

        </div>

        </ItemTemplate>
        </asp:Repeater>

Unfortunately, this doesn't work:

  • "switch" and "if" both give "invalid expression term" inside the <%# ... %> brackets.
  • "Container.DataItem" gives "the name "Container" does not exist in the current context" inside <% ... %> brackets.

Attempt 2: setting asp:PlaceHolder's to Visible = False

I found something that looked useful at how to change the ItemTemplate used in an asp:repeater?. I then tried something like:

        <asp:Repeater ID="searchResultsRepeater" runat="server">
        <ItemTemplate>
        <div class="item">

        <asp:PlaceHolder ID="newsResultListing" runat="server">
            <p><%# DataBinder.Eval(Container.DataItem, "newsHeadline") %></p>
            <p><%# DataBinder.Eval(Container.DataItem, "newsDate") %></p>
        </asp:PlaceHolder>

        <asp:PlaceHolder ID="contactResultListing" runat="server">
            <p><%# DataBinder.Eval(Container.DataItem, "firstName") %></p>
            <p><%# DataBinder.Eval(Container.DataItem, "lastName") %></p>
        </asp:PlaceHolder>

        </div>

        </ItemTemplate>
        </asp:Repeater>

In my ItemDataBound event I did:

        Control newsResultListing = e.Item.FindControl("newsResultListing");
        newsResultListing.Visible = false;
        Control contactResultListing = e.Item.FindControl("contactResultListing");
        contactResultListing.Visible = false;
        switch (item.type)
        {
            case ResultListingType.CONTACT:
                contactResultListing.Visible = true;
                break;
            case ResultListingType.NEWS:
                newsResultListing.Visible = true;
                break;
            default:
                throw new Exception("Unknown result listing type");
        }

Unfortunately this doesn't work because ASP seems to still be running the contents of the PlaceHolder even after I set Visible = false. I get the error "DataBinding: 'usercontrols_ResultsListing+ContactResultsListing' does not contain a property with the name 'newsHeadline'" — i.e. the newsResultListing PlaceHolder is still looking for the "newsHeadline" field, even though that field doesn't exist for the result listing type being displayed.

In fact I've tried a quick test throw new Exception("e"); in my ItemDataBound, and it looks like the "DataBinding" error is thrown even before control flow gets to the ItemDataBound method, so there's really nothing I can do in there to avoid this error.

I suppose I could add every single field to the parent class and leave most of them null in my children, but that seems really ugly.


Is there a way to make this work, or an easier way to vary my ItemTemplate based upon the type of Container.DataItem I'm currently iterating over? I'm very new to ASP so there's likely something simple that I've missed. :)

like image 740
George Avatar asked Apr 04 '11 00:04

George


People also ask

What is ItemDataBound in repeater?

ItemDataBound event is triggered for each Repeater Item when it is bound to the Data row. Database. I have made use of the following table Customers with the schema as follows. I have already inserted few records in the table. Note: You can download the database table SQL by clicking the download link below.

What is a repeater ASPX?

A Repeater is a Data-bound control. Data-bound controls are container controls. It creates a link between the Data Source and the presentation UI to display the data. The repeater control is used to display a repeated list of items.


2 Answers

OK, I think I've found a solution — I've created multiple additional user controls, one for each type of search result. Each one of these controls defines the HTML template for its corresponding type of search result.

My asp:Repeater contains absolutely nothing inside its ItemTemplate:

    <asp:Repeater ID="searchResultsRepeater" runat="server">
        <ItemTemplate>
            <%-- empty template: we insert usercontrols in the ItemDataBound --%>
        </ItemTemplate>
    </asp:Repeater>

I bind a List<SearchResultData> to my asp:Repeater as before, and as before this list contains more specific subtypes of SearchResultData based on the type of result to be shown.

In my ItemDataBound handler I instantiate one of those user controls based on the type of data in e.Item.DataItem and then insert that user control into the repeater:

        var aspxItem = e.Item;
        var dataItem = (SearchResultData) e.Item.DataItem;

        if (dataItem is ContactSearchResult.ContactSearchResultData)
        {
            var contactSearchResultUC = LoadControl("~/UserControls/ResultsListingSearchResult/ContactSearchResult.ascx") as ASP.ContactSearchResult;
            contactSearchResultUC.data = (ContactSearchResult.ContactSearchResultData)dataItem;
            aspxItem.Controls.Add(contactSearchResultUC);
        }
        else if (dataItem is NewsArticleSearchResult.NewsArticleSearchResultData)
        {
            var newsArticleSearchResultUC = LoadControl("~/UserControls/ResultsListingSearchResult/NewsArticleSearchResult.ascx") as ASP.NewsArticleSearchResult;
            newsArticleSearchResultUC.data = (NewsArticleSearchResult.NewsArticleSearchResultData)dataItem;
            aspxItem.Controls.Add(newsArticleSearchResultUC);
        }

        ...etc
like image 119
George Avatar answered Sep 28 '22 05:09

George


To add to George's solution, the <ItemTemplate> can be a mix of markup and dynamic controls. The following example renders a table of name/value pairs.

<table cellspacing="0" cellpadding="5" border="0" width="100%">
    <tbody>

        <asp:Repeater ID="TheRepeater" OnItemDataBound="TheRepeater_ItemDataBound" runat="server">
            <ItemTemplate>

                <tr>
                    <td class="LabelText"><%# ((NameValuePair)Container.DataItem).Name%>:</td>
                    <td class="ValueText">
                        <asp:PlaceHolder ID="ValuePlaceHolder" runat="server" />
                    </td>
                </tr>

            </ItemTemplate>
        </asp:Repeater>

    </tbody>
</table>

Code-behind

    protected void TheRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
    {
        if (ListItemType.Item == e.Item.ItemType || ListItemType.AlternatingItem == e.Item.ItemType)
        {
            NameValuePair nvp = (NameValuePair)e.Item.DataItem;
            PlaceHolder container = (PlaceHolder)e.Item.FindControl("ValuePlaceHolder");

            if (typeof(nvp.Value) is String)
            {
                Literal textControl = new Literal() { Mode = LiteralMode.Encode, Text = (string)nvp.Value, EnableViewState = false };
                container.Controls.Add(textControl);
            }
            ...
like image 40
Doug Domeny Avatar answered Sep 28 '22 06:09

Doug Domeny