Как мне изменить свой ItemTemplate внутри asp: Repeater?
У меня есть пользовательский элемент управления, который используется для отображения результатов поиска. HTML для каждого отображаемого результата будет меняться в зависимости от типа отображаемого результата: "контакты" отображаются в одном виде, "новостные статьи" отображаются в другом и т.д. Существует около 10 различных типов результатов, все отмеченные по-разному, когда они попадают в HTML — поэтому мне нужно около 10 или около того разных шаблонов для отдельных результатов, которые я могу выбирать между ними в зависимости от текущего отображаемого элемента.
Я использую asp:Repeater
для отображения результатов, но я не знаю, как выбрать соответствующий шаблон в asp:Repeater <ItemTemplate>
. В идеале я бы хотел, чтобы ASP выбирал подходящий шаблон для использования на основе типа объекта, передаваемого через searchResultsRepeater.DataSource
— но, к сожалению, я не могу использовать тип включения (см. эту запись в блоге для С# switch on type). Однако я могу просто передать значение перечисления для отображаемого типа результата.
В базовом коде С# у меня есть абстрактный встроенный класс SearchResult
и дети этого класса, такие как ContactSearchResult
, NewsArticleSearchResult
и т.д. 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 to Visible = False
Я нашел что-то полезное в как изменить ItemTemplate, используемый в 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");
}
К сожалению, это не работает, потому что ASP, похоже, все еще работает с содержимым PlaceHolder даже после того, как я установил Visible = false
. Я получаю сообщение об ошибке "DataBinding: usercontrols_ResultsListing + ContactResultsListing" не содержит свойства с именем "newsHeadline" "— то есть newsResultListing
PlaceHolder все еще ищет поле "newsHeadline", даже если это поле не существует для отображаемого типа списка результатов.
На самом деле я пробовал быстрый тест throw new Exception("e");
в моем ItemDataBound, и похоже, что ошибка "DataBinding" возникает даже до того, как поток управления переходит к методу ItemDataBound, поэтому я действительно ничего не могу сделать там избегайте этой ошибки.
Я полагаю, я мог бы добавить каждое отдельное поле в родительский класс и оставить большинство из них пустым в моих дочерних элементах, но это кажется действительно уродливым.
Есть ли способ сделать эту работу или более простой способ изменить мой ItemTemplate в зависимости от типа Container.DataItem. Я сейчас выполняю итерацию? Я очень новичок в ASP, поэтому есть что-то простое, что я пропустил.:)
Ответы
Ответ 1
ОК, я думаю, что нашел решение — Я создал несколько дополнительных пользовательских элементов управления, по одному для каждого типа результата поиска. Каждый из этих элементов управления определяет шаблон HTML для соответствующего типа результата поиска.
My 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
Ответ 2
Чтобы добавить к решению Джорджа, <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>
Code-за
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);
}
...
Ответ 3
Вам нужно будет переопределить обработчик событий ItemDataBound и контролировать его там.
В качестве альтернативы, поместите логику в пользовательский элемент управления и поместите элемент управления пользователя в шаблон.
Пользовательскому элементу управления по-прежнему потребуется реализовать адаптивное подгонку, но упрощает повторное использование логики.