Is it possible display/hide spans in the codebehind based on their class?
I've been able to do this with a span's id but not with classes.
markup:
<span runat='server' id='myId' class='myClass'>some text</span>
codebehind:
protected override void OnPreRender(EventArgs e)
{
// This works
myId.Visible = false;
// This doesn't work
myClass.Visible = false;
}
I get the error "The name myClass does not exist in current context".
But the codebehind has no trouble with the id.
No, there is nothing that exists that lets you refer to something by class; only the ID actually works in the code-behind. You can find the object by ID, and then check it's class, or you can define a container control around something:
<asp:Panel ID="X" runat="server">
.
.
</asp:Panel>
And using this, you can loop through the controls in the panel, check the class, and process it, like:
foreach (var c in X.Controls)
{
if (c is WebControl && ((WebControl)c).CssClass == "myClass")
//Do something
else if (c is HtmlControl && ((HtmlControl)c).Attributes.ContainsKey("class") && ((HtmlControl)c).Attributes["class"] == "myClass")
//Do something
}
That's possible to do. You could also do it at the page level, but would have to do it recursively, and it may affect your apps performance.
You could try something like this:
<span runat='server' id='myId' class='myClass'>some text</span>
...
</span>
myId.Attributes.Add("style", "display:none;");
Of course it will not. You are using class name as an Id.
You can select the control using Contols Collection on Page object.
foreach (Control c in Page.Controls)
{
if (c.CssClass == "myClass")
{
c.Visible=false;
}
}
Related
I'm trying to target the ID of an anchor within a user control named myMenu. The user control is on a master page. So, I'm trying to add a class of "active" from one of the content pages so it will highlight the link for that particular page. Right now I have:
if (Master != null)
{
var sitenav = (UserControl)Master.FindControl(id: "myMenu");
if (sitenav != null)
{
var navlink = sitenav.Parent;
}
}
I'm still trying to figure out the logic here and can't find anything that has that info. I know I'd do the htmlanchor as the type?
html in user control:
<li><a runat="server" ID="linka" href="#">Link A</a></li>
<li><a runat="server" ID="linkb" href="#">Link B</a></li>
<li><a runat="server" ID="linkc" href="#">Link C</a></li>
<li><a runat="server" ID="linkd" href="#">Link D</a></li>
MasterPage.cs:
... class MasterClass ...
public void performAction(bool toggle){
if (toggle){
myId.class += "active"; //something like that
}
}
ContentPage.cs
(MasterClass)performAction(true);
This is how you can access a master page function and get it to do something on the master page elements.
To add a css class from code behind, I'd refer you to this answer which is both elegant and complete.
Caspar Kleijne's answer
So your complete solution would be:
From within the Page you can cast the Master page to a specific type (the type of your own Master that exposes the desired functionality), using as to side step any exceptions on type mismatches:
Content Page:
var master = Master as MyMasterPage;
if (master != null)
{
master.AddClass("active");
}
In the above code, if Master is not of type MyMasterPage then master will be null and no method call will be attempted; otherwise it will be called as expected.
MasterPage
public void AddClass(string classname ){
// add a class
myMenu.CssClass = String.Join(" ", myMenu
.CssClass
.Split(' ')
.Except(new string[]{"",classname})
.Concat(new string[]{classname})
.ToArray()
);
}
Easy one here.
I have a control inside a control inside a masterpage. The page name looks something like this in HTML
ctl00$MasterPageBody$MainControl$ChildControl
Any idea how I can get the above from code behind?
Thanks!
It's the clientID property of the control.
If we're talking about a top-level control on the page, or at least one that isn't in any sort of repeating type, you can just use the ClientID property.
<asp:Label runat="server" ID="testLabel" />
<script>
$('#<%= testLabel.ClientID %>').click(function() { ... });
</script>
If we're talking about something that isn't accessible directly like that, you'll have to do the same thing, but it'll have to be wrapped in a FindControl. The example of that isn't so clean, so I'll trust you can understand from my words. But basically, if you have a label that shows code-behind-given text on each row of a GridView, you'll have to call ((Label)e.Row.FindControl("testLabel")).ClientID. Still pretty straight-forward, but a bit more complicated than the first case.
You should use a function similar to this:
public static Control FindControlRecursive(Control ctl, string id) {
if (!ctl.HasControls())
return null;
Control res = null;
foreach(Control c in ctl.Controls) {
if (c.ID == id) {
res = c;
break;
} else {
res = FindControlRecursive(c, id);
if (res != null)
break;
}
}
return res;
}
in this way:
Control ChildControl = FindControlRecursive(this.Page, "ChildControl");
string ID = ChildControl.ClientID;
Is there a way to access a group of controls in ASP.NET somehow?
In jQuery you can access multiple elements using a class="somegroup" like
$('.somegroup')...
In ASP.NET I understand I can access an element or control using the ID, but is there a way to access multiple controls or elements at once?
For example, let's say I have this in design view:
<asp:Label ID="label1" CssClass="someclass"></asp:Label>
<asp:Label ID="lbl" CssClass="someclass"></asp:Label>
<asp:Label ID="lb2" CssClass="someclass"></asp:Label>
Now I want to turn the visibility off on all of them.
Instead of doing this:
label1.Visible = false;
lbl.Visible = false;
lb2.Visible = false;
Is there an equivalent to this?
someclass.Visible = false;
Is there possibly a different tag property I could be using?
using asp.net and C#
You may write your own function, pass a string class into it (and optionally a parent control or form) then loop thru Controls collection checking for the CssClass property and making needed modifications for matched controls.
Something like
void hide(Control el, string cssClass) {
foreach (WebControl c in el.Controls)
{
if (c.CssClass == cssClass)
{
c.Visible = false;
}
}
}
and call
hide(this, "someclass");
public void Apply(string selector, Control parent, Action<WebControl> a)
{
if (selector.StartsWith("."))
{
foreach(WebControl wc in parent.Controls)
{
if (wc.CssClass == selector.Substring(1))
{
a(wc);
if (wc.HasControls())
{
Apply(selector,wc,a);
}
}
}
}
if (selector.StartsWith("#"))
{
foreach (WebControl wc in parent.Controls)
{
if (wc.ID == selector.Substring(1))
{
a(wc);
return;//no need to search any further.
}else
{
if (wc.HasControls())
{
Apply(selector, wc, a);
}
}
}
}
}
Maybe this will help?
then you can do this:
Apply(".SomeClass", this, a => a.CssClass="SomethingElse");
There is no baked-in syntax for doing a selector-type operation in C#.
You can however write your own loop to do this, like this:
private void SetAllLabelsWithCssClassValueToInvisible(Control parentControl,
string className)
{
foreach(Control childControl in parentControls.Controls)
{
// Try to cast control to a label, null if it fails
var label = childControl as Label;
// Check to see if we successfully cast to label or not
if(label != null)
{
// Yes, it is a label
// Does it have the correct CssClass property value?
if(label.CssClass == className)
{
// Update the Visible property to false
label.Visible = false;
}
}
}
}
Note: Obviously you can expand/improve upon this notion and make it fit your needs, just a proof of concept that is very specific for Label controls with a particular CssClass value and the Visible property.
Internally, the jQuery Sizzle engine is doing this looping for you.
I'm coding a calendar that displays some events. Each day has a button for morning, afternoon and night events, when there are events to show the button is enabled and its color is changed. I am displaying these buttons in an html table and when someone changes the month being displayed the program has to "cleanup" the buttons by disabling all of them and setting their colors to white again. Thing is I was able to enable them by using the FindControl method on the table containing the buttons this way:
string butControl = /* id of the button */
Button block = mainTable.FindControl(butControl) as Button;
block.BackColor = Color.Gray;
block.Enabled = true;
And it works fine. In my cleanup method I don't want to call all the names of the buttons because there are 105, instead I used this method:
private void CleanUp()
{
foreach (Control c in mainTable.Controls)
{
Button bot = c as Button;
if (bot != null)
{
bot.BackColor = Color.White;
bot.Enabled = false;
}
}
}
But this does not change the color or enabled property of any of the buttons. My question is: Are not the controls in the Controls property of the table the same that can be found via the FindControl method? Or am I doing something wrong when retrieving the controls?
Isn't the problem that in you're iterating a list of controls rather than the hierarchy? FindControl uses the hierarchy. You can loop the controls as follows:
public IEnumerable<T> EnumerateRecursive<T>(Control root) where T : Control
{
Stack<Control> st = new Stack<Control>();
st.Push(root);
while (st.Count > 0)
{
var control = st.Pop();
if (control is T)
{
yield return (T)control;
}
foreach (Control child in control.Controls)
{
st.Push(child);
}
}
}
public void Cleanup()
{
foreach (Button bot in EnumerateRecursive<Button>(this.mainTable))
{
bot.BackColor = Color.White;
bot.Enabled = false;
}
}
You can implement it using recursion as well, but I usually prefer a stack because it is much faster.
I assume that you're using an ASP table, as that would certainly not work. You could get around it in other ways, but if it doesn't matter to you to use some HTML, I would suggest that you restructure it to look like this:
<form id="form1" runat="server">
<asp:Panel ID="mainTable" runat="server">
<table>
<tr>
<td>
<asp:Button ID="Button1" runat="server" Text="Button" />
</td>
</tr>
</table>
</asp:Panel>
</form>
Note the use of only html controls inside the asp:Panel except for the actual buttons. Using ASP, you would have to recursively look for children.
EDIT:
Speaking of recursively looking for children, Stefan made that exact suggestion and provided code before I finished writing, and I would definitely recommend his method; he's evidently much less lazy than me.
==================================
Stefan's approach has a slight error in that you can't explicitly typecast without knowing a type, and you can't know a type if you use generics, as he has. Here is a lazy adaptation for use purely with buttons, as you are using it for.
Do not give this "answer" status. It is a corruption of someone else's work.
public IEnumerable<Button> EnumerateRecursive(Control root)
{
// Hook everything in Page.Controls
Stack<Control> st = new Stack<Control>();
st.Push(root);
while (st.Count > 0)
{
var control = st.Pop();
if (control is Button)
{
yield return (Button)control;
}
foreach (Control child in control.Controls)
{
st.Push(child);
}
}
}
public void Cleanup()
{
foreach (Button bot in EnumerateRecursive(this.mainTable))
{
bot.BackColor = Color.White;
bot.Enabled = false;
}
}
I have in many places in my ASP.NET project used the Session variable for storing data. I usually write something like this:
public uint MyPropery
{
get
{
object o = Session["MyProperty"];
if (o != null)
return (uint)o;
else
return 0;
}
set
{
Session["MyProperty"] = value;
}
}
However, this time I get a NullReferenceException in the setter. As far as I know, it is valid to assign the Session variable in the manner above. Also, Session is not null and neither is value.
Any ideas on this?
Edit:
Adding the code for the UserControl in which the property exists. I am using ext.net but that shouldn't have anything to do with this. One thought that crossed my mind:
The UserControl (seen below) is added dynamically in code-behind of a page. Can that have anything to do with it?
I am adding UserControls like this (on a Page):
foreach(CoreCommons.System.Comment c in cg.Reply_Comments)
{
WebApplicationExtNetTest.Secure.UserControls.CoreComment cc = new UserControls.CoreComment();
cc._Comment = c; // here is where i get the NullRef
this.Panel1.ContentControls.Add(cc);
}
Markup:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="CoreComment.ascx.cs" Inherits="WebApplicationExtNetTest.Secure.UserControls.CoreComment" %>
<%# Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<ext:Panel runat="server" ID="CoreCommentOuterPanel" BodyStyle="background: #FFFDDE">
<Items>
<ext:ColumnLayout runat="server">
<Columns>
<ext:LayoutColumn ColumnWidth="0.8">
<ext:Image runat="server" ImageUrl="/Resources/bullet_triangle_green_16x16.png" Align="AbsMiddle"></ext:Image>
<ext:Label runat="server" ID="lblCommentInfo"></ext:Label>
</ext:LayoutColumn>
<ext:LayoutColumn ColumnWidth="0.2"><ext:Button runat="server" ID="btnDelete" Icon="Delete"></ext:Button></ext:LayoutColumn>
</Columns>
</ext:ColumnLayout>
<ext:Label runat="server" ID="lblComment"></ext:Label>
</Items>
</ext:Panel>
Code-behind:
namespace WebApplicationExtNetTest.Secure.UserControls
{
public partial class CoreComment : System.Web.UI.UserControl
{
public CoreCommons.System.Comment _Comment
{
get
{
object o = Session["CoreComment_ObjectId"];
if (o != null)
return (tWorks.Core.CoreCommons.System.Comment)o;
else
return null;
}
set
{
Session["CoreComment_ObjectId"] = value;
SetComment();
}
}
protected void Page_Load(object sender, EventArgs e)
{
}
private void SetComment()
{
if (_Comment == null)
{
lblCommentInfo.Text = "";
lblComment.Text = "";
}
else
{
lblCommentInfo.Text = _Comment.Author + ", " + _Comment.TimeStamp.ToString("g");
lblComment.Text = _Comment.Text;
}
}
}
}
I'm almost completely sure the NullReferenceException is thrown in SetComment() because none of the CoreComment's child controls (lblComment, lblCommentInfo) are properly instantiated at the point you set the _Comment property.
The reason these child controls are not instantiated is indeed the way you currently add the CoreComment controls. For dynamically adding UserControls, you must use Page.LoadControl() (see: here) to create a new instance of the control, as it does some behind-the-scenes magic to ensure it is properly initialized, which includes the instantiation of the child controls.
On a sidenote, personally I'd change SetComment() to SetComment(CoreCommons.System.Comment comment) and use the parameter instead of repeatedly calling the getter, or, if staying with the original, at least call the getter only once and store the result in a local variable. With what I assume is probably InProc session storage it won't make much of a difference, but in any other storage mode you'd repeatedly deserialize the Comment object for no reason.
You need to use the Page.LoadControl() method instead , please look here
BTW:the problem is in adding the control programatically with that way.
Use:
return Session["MyProperty"] as uint? ?? 0;
and post somewhere full exception stack trace with inner exception(s)