我有一个用户控件,用于显示搜索结果。显示的每个结果的HTML将根据显示的结果类型而变化:“联系人”以一种方式显示,“新闻文章”显示在另一种中,等等。大约有10种不同类型的结果都被标记出来当他们使用HTML时会有所不同 - 所以我需要大约10个不同的模板用于个别结果,我可以根据显示的当前项目进行选择。
我正在使用asp:Repeater
来显示结果,但我不知道如何在asp:Repeater <ItemTemplate>
中选择合适的模板。理想情况下,我希望ASP根据通过searchResultsRepeater.DataSource
传入的对象类型选择要使用的相应模板 - 但遗憾的是我无法使用开启类型(请参阅this blog entry for C# switch on type)。但是,我可以通过枚举值来显示正在显示的结果类型。
在后端C#代码中,我有一个抽象的内联SearchResult
类,该类的子项如ContactSearchResult
,NewsArticleSearchResult
等。searchResultsRepeater.DataSource
将被绑定到List<SearchResult>
。每个SearchResult
都包含一个ResultListingType type
字段,用于显示要显示的商家信息的类型。
我的第一次尝试是这样的:
<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>
不幸的是,这不起作用:
<%# ... %>
括号内提供“无效表达式术语”。<% ... %>
括号内给出“名称”容器“当前上下文中不存在。”我在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很陌生,所以我可能会错过一些简单的东西。 :)
答案 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事件处理程序并在那里控制它。
或者,将逻辑放入用户控件并将用户控件放入模板中。
用户控件仍然需要实现适当的切换,但可以更容易地重用逻辑。