I have a few scattered <p>
elements on the aspx page which I am grouping together using a class like 开发者_开发知识库so - <p class="instructions" runat="server">
In my code behind, using C# I want to hide these elements, using something like
instructions.Visible = false;
However I realize I can only do this in codebehind if I use ID but this will result in invalid HTML/CSS Selector since you can't have multiple ID's with the same ID name...
Alternatively is there another way to group the controls if not by class?
EDIT: I can't use JavaScript, so the selection must be done in C# codebehind/ASP.NET
The thing is quite easy. In your ASPX:
<p class="instructions" runat="server" OnPreRender="Paragraph_PreRender">
In your codebehind:
protected void Paragraph_PreRender(object sender, EventArgs e)
{
Control paragraph = (Control)sender;
paragraph.Visible = !paragraph.CssClass.Contains("instructions");
}
The codebehind will be hooked up automatically to the PreRender event handler in your class. This casts the sender to the control and sets its Visibility dependent on the css class. You just have to adjust the tags and you don't need a lot code traversing your control collection.
Aside from grouping all of the controls in a single container control, there is no easy way to find a group of controls given some property in ASP.NET server-side code.
On the client side, you could use something like jQuery to find these elements and hide them:
$(".instructions").hide();
I would probably do this in response when the page is fully loaded:
$(document).ready(function() {
$(".instructions").hide();
});
One downside to hiding elements in Javascript is that if there's enough data it may take a second and cause content to flicker. Another difference is that hiding content client-side does not remove it from the DOM - the content is there just hidden. Hiding controls server-side prevents their content from even being emitted to the HTML.
Doing the same thing in C# is a bit harder - it requires recursively traversing the control tree and looking for elements in the Control
collection that match. This is a common enough operation that a utility function is useful. C# iterator syntax (yield return) is helpful in making this clean:
// 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" );
Hiding the controls now is relatively easy:
foreach( WebControl c in FindRecursive( Page, c => (c is WebControl) &&
((WebControl)c).CssClass == "instructions" ) )
{
c.Visible = false;
}
I want to respond to one of the first answers - where we are using recursion to go through all the controls. First of all, shouldn't we be recursing on the child items? I didn't look closely at the code and saw that we kept calling the method recursively on "c", not "child." Secondly, I found that none of the items on my web page could be cast to WebControl - only to HtmlGenericControl.
After editing, I had this:
// 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;
}
Note that I couldn't use "CssClass" - I had to put my own attribute ('ishidden') to get to this to work.
<div runat="server" ishidden="1"> ... </div>
I'm using ASP.NET framework 2.0/3.0/3.5.
If you include the JQuery core, all you have to do is register this script on your page :
<script>
$(document).ready(function() {
$(".instructions").hide();
});
</script>
It uses the JQuery class selector http://api.jquery.com/class-selector/
You could create a recursive function to walk the pages control array, checking the the CssClass attribute for each control and set as required.
<asp:Panel>
approach
You could put them all inside an if they are consecutively placed in your form. That way you can easily toggle the .Visible property of the panel to hide chunks of the form.
JavaScript approach
You can use the ClientScriptManager.RegisterStartupScript()
to emit JavaScript and then use jQuery to hide by class.
- http://msdn.microsoft.com/en-us/library/z9h4dk8y.aspx
Building on the Sebastian P.R. Gingter solution, Here's what I did, although it still feels a bit of a hack considering that I have to use a MS based WebControl instead of opting out for a simpler, HTML control.
In C#:
protected void Paragraph_PreRender(object sender, EventArgs e)
{
WebControl paragraph = (WebControl)sender;
paragraph.Visible = !paragraph.CssClass.Contains("instructions");
}
In aspx:
<asp:Label ID="Label1" CssClass="instructions" runat="server" Text="Label" onPreRender="Paragraph_PreRender"></asp:Label>
I've tested blackcatweb's solution. It returns dublicates, so i fixed it and added method for WebControls. My code is below. To search by class set 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;
}
}
You can use JQuery Class Name Selector for this purpose.
Another solution is to keep the same id's and not making the controls server side. in JavaScript use document.getElementById to get the controls, in your case you will get an array which will hold all the controls having same id's. Iterate through these controls and set their display attributes accordingly.
精彩评论