Okay, so we all know about changing a master page dynamically in a page's OnPreInit event.
But what about a nested master page? Can I change a master's master?
There is no OnPreInit event exposed in the MasterPage class.
Any ideas?
Just tested this and it works from the PreInit of the Page that is using the nested MasterPage.
protected void Page_PreInit(object sender, EventArgs e)
{
this.Master.MasterPageFile = "/Site2.Master";
}
Obviously you will need to ensure that the ContentPlaceholderIds are consistent across the pages you are swapping between.
We combine Andy's method with a "BasePage" class - we create a class that inherits from System.Web.UI.Page, and then all our pages inherit from this class.
Then, in our base page class, we can perform the relevant checks to see which root master page should be used - in our case we have a "Presentation" master, and an "Authoring" master - the presentation version has all the navigation and page furniture, along with heavy display CSS, while the authoring master has some extra JS for the authoring tools, lighter CSS, and no navigation (it's what we use when the user is actually authoring a page, rather than modifying the site layout).
This base page can then call Page.Master.MasterPageFile and set it to the Authoring master if that is the correct state for the page.
Just in case anyone stumbles across this and tears their hair out with a "Content controls have to be top-level controls in a content page or a nested master page that references a master page" error when trying Andy's code, get rid of the this.Master. So, the code becomes:
protected void Page_PreInit(object sender, EventArgs e)
{
MasterPageFile = "/Site2.Master";
}
Edit As Zhaph points out below, the code I have ^^ there will only change the current page's master, not the master's master. This is the code Hainesy was talking about when he mentioned "we all know about changing a master page dynamically" (which I didn't, d'oh). If you happen to get to this page by googling "stackoverflow change master page" (which is what I did) then this is possibly the code you're looking for :-)
To add on to the answer of Zhaph - Ben Duguid, (+1 by the way):
Here is example code that sets the master page of the nested master page. All pages inherit from this BasePage, so this code only exists in one place.
public class BasePage : System.Web.UI.Page
{
private void Page_PreInit(object sender, System.EventArgs e)
{
if (Request.Browser.IsMobileDevice)
{
if (Page.MasterPageFile == "~/master/nested.master"))
{
Page.Master.MasterPageFile = "~/master/mobile.master";
}
else
{
MasterPageFile = "~/master/mobile.master";
}
}
}
}
Related
I have an authentication roles-based system: different roles are redirected to different folders and in each folder there is a web.config that allows the access only to a particular username.
Few roles have the default page in common with a gridview that react in different ways depending on the role(different columns are shown, events trigger different methods, etc.).
so my problem is that everytime I need to make minor changes to a page I need to copy/paste the same changes to all the others default pages in the other folders.
In terms of code I solved by creating a DefaultFather class which extends System.Web.UI.Page and every other Default class inherits from DefaultFather. In this way, if I dont declare a Page-life-method, the DefaultFather method will be triggered.
but what about the graphic part(html, javascript, asp components, etc...)??
I created a NestedMasterPage just for the Default pages but everytime I need to change the appearance/behaviour of controls(gridview, buttons, linkbuttons) I must use the FindControl() method.
there isnt really another way to solve this problem?
Im thinking of using the Page_Load() method to search for each control with FindControl() and save them into attributes for later usage but it doesnt really look like a good solution.
It would be nice if I could use the masterpage components as properties but I think that in order to do that I should create public properties and I dont know if it will cause some kind of security problem.
any suggestion?
btw, if masterpage is the solution, should I remove the DefaultFather class and place the code directly into the masterpage? or is it a good idea to have another class just for the code?
I'd say there's nothing wrong with having both a master page and a base class for your page. They serve different purposes. The master page is generally all about layout, and the base class would be about page functionality.
If you want to manipulate the markup on your master page, rather than accessing the fields directly, I'd say create a logical function which does what you need it to do, and let the master page do it.
// Site.Master.cs
public void HideSubmitButton()
{
btnSubmit.Visible = false;
}
// Default.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
((SiteMaster)Master).HideSubmitButton();
}
I'd probably wrap that cast so you can use it more easily - that is something that would belong in your base class:
// DefaultFather.cs
protected new SiteMaster Master { get { return (SiteMaster)base.Master; } }
// Default.aspx.cs
Master.HideSubmitButton();
EDIT
Per your comment about attaching event handlers - if you need to attach events to objects that live on the master (which may not be a good idea - ideally the event handler for something living on the master lives on the master - but if you really need it) you can expose methods to do that as well, like:
// Site.Master.cs
public void AttachEventHandlerToGoButton(EventHandler eventHandler)
{
btnGo.Click += eventHandler;
}
// Default.aspx.cs
Master.AttachEventHandlerToGoButton(DoMyThing);
private void DoMyThing(object sender, EventArgs e) { }
or if you want to get fancy, write a wrapper event:
// Site.Master
<asp:Button ID="btnGo" runat="server" OnClick="btnGo_Click" />
// Site.Master.cs
public event EventHandler GoButtonClick;
protected void btnGo_Click(object sender, EventArgs e) {
if (GoButtonClick != null) {
GoButtonClick(sender, e);
}
}
// Default.aspx.cs
Master.GoButtonClick += DoMyThing;
private void DoMyThing(object sender, EventArgs e) { }
Also see my edit on the Master wrapper - you need the base. there to avoid a stack overflow.
On loading of every content page, I want to get content page name. So I want to know that which Master Page event is fired on every content page load ?
Here's a resource that might help, it states the events that ocur for ASP.NET pages:
http://weblogs.asp.net/ricardoperes/archive/2009/03/08/asp-net-page-events-lifecycle.aspx
Page.OnPreInit
MasterPageControl.OnInit (for each control on the master page)
Control.OnInit (for each contol on the page)
MasterPage.OnInit
Page.OnInit
Page.OnInitComplete
Page.LoadPageStateFromPersistenceMedium
Page.LoadViewState
MasterPage.LoadViewState
Page.OnPreLoad
Page.OnLoad
MasterPage.OnLoad
MasterPageControl.OnLoad (for each control on the master page)
Control.OnLoad (for each control on the page)
OnXXX (control event)
MasterPage.OnBubbleEvent
Page.OnBubbleEvent
Page.OnLoadComplete
Page.OnPreRender
MasterPage.OnPreRender
MasterPageControl.OnPreRender (for each control on the master page)
Control.OnPreRender (for each control on the page)
Page.OnPreRenderComplete
MasterPageControl.SaveControlState (for each control on the master
page)
Control.SaveControlState (for each control on the page)
Page.SaveViewState
MasterPage.SaveViewState
Page.SavePageStateToPersistenceMedium
Page.OnSaveStateComplete
MasterPageControl.OnUnload (for each control on the master page)
Control.OnUnload (for each control on the page)
MasterPage.OnUnload
Page.OnUnload
Also here is the official documentation about ASP.NET page lifecycle which goes into detail about all the events. Hopefully this will help you.
EDIT;
Hmmm, actually the above looks a bit over the top. It looks like all you need to do is - in each content page, make sure you reference the master page in the ASPX file:
<%# MasterType virtualpath="~/Masters/Master1.master" %>
Then in the master page have a public method such as:
public void LogContentPageName(string name)
{
// Do whatever you want with the passed name.
}
Then in the Page_Load event of the content pages you can do:
protected void Page_Load(object sender, Eventargs e)
{
Master.LogContentPageName("Whatever");
}
Please try with below event
protected override void OnInit(EventArgs e) {
//do your stuff here }
I am trying to change the master page dynamically, and although it's easy to do from a content page (overriding OnPreInit), there is no such event for a master page. Is it possible to introduce this event somehow?
UPDATE: I got halfway there by going via the PreInit of the pages at the bottom of the ladder, turns out you can do things like base.Master.MasterPageFile = "/master.Master";, but for some reason this doesn't load the stuff in the header of the top-most master page, namely stylesheets.
Quoting from: Can I change a nested master page's master dynamically?
Just tested this and it works from the PreInit of the Page that is using the nested MasterPage.
protected void Page_PreInit(object sender, EventArgs e)
{
this.Master.MasterPageFile = "/Site2.Master";
}
Obviously you will need to ensure that the ContentPlaceholderIds are consistent across the pages you are swapping between.
If you overrode the MasterPageClass and added your own onPreInit you might could do it, but I don't think even that would work. There's definitely no construct for it according to Reflector, nothing to even override, altho since it inherits UserControl then there's always OnInit ... alternately you could attempt to override get_Master() but that might not work either ...
Use the masterpage constructor.
Let's say you want to use a different master page without a menu, pass query string NoMenu.
protected void Page_PreInit(object sender, EventArgs e)
{
//You'll go through infinite loop if you do not check if we already have the new master page, this will switch to different master page if requested without a menu for example
if (Request.QueryString["NoMenu"] != null && this.MasterPageFile != "/MasterPageNoMenu.master")
{
this.MasterPageFile = "/MasterPageNoMenu.master";
base.OnPreInit(e);
}
}
In my inline .aspx page, I want to override the pre-init event.
How can I do this?
is it:
void pre_init(object sender)
?
Update
This is a user control
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
}
In your page you can start typing "protected override", hit enter or tab to complete, hit space, and intellisense will give a list of all the stuff you can override. If you pick one from the list it will generate the
base.myOverriddenMethod(e);
etc..
Have a look at this MSDN page for an overview of the page life cycle and a description of all events you can handle.
I'm currently migrating a .net 1.1 application to .net 3.5.
The .net 1.1 application has a number of number of page + usercontrol's that I would like migrated to masterpages.
My issue is trying to test progmatically to see if the masterpage's contentplaceholders content has been overridden by a child page.
Is it possible?
Does anyone have samples or references that I could take a look at?
Thanks in advance.
A page can communicate with the master page but not vice versa since the content in the contentplaceholder does not belong to the master page. The quickest way of setting up a page "registering" itself to the master page is to declare a class that inherits from the .NET MasterPage and expose communication functionality in that class.
public abstract class MyMaster : System.Web.UI.MasterPage
{
public MyMaster() { }
public abstract void TellMeSomethingAboutTheContent(SomeArgs args);
}
Then in your page that uses the master you can do something like:
protected void Page_Load(object sender, EventArgs e)
{
MyMaster master = Page.Master as MyMaster;
if (master == null)
return;
master.TellMeSomethingAboutTheContent(args);
}
Assuming of course that you have a SomeArgs class that contains the data you want the master page to know about.