I have the following piece of code:
public abstract class BasePage : Page
{
protected void Page_PreInit(object sender, EventArgs e)
{
if (IsPostBack)
return;
var section = ConfigurationManager.GetSection("UrlRewriter/PlainRules");
if (section == null)
SetMaster("~/App_Shared/Master/BaseRegular.Master");
else
SetMaster("~/App_Shared/Master/BaseRewritable.Master");
}
protected void SetMaster(string value)
{
MasterPage master = Master;
while (master != null)
{
if (master is SharedMaster)
{
master.MasterPageFile = value;
break;
}
master = master.Master;
}
}
}
It works great in changing my master pages dynamically, but I'd want to be able to do this directly from SharedMaster, rather than from every single page I have.
Page_PreInit doesn't ever fire if placed on the master page, so how can I accomplish this?
If you put this functionality in BasePage and then inherit each of your page from BasePage then you don't have to repeat the code in every page. You already seems to have a perfect working code.
As far as putting the logic in master page, it will not be possible - because once master page gets associated with the page and control tree is loaded, you cannot change the master page. The pre_init does not trigger for master page because master page is not loaded till that point and so once can change the master page associated with the page. Then master page is loaded and a composite control tree gets created and after that you will receive master page events.
Related
I'm trying to add a Link to a webform page from a user control. The CSS file contains the styling used by the control. When I try, I get the following exception:
The control collection cannot be modified during DataBind, Init, Load,
PreRender or Unload phase
So, when can anything be added to the controls collection? Seems to me the exception message suggests that all options are off the table. Here's the code:
public partial class AddNoteDlg : UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
RegisterFiles();
}
private void RegisterFiles()
{
Page.ClientScript.RegisterClientScriptInclude(GetType(), "addNoteDlgComponent-1.0.0.js", ResolveUrl("~/Shared/scripts/js/addNoteDlgComponent-1.0.0.js"));
var css = new HtmlLink();
css.Href = ResolveUrl("~/Shared/css/addNoteDlg-0.0.1.css");
css.Attributes["rel"] = "stylesheet";
css.Attributes["type"] = "text/css";
css.Attributes["media"] = "all";
Page.Controls.Add(css);
}
}
Instead, stuck the link tag in the user control markup, as there is no difference, practically speaking.
(The original motivation for doing it programmatically was so I could add it to the header, with Page.Header.Controls.Add(css), allowing me to keep all CSS links in the header.)
I have a master page with a placeholder control.
I have a page which inherits from master page.
I then have a custom control which is displayed on the page, this custom control has a cast to the masterpage so I can access the placeholder control to turn visibility on and off. Everything works as expected when I watch it in the debugger, but the placeholder control fails to turn it's visibility off.
I feel this has something to do in the order in which the events are firing. It appears any code in the custom control on the page is firing after the masterpage has already rendered.
Does anybody have any idea how I can affect the way this page is rendered so the custom control can turn the placeholder and on and off?
the code in the control looks like this;
protected override void Render(HtmlTextWriter writer)
{
var master = this.Page.Master as Site;
if (master != null) // cast failed, your master is a different type
{
master.NavBar.Visible = false;
}
// other stuff
}
Include the MasterType tag in your page, so that you dont need to typecase the page.Master, directly you can get the Master instance.
The solution to this problem was the order in which the control, masterpage and page were being fired, it was ignoring the setting on the custom control. The solution is to add this functionality to the custom control on the OnPreRender(EventArgs e) method.
protected override void OnPreRender(EventArgs e)
{
var master = this.Page.Master as Site;
if (master != null) // cast failed, your master is a different type
{
var progressShown = master.FindControl("ProgressShown");
if (progressShown != null)
{
master.NavBar.Attributes.Add("class", "test");
}
}
base.OnPreRender(e);
}
I have a user control in master page and I need to get content page's name (for example Home.aspx) in user control. Unfortunately I am not getting how to do it. Please guide and help me.
In Page_Load() Event of MasterPage.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string strPage = Page.AppRelativeVirtualPath;
}
}
It will return the child page's virtual path.
Ex: ~/Default.aspx
Try using Page.AppRelativeVirtualPath property - you can extract the page file name from the virtual path.
You can use Page.Title property.Use it in the page load function
string Title = Page.Title;
Add a property (say CurrentPage) in the master or in the base page that returns the current page and refer this from the User Control using
var page ='<%=CurrentPage%>'
First let me clear the air and post articles which already explain how to override the SaveButton:
How to override or customize the Sharepoint SaveButton?
How to override functionality of the Ribbon Save button
Override SharePoint Save Button to Provide Custom Functionality
I have read those and understood them, I just don't know how to fully implement it in my particular case:
I have a custom rendering template "CustomRender" which includes the "real form". The code for the real form looks something around these lines:
<%# Register TagPrefix="wssuc" TagName="ToolBar"
src="~/_controltemplates/ToolBar.ascx" %>
<%# Control Language="C#" AutoEventWireup="true"
CodeBehind="RealForm.ascx.cs" Inherits="CustomNameSpace.CustomForm" %>
<p>Test</p>
<wssuc:ToolBar runat="server" id="toolbar">
<TemplateButtons>
<SharePoint:SaveButton runat="server" />
</TemplateButtons>
</wssuc:ToolBar>
Now I want to override this save button. The sites above state that I just have to write another control which overrides the button. E.g.:
public class NewSaveButton: SaveButton
{
protected override bool SaveItem()
{
bool success = base.SaveItem();
RedirectUrl = String.Concat(List.ParentWeb.ServerRelativeUrl, "/",
List.Forms[PAGETYPE.PAGE_DISPLAYFORM].Url, #"?ID=",
ListItem.ID, #"&Source=", ListItem.ParentList.DefaultViewUrl);
return success;
}
}
Now I just don't know how to register this template inside my other template. Could I not just override the SaveButton in the Code behind of my template - how would I do that and reference it later on?
Option one: Code-Behind of the form (RealForm.ascx.cs) - can I just put the override method in there? How can I reference the button then in the form (how do I get <NewSaveButton>)?
Option two: Another template just for the button, e.g. SaveButton.ascx" - how do I reference that via <%# Register... %>, i.e. how do I know PublicKeyToken etc. when deployed via a Feature. And same thing here: My goal is to get some kind of "<NewSaveButton>" control for the form.
You're creating a new server control when you do this, so you'll need to register the new control on the page (or in this case, in the template .ascx file).
<%# Register TagPrefix="MyPrefix" Namespace="ControlNamespace" Assembly="MyFullyQualifiedAssembly" %>
In your code file you can to add the ToolboxDataAttribute to the class (this is only necessary if you are dragging&dropping the control from the toolbox in visual studio)
[ToolboxData("<{0}:NewSaveButton runat=\"server\"></{0}:NewSaveButton>")]
public class NewSaveButton : SaveButton {}
Now, you should be able to replace the save button on the form with the following:
<MyPrefix:NewSaveButton runat="server"></MyPrefix:NewSaveButton>
You're basically creating a new server control following the rules of asp.net (no sharepoint specific stuff is happening here).
For more information, take a look at this page: http://msdn.microsoft.com/en-us/library/yhzc935f(v=VS.85).aspx
On your page with SaveButton you could do the following trick (in my case save button is added in DataFormWebPart's XSL markup):
// On your page with SaveButton you could do the following trick
// (in my case save button is added in DataFormWebPart's XSL markup):
SPContext itemContext;
DataFormWebPart dataForm; // from designer's code behind
void Page_Init(object sender, EventArgs e)
{
// NOTE: by some reason ItemContexts of controls in DFWP are differ,
// so only SaveButton's OnSaveHandler is invoked
itemContext = dataForm.Controls.FindControlRecursive<SaveButton>().ItemContext;
}
void Page_Load(object sender, EventArgs e)
{
if (itemContext.FormContext.FormMode == SPControlMode.New ||
itemContext.FormContext.FormMode == SPControlMode.Edit)
{
itemContext.FormContext.OnSaveHandler += OnSaveHandler;
}
}
void OnSaveHandler(object sender, EventArgs eventArgs)
{
// TODO: Add your code before saving the item
SaveButton.SaveItem(saveButton.ItemContext, false, string.Empty);
// TODO: Add your code after saving the item
}
The FindControlRecursive() extension implementation is
public static class ControlExtensions
{
public static TControl FindControlRecursive<TControl>
(
this ControlCollection controls
) where TControl : Control
{
if (controls != null)
{
foreach (Control control in controls)
{
var foundControl = control as TControl
?? control.Controls.FindControlRecursive();
if (foundControl != null)
{
return foundControl;
}
}
}
return null;
}
}
Trying to set the value of a literal user control on my child master page via the code behind of the same master page.
Here is example of the code I am using:
Global.master
<form id="form1" runat="server">
<div>
<asp:ContentPlaceHolder id="GlobalContentPlaceHolderBody" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
Template.master (child of Global.master)
<asp:Content ID="TemplateContentBody" ContentPlaceHolderID="GlobalContentPlaceHolderBody" Runat="Server">
<asp:Literal ID="MyLiteral1" runat="Server"></asp:Literal>
<p>This is template sample content!</p>
<asp:ContentPlaceHolder ID="TemplateContentPlaceHolderBody" runat="server">
</asp:ContentPlaceHolder>
Template.master.cs
protected void Page_Load(object sender, EventArgs e)
{
MyLiteral1.Text = "Test";
}
ContentPage.aspx
< asp:Content ID="ContentBody" ContentPlaceHolderID="TemplateContentPlaceHolderBody" Runat="Server">
</asp:Content>
Once I am able to achieve this, I will also need to be able to access content on the global and template master pages via content pages.
If I understand your scenario you want to have your content pages access items from your master pages. If so, you'll need to setup a property to expose them from your master page, and in your content page you can setup a MasterType directive.
Take a look at this post for an example.
Found a working solution. In the template.master (nested child master), I had to put the code in OnLoad event.
protected override void OnLoad(EventArgs e)
{
MyLiteral1.Text= "<p>MyLiteral1 Successfully updated from nested template!</p>";
base.OnLoad(e);
}
Very strange...
Basically, I am using the global master as the page that has code shared on every page, then I will have various nested pages to suit each website section. For the navigation nested template, I want to be able to show if the user is logged in and how many items in shopping cart.
If there is a better way to achieve this, I am open to suggestions.
EDIT: This is my answer when I thought you were trying to access a control on the child master from the parent master code behind.
You can use a recursive findControl function:
protected Control FindControlRecursive(string id, Control parent)
{
// If parent is the control we're looking for, return it
if (string.Compare(parent.ID, id, true) == 0)
return parent;
// Search through children
foreach (Control child in parent.Controls)
{
Control match = FindControlRecursive(id, child);
if (match != null)
return match;
}
// If we reach here then no control with id was found
return null;
}
Then use this code in your master page:
protected void Page_Load(object sender, EventArgs e)
{
//EDIT: if GlobalContentPlaceHolderBody isn't visible here, use this instead:
//Control c = FindControlRecursive("MyLiteral1", Page.FindControl("GlobalContentPlaceHolderBody"));
Control c = FindControlRecursive("MyLiteral1", GlobalContentPlaceHolderBody);
if(c != null)
((Literal)c).Text = "Test";
}