Как выбрать элемент по классу вместо ID в ASP.NET?

У меня есть несколько разбросанных элементов <p> на странице aspx, которые я группирую, используя класс, подобный этому - <p class="instructions" runat="server">

В моем коде позади, используя С#, я хочу скрыть эти элементы, используя что-то вроде instructions.Visible = false;

Однако я понимаю, что могу сделать это только в коде, если я использую ID, но это приведет к недопустимому HTML/CSS Selector, поскольку у вас не может быть несколько ID с тем же именем ID...

Альтернативно есть ли другой способ группировать элементы управления, если не по классу?

EDIT: я не могу использовать JavaScript, поэтому выбор должен выполняться в С# codebehind/ASP.NET

Ответы

Ответ 1

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

На стороне клиента вы можете использовать что-то вроде jQuery, чтобы найти эти элементы и скрыть их:

$(".instructions").hide();

Я, вероятно, сделаю это в ответ, когда страница будет полностью загружена:

$(document).ready(function() { 
   $(".instructions").hide(); 
});

Один недостаток, который скрывает элементы в Javascript, заключается в том, что при наличии достаточного количества данных может потребоваться секунда и вызвать мерцание содержимого. Другое отличие заключается в том, что скрытие содержимого на стороне клиента не удаляет его из DOM - содержимое там просто скрыто. Скрытие управляющих серверов на стороне сервера предотвращает их распространение даже из HTML.

Выполнение того же самого в С# немного сложнее - требуется рекурсивное перемещение дерева управления и поиск элементов в коллекции Control, которые соответствуют. Это достаточно распространенная операция, полезная функция полезности. Синтаксис итератора С# (возврат доходности) полезен при создании этого чистого:

// utility method to recursively find controls matching a predicate
IEnumerable<Control> FindRecursive( Control c, Func<Control,bool> predicate )
{
    if( predicate( c ) )
        yield return c;

    foreach( var child in c.Controls )
    {
        if( predicate( c ) )
            yield return c;
    }

    foreach( var child in c.Controls )
        foreach( var match in FindRecursive( c, predicate ) )
           yield return match;
}

// use the utility method to find matching controls...
FindRecursive( Page, c => (c is WebControl) && 
                          ((WebControl)c).CssClass == "instructions" );

Скрытие элементов управления теперь относительно просто:

foreach( WebControl c in FindRecursive( Page, c => (c is WebControl) && 
                           ((WebControl)c).CssClass == "instructions" ) )
{
    c.Visible = false;
}

Ответ 2

Всё довольно просто. В вашем ASPX:

<p class="instructions" runat="server" OnPreRender="Paragraph_PreRender">

В вашем коде:

protected void Paragraph_PreRender(object sender; EventArgs e)
{
  Control paragraph = (Control)sender;
  paragraph.Visible = !paragraph.CssClass.Contains("instructions");
}

Код будет автоматически подключен к обработчику событий PreRender в вашем классе. Это отдает отправителю элемент управления и устанавливает его видимость, зависящую от класса css. Вам просто нужно настроить теги, и вам не нужно много кода, перемещающего вашу коллекцию управления.

Ответ 3

Я хочу ответить на один из первых ответов - где мы используем рекурсию для прохождения всех элементов управления. Во-первых, разве мы не должны рекурсивно переходить на детские предметы? Я не смотрел внимательно на код и видел, что мы продолжаем вызывать метод рекурсивно на "c", а не "child". Во-вторых, я обнаружил, что ни один из элементов на моей веб-странице не может быть перенесен в WebControl - только в HtmlGenericControl.

После редактирования у меня было это:

    // utility method to recursively find controls matching a predicate
    IEnumerable<Control> FindRecursive( Control c, Func<Control,bool> predicate )
    {
        if( predicate( c ) )
            yield return c;

        foreach (var child in c.Controls) {
            if (predicate((Control)child)) {
               yield return (Control)child;
            }
        }

        foreach( var child in c.Controls )
            foreach( var match in FindRecursive( (Control)child, predicate ) )
               yield return match;
    }

    foreach (Control c in FindRecursive(Page, c => (c is HtmlGenericControl) &&
         ((HtmlGenericControl)c).Attributes["ishidden"] == "1"))
    {
         c.Visible = false;
    }

