Как выбрать элемент по классу вместо 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>