Проблема с asp: ContentPlaceHolder и блоками кода

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

Например:

MasterPage.aspx

<asp:ContentPlaceHolder ID="Content1" runat="server" />
<asp:ContentPlaceHolder ID="Content2" runat="server" />

<div>Content1: <%= Content1.Controls.Count %></div>
<div>Content2: <%= Content2.Controls.Count %></div>

APage.aspx

<asp:Content ContentPlaceHolderID="Content1" runat="server">
    Plain text content.
</asp:Content>

<asp:Content ContentPlaceHolderID="Content2" runat="server">
    <%= "Code block content." %>
</asp:Content>

Это отобразит следующее:

Обычный текстовый контент. Содержимое блока кода.

Содержание1: 1

Content2: 0

Почему коллекция ContentPlaceHolder.Controls главной страницы пуста?

Я хочу проверить, заполнен ли ContentPlaceHolder (см. Также этот вопрос), но не могу, если он содержит какие-либо блоки <%=.

Кто-нибудь знает способ обойти это?


person Keith    schedule 08.06.2009    source источник
comment
Спасибо за комментарий к предыдущему вопросу - проверю это позже, когда вернусь домой из офиса .. Этот меня заинтриговал! (+1 от меня!) Позже обновлю :)   -  person Rob Cooper    schedule 08.06.2009


Ответы (3)


Как и обещал, я сказал, что посмотрю. Извините, я так и не загрузил вчера вечером, долгий день, и мне нужно было убрать сено!

Итак, я проверял ContentPlaceHolder.Controls различия в коллекциях между тем, как они заполняются. Я заметил, что при использовании блока кода он переворачивается только для чтения. В любой другой момент он будет просто пустым или заполненным.

Поэтому я решил добавить метод расширения, чтобы проверить это для нас:

ContentPlaceHolderExtensions.cs

public static class ContentPlaceHolderExtensions
{
    public static bool ContainsControlsOrCodeBlock(this ContentPlaceHolder placeHolder)
    {
        if (placeHolder.Controls.Count > 0)
             return true;
        
        return placeHolder.Controls.IsReadOnly;
    }
}

А затем проверьте это на главной странице:

Site.Master

<asp:ContentPlaceHolder ID="Content1" runat="server" />
<asp:ContentPlaceHolder ID="Content2" runat="server" />
<asp:ContentPlaceHolder ID="Content3" runat="server" />

<div>Content1: <%= Content1.Controls.Count %></div>
<div>Content2: <%= Content2.Controls.Count %></div>
<div>Content3: <%= Content3.Controls.Count %></div>

<div>Content1 (Ex. Meth.): <%= Content1.ContainsControlsOrCodeBlock() %></div>
<div>Content2 (Ex. Meth.): <%= Content2.ContainsControlsOrCodeBlock() %></div>
<div>Content3 (Ex. Meth.): <%= Content3.ContainsControlsOrCodeBlock() %></div>

Затем в качестве доказательства концепции я добавил страницу с контентом:

Index.aspx

<asp:Content ContentPlaceHolderID="Content1" runat="server">
Plain Text Content
</asp:Content>

<asp:Content ContentPlaceHolderID="Content2" runat="server">
<%= "Code block content" %>
</asp:Content>

И все сделано так, как ожидалось (я считаю) ..

TBH, хотя он и не идеальный .. Я не думаю, что в этой ситуации мы сможем добиться большего изящества. Я не уверен, как настраиваются другие коллекции элементов управления в этих различных сценариях, поэтому я подключился только к элементу управления ContentPlaceHolder. Другие шаблонные элементы управления могут работать, а могут и не работать.

Мысли?

Вы можете загрузить проект с здесь:

http://code.google.com/p/robcthegeek/source/browse/#svn/trunk/stackoverflow/964724.

person Rob Cooper    schedule 09.06.2009
comment
Ура (+1 и соотв.), Это работает. Я сделал одну настройку - дополнительную проверку для одного LiteralControl, содержащего только пробелы. - person Keith; 09.06.2009
comment
Хороший звонок, я сам думал добавить это :) Спасибо! - person Rob Cooper; 09.06.2009

Слишком много для комментария, вот полный код, который я наконец получил (адаптирован из ответа @Rob Cooper):

public static bool HasContent( this ContentPlaceHolder placeHolder )
{
    if ( placeHolder.Controls.Count > 0 )
    {
        LiteralControl textBlock;
        ContentPlaceHolder subContent;

        foreach ( var ctrl in placeHolder.Controls )
            if ( (textBlock = ctrl as LiteralControl) != null )
            {   //lit ctrls will hold any blocks of text
                if ( textBlock.Text != null && textBlock.Text.Trim() != "" )
                    return true;
            }
            else if ( (subContent = ctrl as ContentPlaceHolder) != null )
            {   //sub content controls should call this recursively
                if ( subContent.HasContent() )
                    return true;
            }
            else return true;   //any other control counts as content

        //controls found, but all are empty
        return false;
    }

    //if any code blocks are used the render mode will be different and no controls will
    //be in the collection, however it will be read only
    return placeHolder.Controls.IsReadOnly;
}

Это включает в себя две дополнительные проверки - сначала для пустых буквальных элементов управления (которые происходят, если страница включает теги <asp:Content с любыми пробелами между ними), а затем для суб-ContentPlaceHolder, который будет выполняться для любых вложенных главных страниц.

person Keith    schedule 11.08.2009

Коллекция элементов управления пуста, потому что, когда присутствуют теги сценария ‹% =%>, буквальные элементы управления не добавляются в дерево элементов управления. Однако серверные элементы управления по-прежнему будут добавляться. Так:

<asp:Content ID="Content2" ContentPlaceHolderID="Content2" Runat="Server">
     <%= "Code block content." %>
     <asp:GridView runat="server" ID="gvTest" />
</asp:Content>

<div>Content2: <%= Content2.Controls.Count %></div>

вернусь

Содержание 2: 1

У Рика Стрэла есть отличная статья , в которой объясняется такое поведение:

Чтобы заставить такой код работать, ASP.NET необходимо переопределить отрисовку конкретного контейнера, в котором размещен любой код сценария. Для этого он использует SetRenderMethodDelegate в контейнере и создает собственный метод рендеринга ...

Вместо того, чтобы создавать буквальные элементы управления в дереве элементов управления, ASP.NET добавляет серверные элементы управления в дерево элементов управления только тогда, когда для контейнера присутствуют теги ‹%%>. Для обработки буквального содержимого и разметки скрипта ASP.NET создает настраиваемый метод отрисовки. Затем этот метод явно записывает любое статическое содержимое HTML и любые выражения сценария с помощью HTML TextWriter. Любой код скрипта (‹%%>) генерируется как сырой код самого метода.

К сожалению, я не могу придумать элегантного решения этой головоломки.

person Chris Van Opstal    schedule 08.06.2009
comment
Хорошее объяснение. Это неприятно для содержимого MVC - я не хочу загромождать содержимое ‹%%› с помощью ‹asp: Controls› только для того, чтобы главная страница могла видеть, что панель содержимого была использована. Должен быть какой-то способ обойти это. - person Keith; 08.06.2009