Подтвердить что ты не робот

Как мне изменить свой 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, поэтому есть что-то простое, что я пропустил.:)

4b9b3361

Ответ 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 и контролировать его там.

В качестве альтернативы, поместите логику в пользовательский элемент управления и поместите элемент управления пользователя в шаблон.

Пользовательскому элементу управления по-прежнему потребуется реализовать адаптивное подгонку, но упрощает повторное использование логики.