From within the code-behind of an ASP .NET web user-control (ascx) I need to get access (at runtime) to its parent, a div element. The aim is simple as to modify the visibility of the mentioned parent div.
I can not touch so much of the web-page code so I'd need a solution requiring only modifications in the user-control's code behind.
So in the HTML "part" of the code of the web page I have this:
<div id="parentDIV" runat="server">
<uc:MyUserControl ID="myUserControlInstance" runat="server" />
</div>
I'd like to do in the code behind of the user-control something like this:
this.Container.Visible = false;
Note that I'm not asking if it is a good practise or not to do this.
EDIT:
The user-control code behind does not "know" about the ID of the parent DIV.
I would hide it on the client. Decorate your user control container (div?) with a class like "awesomeUserControl". Then emit some javascript using the ScriptManager object to hide the parent like this:
ScriptManager.RegisterStartupScript(this, this.GetType(), "HideMyAwesomeUserControl", "$('.awesomeUserControl').parent().hide();", true);
The better way...
What you should do is to create a custom event in your user control, to which your container will subscribe - very much like subscribing to a button event, only this is your custom control. This event passes information to your container which can then discern from it what it needs, such as whether or not the div should/not be visible.
It might look something like:
protected void Page_Load(object sender, EventArgs e)
{
this.myuserControl.Update += new MyUserControlUpdateEventHandler(myuserControl_Update);
}
void myuserControl_Update(object sender, MyuserControlEventArgs e)
{
this.parentDiv.visible = !e.ShouldHideUI;
}
This method will decouple your parent with the user control, i.e. your user control doesn't have to have any knowledge at all of the parent's controls, nor should it.
If you are curious, here is a rough example of how your user control will define such an event:
public class MyuserControlEventArgs : EventArgs
{
public bool ShouldHideUI { get;set;}
public MyuserControlEventArgs (bool shouldHideUI)
{
this.ShouldHideUI = shouldHideUI;
}
}
public delegate void MyUserControlUpdateEventHandler(object sender, MyuserControlEventArgs e);
public event MyUserControlUpdateEventHandler Update;
protected void OnUpdate(MyuserControlEventArgs e)
{
if (Update!= null)
Update(this, e);
}
Your user control will simply need to call OnUpdate whenever it feels its subscribers need to know about it.
The Quick and Dirty way...
If you need quick and dirty, then try this (inside your user control):
TheParentControl parentControl = (TheParentControl)this.Parent;
parentControl.ParentDiv.Visible = true;
The key is to cast to the appropriate type (apparently your user control would know what type of parent it has), then set the parent's property. You might expose your div as a property in the parent control. Note, that parent could be ANY control (page, FooControl, BarControl, whatever) whose control collection your user control resides. Once you get a handle to the parent, you can even FindControl() to find a control by name.
You're almost there....
this.Parent.Visible = false;
When all controls get rendered, the HTML Parent child controls can be determined like below.
I am using while loop, so in case you add some other intermediate control, it may not give crash or unexpected results.
public Control ParentControl
{
get
{
Control ctl = this.Parent;
while (true)
{
if (ctl == null) break;
if (ctl.ID.Equals("parentDIV"))
{
break;
}
ctl = ctl.Parent;
}
return ctl;
}
}
if(ParentControl != null)
ParentControl.Visible = true|false;
Am I missing something, it sounds like you have a usercontrol and an aspx page (both have code behind's).
The user control appears to have been added to the aspx page, wrapped in a div. YOu've made the div control runat server (though you can still do this via htmlcontrol).
All you ned to do to manage the div is:
parentDIV.visible = false;
or whatever you need to do with the div.
Why does the UC code behind need to know about the APSX pages DIV, it doesn't.
Related
I'm am a little bit stuck in the ASP.Net's page lifecycle. This is my first ASP.Net project after many years of doing React so I might be missing something;)
Simplified code:
protected void Page_Load(object sender, EventArgs e)
{
BuildView();
}
private void BuildView()
{
switch (pageViewMode.Value)
{
case "Overview": BuildOverview(); break;
case "Runs": BuildRunsOverview(); break;
}
}
private void BuildOverview()
{
var tilesContainer = new TilesContainer();
tilesContainer.OnTileClicked += (InfoTile targetTile) =>
{
pageViewMode.Value = targetTile.Value;
BuildView();
};
rootElement.Controls.Add(tilesContainer);
}
The problem is that the "OnTileClicked" event works only on the first load and not after the postback. I believe it has something to do with the page lifecycle and registering the events after the Control events ( https://learn.microsoft.com/en-us/previous-versions/aspnet/ms178472(v=vs.100)?redirectedfrom=MSDN ).
If it is really the case, how do I then dynamically build pages from the code behind? Should I really create all the controls ( BuildOverview() and BuildRunsOverview()) and then conditionally show or hide them?
'Should I really create all the controls ( BuildOverview() and BuildRunsOverview()) and then conditionally show or hide them?'
Answer is: yes.
You don't dynamically build pages from code behind - at least its not that well supported in asp.net pages.
In your case you need the TilesContainer on every postback and attach the event handler to it, else the event won't be called. So it would be easier to put all your controls in the markup (.aspx) and just set them to Visible = false/true depending on your code. Controls you set to Visible = false won't be rendered on the client side, so at least no overhead there.
If you use custom-controls (I assume your TilesContainer is a custom-control), then you need to implement the Visible-property the right way, e.g. if your TilesContainers main control is a Panel, override Visible and set the value there:
public override bool Visible
{
get { return base.Visible; }
// set all neccessary controls visibility here
set { this.pnlMain.Visible = base.Visible = value; }
}
I'm trying to perform an operation on every control on a page that is inherited from a masterpage. I don't know how to access the child pages controls. I have tried recursively getting to my controls like this:
private void checkControls(ControlCollection controlcollection)
{
foreach (Control control in controlcollection)
{
if (control.Controls.Count > 0)
{
Debug.WriteLine(control.GetType().ToString());
checkControls(control.Controls);
}
else
{
Debug.WriteLine(control.GetType().ToString());
}
}
The method is called like this:
protected void resettodefault()
{
checkControls(this.Page.Controls);
}
However, the only controls that are printed from this execution are:
ASP.site_master
System.Web.UI.LiteralControl
I would prefer to access my controls directly (without recursion). Otherwise, how can I modify my recursion to get to the desired page's controls?
I would suggest using a base page instead of a master page, this way your logic for iterating over controls (and whatever you will do with that afterwards) is not tied to which master page a page is using.
As far as getting all the controls on the page, because the controls are hierarchical, as is the HTML they represent, so iterating over them recursively makes sense. However if you are dead set on not recursively getting controls something like this should work:
IEnumerable<Control> GetAllControls()
{
var allControls = new List<Control>();
var currentControls = new Queue<Control>();
currentControls.Enqueue(this.Page);
while (currentControls.Count >0)
{
var c = currentControls.Dequeue();
if (!allControls.Contains(c))
{
allControls.Add(c);
if (c.Controls != null && c.Controls.Count > 0)
{
foreach (Control e in c.Controls)
{
currentControls.Enqueue(e);
}
}
}
}
return allControls;
}
The last thing to consider is the lifecycle of the page, and when you iterate over the controls. If you try to walk to control tree too early not all controls may exist.
EDIT: Updated code.
Update
For validation purposes I would highly recommend using the built in validation controls of asp.net. You can read more about them here. This has the added benefit of providing validation on the client, providing faster UI responses and easing the load off the servers.
For resetting all the textboxes. I would recommend creating a new class for this purpose, then calling upon that class when needed instead of messing with the master page:
public class UIControlsHelper
{
public static void ClearTextboxes(Page page)
{
GetAllControls(page)
.Where(x => typeof(TextBox).IsAssignableFrom(x.GetType())
.ToList()
.ForEach(x => (TextBox)x.Text = string.Empty);
}
IEnumerable<Control> GetAllControls(Page page)
// Same as above, but with the page parameter replaced.
}
}
And in any of your pages:
UIControlsHelper.ClearTextboxes(this);
To access the controls in your child page do the following steps:
1-declare a variable of the type you want to access. For example if you want to access a Label in your child page use:
Label lbl_child=this.ContentPlaceHolder1.findcontrol("your label id in child page") as Label;
Now you have your label and you are free to make changes on it. Every change on this control will be reflected on the child control.
ContentPlaceHolder1 is your contentplace holder id so change it with your content id.
public void ClearTextboxes(Page page) {
GetAllControls(page)
.Where(x => typeof(TextBox).IsAssignableFrom(x.GetType()))
.ToList()
.ForEach(x => ((TextBox)x).Enabled=false);
}
I have a user control that is dynamically loaded in the the page load:
protected void Page_Load(object sender, EventArgs e)
{
MyControl ctl = (MyControl)LoadControl(controlPath);
ctl.ID = "mycontrol";
this.MyControlPlaceHolder.Controls.Add(ctl);
}
Front End of the Page:
<asp:PlaceHolder runat="server" ID="MyControlPlaceHolder"></asp:PlaceHolder>
I have an click event on the page that initiates and postback and calls a method where I'm trying to find the control and set some properties:
MyControl ctl = (MyControl)FindControl("mycontrol");
if (ctl != null){
ctl.MyProperty = true;
Response.Write("success");
}
else
Response.Write("fail");
This is writing fail after the postback, so it seems like I'm doing something incorrectly in finding the control. What is the best way to do this?
EDIT:
I switched it to MyControl ctl = (MyControl)this.MyControlPlaceHolder.FindControl("mycontrol");
This made it so it was finding the control, however, when the control loads after the postback, it appears as though the property is not set.
You have to use a recursive FindControl implementation because FindControl will only find direct childs. Your control is inserted in a naming container at a lower level. A generic FindControlRecurisve from MSDN:
private Control FindControlRecursive(Control rootControl, string controlID)
{
if (rootControl.ID == controlID) return rootControl;
foreach (Control controlToSearch in rootControl.Controls)
{
Control controlToReturn =
FindControlRecursive(controlToSearch, controlID);
if (controlToReturn != null) return controlToReturn;
}
return null;
}
from MSDN
Or, if you only have one specific conatiner like in your sample:
MyControl ctl = this.MyControlPlaceHolder.FindControl("mycontrol");
if (ctl != null){
ctl.MyProperty = true;
Response.Write("success");
}
else
Response.Write("fail");
ViewState enable your control
public class MyControl:Control
{
public bool MyProperty
{
get
{
return ViewState["my"] != null? (bool) ViewState["my"]: false;
}
set
{
ViewState["my"] = value;
}
}
}
Try moving the code to dynamically add the control into Init instead of load. I can't be sure but there are a lot of things that happen between Init and Load and if your control is not present and accounted for it may cause issues like this.
You added the Control in the controls collection of a placeholder.
Apart of what control is your dynamically created control, if you want to looking for your dynamically added control in that way you have to do a recursive search starting from the root (maybe the page), so, if you surf over the net, you could find good solutions for that.
Personally I prefer solutions with: generics support and expressed as extension methods, so you could use the solution everywhere. These are some usefull links
Recursive Find Control with generics
Recursive Find Control with generics by extension method
Recursive Find Control with generics by extension method and linq support/example
hope this helps
I have a login control and at is nested 2 deep in a header control
i.e Page --> Header Control --> Login Control. I cannot get a reference to the control on the page using FindControl. I want to be able to set the visible property of the control like
if (_loginControl != null)
_loginControl.Visible = false;
I ended up using a recursive FindControl method to find the nested control.
public static Control FindControlRecursive(Control root, string id)
{
if (root.ID == id)
{
return root;
}
foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, id);
if (t != null)
{
return t;
}
}
return null;
}
Are you needing to disable/hide the User Control from the ASP.NET page it resides on (or does the User Control exist on a master page, say)? If it's in the same page, then in your ASP.NET page's code-behind you'd do:
MyUserControlsID.Visible = false
Where MyUserControl is the ID of your User Control. To determine the ID of your User Control look at the markup of your .aspx page and you will see something like this:
<uc1:UserControlName ID="MyUserControlsID" runat="server" ... />
Happy Programming!
A good way would be to use:
Page.FindControl()
if that yields null, the control is not there.
Try calling this.FindControl("_loginControl") or this.Page.FindControl("_loginControl").
See MSDN for method details:
http://msdn.microsoft.com/en-us/library/system.web.ui.control.findcontrol.aspx
The login control, if it's registered in the markup, will also be an instance member of your codebehind page; you can refer to it from the codebehind class as if it were a normal member, using the same name you provided as the ID (I do recommend using codebehinds for most logic, instead of inlining code in the markup, BTW).
You can also use the FindControl() method of your page, which will search its control subtree for a control with a given ID. That takes longer, so I would recommend the first option unless the logic control is added dynamically and you don't always know it's there.
private List<Control> GetAllNestedUserControl(Control ph)
{
List<Control> Get = new List<Control>();
foreach (var control in ph.Controls)
{
if (control is UserControl)
{
UserControl uc = control as UserControl;
if (uc.HasControls())
{
Get = GetAllNestedUserControl(uc);
}
}
else
{
Control c = (Control)control;
if (!(control is LiteralControl))
{
Get.Add(c);
}
}
}
return Get;
}
just call this code from you any parent page and then get any control by the following code
List<Control> Get = GetAllNestedUserControl(ph);
Label l = (Label)Get.Find(o => o.ID == "lblusername");
l.Text = "changed from master";
I want to show some panel with a label, both located on a MasterPage, from inside it's child pages.. I already did the coding on the MasterPage:
public class MyMaster : MasterPage
{
public void ShowPanel(string pMessage)
{
labelInside.Text = pMessage;
myPanel.visible = true;
}
}
Then I make the calls from child pages:
public void ShowPanel(string pMessage)
{
MyMaster masterPage = this.Master as MyMaster;
masterPage.ShowPanel(pMessage);
}
This "works" ok, but it won't show nothing, since I need the page to be "refreshed" in an "ajax-way" like an UpdatePanel, which I can't use because the Trigger is in another page, right?
I really need this to work.. even if you have another completely different way to do this, I would appreciate.
You must place your panel inside an UpdatePanel(UpdateMode conditional) and in ShowPanel call its Update method.
Have you considered having the masterpage just have a placeholder for the label, but having each child page put its own content label inside that placeholder, which it would then have full control over?
you can subClass your page, and expose a property say.. MyPage.FooVisible
than in your masterPage, you can:
myPage = this.Page as MyPage
if (myPage != null) myPage.FooVisble = false;
in your page you can handle that any way you like,
FooVisible {
set { SomeElement.Visible = value; }
}
pseudo code of course :)