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 :)
Related
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.
I have a user control .Is there some way to get the page in which usercontrol is available ?
In your usercontrol write this method
protected void MyMethod()
{
Page myParent = this.Page;
...
}
You want the Page property.
If you need to write back to a page of a certain type, you'll have to cast:
var myUserPage = Page as MyCustomUserPageClass;
if (myUserPage != null) {
myUserPage.Foo = "bar";
}
I have a nested master page that has its own master page. The parent master page has a property defined in its code behind.
Public ReadOnly Property SelectedPage() As String
Get
Return _selectedPage
End Get
End Property
How can I reference the parent master page's property from within either the child master page's code behind Page_Load or aspx template page?
VB.Net:
DirectCast(Master, MyMastPageType).SelectedPage
C#:
((MyMastPageType)Master).SelectedPage
http://msdn.microsoft.com/en-us/library/system.web.ui.masterpage.master.aspx
protected void Page_Load(object sender, EventArgs e)
{
MyDemoMaster m = Master as MyDemoMaster;
m.MyProperty = "My button text";
}
See:
How to access controls inside a
nested master page?
The right way of accessing Master
page properties from a child page
Like this:
DirectCast(MyMastPageType, Master).SelectedPage
Here is how I use
MasterPage tmp = this.Master;
while (tmp.Master != null)
{
tmp = tmp.Master;
}
I have a master page which is nested 2 levels. It has a master page, and that master page has a master page.
When I stick controls in a ContentPlaceHolder with the name "bcr" - I have to find the controls like so:
Label lblName =(Label)Master.Master.FindControl("bcr").FindControl("bcr").FindControl("Conditional1").FindControl("ctl03").FindControl("lblName");
Am I totally lost? Or is this how it needs to be done?
I am about to work with a MultiView, which is inside of a conditional content control. So if I want to change the view I have to get a reference to that control right? Getting that reference is going to be even nastier! Is there a better way?
Thanks
Finding controls is a pain, and I've been using this method which I got from the CodingHorror blog quite a while ago, with a single modification that returns null if an empty id is passed in.
/// <summary>
/// Recursive FindControl method, to search a control and all child
/// controls for a control with the specified ID.
/// </summary>
/// <returns>Control if found or null</returns>
public static Control FindControlRecursive(Control root, string id)
{
if (id == string.Empty)
return null;
if (root.ID == id)
return root;
foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, id);
if (t != null)
{
return t;
}
}
return null;
}
In your case, I think you'd need the following:
Label lblName = (Label) FindControlRecursive(Page, "lblName");
Using this method is generally much more convenient, as you don't need to know exactly where the control resides to find it (assuming you know the ID, of course), though if you have nested controls with the same name, you'll probably get some strange behavior, so that might be something to watch out for.
Firstly, you should know that MasterPages actually sit inside Pages. So much so that a MasterPage's Load event is actually called after your ASPX's Load event.
This means, the Page object is actually the highest control in the control hierarchy.
So, knowing this, the best way to find any control in such a nested environment, is to write a recursive function that loops through every control and child controls until it finds the one you're looking for. in this case, your MasterPages are actually child controls of the main Page control.
You get to the main Page object from inside any control like this:
C#:
this.Page;
VB.NET
Me.Page
I find that usually, the Control's class FindControl() method is pretty useless, as the enviroment is always nested.
Because if this, I've decided to use .NET's 3.5 new Extension features to extend the Control class.
By using the code below (VB.NET), in say, your AppCode folder, all your controls will now peform a recursive find by calling FindByControlID()
Public Module ControlExtensions
<System.Runtime.CompilerServices.Extension()> _
Public Function FindControlByID(ByRef SourceControl As Control, ByRef ControlID As String) As Control
If Not String.IsNullOrEmpty(ControlID) Then
Return FindControlHelper(Of Control)(SourceControl.Controls, ControlID)
Else
Return Nothing
End If
End Function
Private Function FindControlHelper(Of GenericControlType)(ByVal ConCol As ControlCollection, ByRef ControlID As String) As Control
Dim RetControl As Control
For Each Con As Control In ConCol
If ControlID IsNot Nothing Then
If Con.ID = ControlID Then
Return Con
End If
Else
If TypeOf Con Is GenericControlType Then
Return Con
End If
End If
If Con.HasControls Then
If ControlID IsNot Nothing Then
RetControl = FindControlByID(Con, ControlID)
Else
RetControl = FindControlByType(Of GenericControlType)(Con)
End If
If RetControl IsNot Nothing Then
Return RetControl
End If
End If
Next
Return Nothing
End Function
End Module
Although I love recursion, and agree with andy and Mun, one other approach you may want to consider is to have a strongly typed Master page. All you have to do is add one directive in your aspx page.
Instead of accessing a page's control from your master page, consider accessing a control in your master page from the page itself. This approach makes a lot of sense when you have a header label on your master page, and want to set its value from each page that uses the master.
I'm not 100% sure, but I think this would be simpler technique with nested master pages, as you would just point the VirtualPath to the master containing the control you wish to access. It might prove to be tricky though if you want to access two controls, one in each respective master page.
Here is a code that is more generic and works with a custom condition (that can be a lambda expression!)
Call:
Control founded = parent.FindControl(c => c.ID == "youdId", true);
Control extension
public static class ControlExtensions
{
public static Control FindControl(this Control parent, Func<Control, bool> condition, bool recurse)
{
Control founded = null;
Func<Control, bool> search = null;
search = c => c != parent && condition(c) ? (founded = c) != null :
recurse ? c.Controls.FirstOrDefault(search) != null :
(founded = c.Controls.FirstOrDefault(condition)) != null;
search(parent);
return founded;
}
}
I have used the <%# MasterType VirtualPath="~/MyMaster.master" %> method. I have a property in the main master page then in the detail master page other property with the same name calling the main master property and it works fine.
I have this in the main master page
public string MensajeErrorString
{
set
{
if (value != string.Empty)
{
MensajeError.Visible = true;
MensajeError.InnerHtml = value;
}
else
MensajeError.Visible = false;
}
}
this is just a div element that have to show an error message. I would like to use this same property in the pages with the detail master page(this is nested with the main master).
Then in the detail master I have this
public string MensajeErrorString
{
set
{
Master.MensajeErrorString = value;
}
}
Im calling the main master property from the detail master to create the same behavior.
I just got it working perfectly.
In contentpage.aspx, I wrote the following:
If Master.Master.connectsession.IsConnected Then
my coded comes in here
End If
I've encountered an odd problem that doesn't make any sense to me. I am trying to dynamically set up MasterPage Content controls on a page. I have it working nicely with the following code:
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
MasterPageFile = "~/MasterPages/Default.master";
string existantContentPlaceHolderID = "ContentPlaceHolder1";
string nonExistantContentPlaceHolderID = "foo";
//Control c = Master.FindControl(existantContentPlaceHolderID);
//Control c1 = Master.FindControl(nonExistantContentPlaceHolderID);
TextBox t = new TextBox
{
Text = "Text"
};
ITemplate iTemplate = new GenericITemplate(container => container.Controls.Add(t));
AddContentTemplate(existantContentPlaceHolderID, iTemplate);
}
public delegate void InstantiateTemplateDelegate(Control container);
public class GenericITemplate : ITemplate
{
private readonly InstantiateTemplateDelegate m_instantiateTemplate;
public void InstantiateIn(Control container)
{
m_instantiateTemplate(container);
}
public GenericITemplate(InstantiateTemplateDelegate instantiateTemplate)
{
m_instantiateTemplate = instantiateTemplate;
}
}
This works great, except I want to be able to double-check that the contentPlaceHolderIDs exist on the MasterPage before calling AddContentTemplate as the Page will throw an error if you add a Content control that points to a non-existing ContentPlaceHolder.
The problem I am having is that in the above example when I call one of the commented Master.FindControl lines, the TextBox no longer renders.
Does anyone have any ideas why this might be... I cannot makes heads or tails of what is going on.
Thanks,
Max
The problem is that AddContentTemplate just records its parameters in a hashtable ready to be combined with the master page instance when it is created. Calling it after the master page has been created won't do anything, and reading the Master property causes the master page to be created.
The best way I can see around this is to create a separate instance of the master page with LoadControl, which you can inspect without affecting the page's own Master property...
MasterPage testMaster = (MasterPage) LoadControl( MasterPageFile );
Control c = testMaster.FindControl(existantContentPlaceHolderID);
There's some overhead in creating a second instance, but it's not immediately obvious to me whether it will be worth worrying about.