如何在asp:Repeater中改变我的ItemTemplate?

时间:2011-04-04 00:49:58

标签: c# asp.net

我有一个用户控件,用于显示搜索结果。显示的每个结果的HTML将根据显示的结果类型而变化:“联系人”以一种方式显示,“新闻文章”显示在另一种中,等等。大约有10种不同类型的结果都被标记出来当他们使用HTML时会有所不同 - 所以我需要大约10个不同的模板用于个别结果,我可以根据显示的当前项目进行选择。

我正在使用asp:Repeater来显示结果,但我不知道如何在asp:Repeater <ItemTemplate>中选择合适的模板。理想情况下,我希望ASP根据通过searchResultsRepeater.DataSource传入的对象类型选择要使用的相应模板 - 但遗憾的是我无法使用开启类型(请参阅this blog entry for C# switch on type)。但是,我可以通过枚举值来显示正在显示的结果类型。

在后端C#代码中,我有一个抽象的内联SearchResult类,该类的子项如ContactSearchResultNewsArticleSearchResult等。searchResultsRepeater.DataSource将被绑定到List<SearchResult>。每个SearchResult都包含一个ResultListingType type字段,用于显示要显示的商家信息的类型。

尝试1:在ASP本身内部使用控制流程

我的第一次尝试是这样的:

        <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>

不幸的是,这不起作用:

  • “switch”和“if”都在<%# ... %>括号内提供“无效表达式术语”。
  • “Container.DataItem”在<% ... %>括号内给出“名称”容器“当前上下文中不存在。”

尝试2:将asp:PlaceHolder设置为Visible = False

我在how to change the ItemTemplate used in an asp:repeater?找到了一些有用的东西。然后我尝试了类似的东西:

        <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>

在我的ItemDataBound事件中,我做了:

        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");
        }

不幸的是,这不起作用,因为即使在我设置Visible = false之后,ASP似乎仍在运行PlaceHolder的内容。我收到错误“DataBinding:'usercontrols_ResultsListing + ContactResultsListing'不包含名为'newsHeadline'的属性” - 即newsResultListing PlaceHolder仍然在寻找“newsHeadline”字段,即使该字段不是存在显示的结果列表类型。

事实上我已经在我的ItemDataBound中尝试了快速测试throw new Exception("e");,看起来甚至在控制流到达ItemDataBound方法之前就抛出了“DataBinding”错误,所以我真的无能为力那里是为了避免这个错误。

我想我可以将每个字段添加到父类中,并将大部分字段留给我的孩子,但这看起来真的很难看。


有没有办法让这项工作,或者根据我正在迭代的Container.DataItem的类型更改我的ItemTemplate的更简单方法?我对ASP很陌生,所以我可能会错过一些简单的东西。 :)

3 个答案:

答案 0 :(得分:14)

好的,我想我找到了一个解决方案 - 我已经创建了多个额外的用户控件,每个用户控件对应一种类型的搜索结果。这些控件中的每一个都为其相应类型的搜索结果定义HTML模板。

我的asp:Repeater在其ItemTemplate

中完全没有任何内容
    <asp:Repeater ID="searchResultsRepeater" runat="server">
        <ItemTemplate>
            <%-- empty template: we insert usercontrols in the ItemDataBound --%>
        </ItemTemplate>
    </asp:Repeater>

我像以前一样将List<SearchResultData>绑定到asp:Repeater,之前这个列表包含更多特定的SearchResultData子类型,具体取决于要显示的结果类型。

在我的ItemDataBound处理程序中,我根据e.Item.DataItem中的数据类型实例化其中一个用户控件,然后将该用户控件插入到转发器中:

        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

答案 1 :(得分:3)

要添加到George的解决方案,<ItemTemplate>可以是标记和动态控件的混合。以下示例呈现名称/值对的表。

<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>

代码隐藏

    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);
            }
            ...

答案 2 :(得分:1)

您需要覆盖ItemDataBound事件处理程序并在那里控制它。

或者,将逻辑放入用户控件并将用户控件放入模板中。

用户控件仍然需要实现适当的切换,但可以更容易地重用逻辑。