Обратите внимание, что я не мог использовать "CssClass" - мне пришлось поместить свой собственный атрибут ( "ishidden" ), чтобы добраться до этого, чтобы работать.

<div runat="server" ishidden="1"> ... </div>

Я использую ASP.NET framework 2.0/3.0/3.5.

Ответ 4

Если вы включаете ядро ​​JQuery, все, что вам нужно сделать, это зарегистрировать этот script на своей странице:

<script>
 $(document).ready(function() {
    $(".instructions").hide();
 });
</script>

Он использует селектор класса JQuery http://api.jquery.com/class-selector/

Ответ 5

Вы можете создать рекурсивную функцию для перемещения массива управления страницами, проверки атрибута CssClass для каждого элемента управления и установки по мере необходимости.

Ответ 6

<asp:Panel> подход

Вы можете поместить их внутрь, если они последовательно помещены в вашу форму. Таким образом вы можете легко переключить свойство .Visible панели, чтобы скрыть куски формы.

Подход к JavaScript

Вы можете использовать ClientScriptManager.RegisterStartupScript() для испускания JavaScript, а затем использовать jQuery для скрытия по классу.

Ответ 7

Я тестировал решение blackcatweb. Он возвращает dublicates, поэтому я исправил его и добавил метод для WebControls. Мой код ниже. Для поиска по набору классов attrName = "class":

    /// <summary>
    /// Find controls
    /// </summary>
    /// <param name="c">Control</param>
    /// <param name="predicate">Function</param>
    /// <returns>Control's</returns>
    public static IEnumerable<Control> FindRecursive(Control c, Func<Control, bool> predicate)
    {
        if (predicate(c))
        {
            yield return c;
        }

        foreach (Control child in c.Controls)
        {
            foreach (Control founded in FindRecursive(child, predicate))
            {
                yield return founded;
            }
        }
    }

    /// <summary>
    /// Find WebControls by attr
    /// </summary>
    /// <param name="c">Control</param>
    /// <returns>WebControls</returns>
    public static IEnumerable<WebControl> FindWebControlsByAttr(Control baseControl, string attrName, string attrValue)
    {
        foreach (WebControl c in FindRecursive(baseControl, c => (c is WebControl)
            && !string.IsNullOrEmpty(((WebControl)c).Attributes[attrName])
            && ((WebControl)c).Attributes[attrName].Contains(attrValue)))
        {
            yield return c;
        }
    }

    /// <summary>
    /// Find HtmlGenericControls by attr
    /// </summary>
    /// <param name="c">Control</param>
    /// <returns>HtmlGenericControls</returns>
    public static IEnumerable<HtmlGenericControl> FindControlsByAttr(Control baseControl, string attrName, string attrValue)
    {
        foreach (HtmlGenericControl c in FindRecursive(baseControl, c => (c is HtmlGenericControl)
            && !string.IsNullOrEmpty(((HtmlGenericControl)c).Attributes[attrName])
            && ((HtmlGenericControl)c).Attributes[attrName].Contains(attrValue)))
        {
            yield return c;
        }
    }

Ответ 8

Вы можете использовать JQuery Class Name Selector для этой цели.
Другое решение состоит в том, чтобы сохранить один и тот же идентификатор и не создавать серверы на стороне управления. в JavaScript используйте document.getElementById, чтобы получить элементы управления, в вашем случае вы получите массив, который будет содержать все элементы управления с одинаковыми идентификаторами. Перейдите через эти элементы управления и соответствующим образом настройте их атрибуты отображения.

Ответ 9

Основываясь на решении Sebastian P.R. Gingter, вот что я сделал, хотя он все еще чувствует себя немного взломанным, учитывая, что мне нужно использовать MSControl на основе MS вместо того, чтобы отказаться от более простого элемента управления HTML.

В С#:

protected void Paragraph_PreRender(object sender, EventArgs e) 
{
  WebControl paragraph = (WebControl)sender;
  paragraph.Visible = !paragraph.CssClass.Contains("instructions"); 
} 

В aspx:

 <asp:Label ID="Label1" CssClass="instructions" runat="server" Text="Label" onPreRender="Paragraph_PreRender"></asp:Label>