Проблема с asp: ContentPlaceHolder и блоками кода - PullRequest
7 голосов
/ 08 июня 2009

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

Например:

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 (см. Также этот вопрос ), но не могу, если он содержит какие-либо <%= блоки.

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

Ответы [ 3 ]

8 голосов
/ 09 июня 2009

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

Итак, я проверял различия в коллекции 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

3 голосов
/ 11 августа 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 с пробелами между ними), а затем для sub-ContentPlaceHolder, который будет выполняться для любых вложенных главных страниц.

2 голосов
/ 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. Любой код скрипта (<%%>) генерируется в виде исходного кода самого метода.

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

...