extending ASP.NET Page using MasterPage attributes as properties - asp.net

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.

Related

Base class and drived pages load event

My aspx pages pages are drived from a base class, my base class creates the dynamic header of my website. I have a method called "genMenu" and i call in within pageLoad event of base class to create menu.
In some pages i need to override this method, however as we know page load of base class fires before the pageLoad of drived pages. So i cant really do that.
So how can i override the genMenu in aspx page and NoT fire it within base class. I know i can manually call the genMenu function in all drived pages, and not call it within pageLoad of base class, but there has to be a better way of handling it.
Create a virtual method called GenerateMenu or something like that, and always call it from the base page. Do the actual work there. Then your derived pages that need to intercept it can override that instead.
E.g., something like this:
// Base page
protected SomeType MenuVariable;
protected void Page_Load(object sender, EventArgs e)
{
this.GenerateMenu();
}
protected override void OnInit(EventArgs e) {
this.GetMenuVariable();
}
private void GetMenuVariable() {
// Some stuff with query string or the database
this.MenuVariable = FooBar();
}
protected virtual void GenerateMenu() {
// Generate the menu here
}
And:
// Derived page
protected override void GenerateMenu() {
// Calls from the base page Load event
// will be intercepted by this override
// Do the alternate work for this page
// here
}
Updated based on clarification:
In your base class, define a virtual method called AutoCallGenMenu(), which returns true by default. In your page load in the base class, check the result of that method, if it returns true, call genMenu().
In the pages you want to manually call genMenu(), override AutoCallGenMenu() to return false. Then, just manually call the method where you need it.
Instead of pageLoad, why don't you use pageInit or page_init to call your method, so that it occurs before page_load?
Refer to the. Net page life cycle for order of execution, you can then decide which method is best to use
http://msdn.microsoft.com/en-us/library/ms178472(v=vs.100).aspx
I may have misunderstood your question, but tou could also override your genmenu and ignore thr call to .base()

How to dynamically change master page's master page?

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);
}
}

How to access an usercontrol from the code behind?

I have an usercontrol with fck editor for allowing user to add note in my aspx page, which allows the user all kind of text formatting.My need is that I have to access user control from the code behind and collect the content as the same format some one has entered in the fck editor.How can I do this?
I'll elaborate on Brian's answer. You need to expose the content from the UserControl by adding a public property:
public string Content
{
get
{
return editor.Content;
}
}
Then to get the content from the user control, just called the property from the Page's code beind:
protected void Page_Load(object sender, EventArgs e)
{
string content = this.UserControl1.Content;
}
In the page, the page code-behind should be able to use it directly like:
this.uc1.<uc code-behind properties or methods>
So you can expose things from your user control by adding public properties or methods.

Where to register RegisterClientScriptBlock in ASP.NET?

Where is the best part of asp.net page or code behind to register RegisterClientScriptBlock.
You have a bunch of options.
Register script includes in your <head> section or do inline <script> tags. I prefer to have my scripts at the bottom of the page though.
You can also register it at the Page level in your Page_Load (or any other event) by calling ClientScript.RegisterClientScriptBlock and passing it the script you want. Remember that if you do go with RegisterClientScriptBlock, you will need to make sure that you register the code with every page load so that is why I would recommend the Page_Load event if you want to use this method.
For example:
protected void Page_Load(object sender, EventArgs e)
{
AddClientSideJavascript();
// Do other stuff
}
private void AddClientSideJavascript()
{
// Register some client script code
Type someType = this.GetType();
if (!ClientScript.IsClientScriptBlockRegistered(someType, "TESTSCRIPT"))
{
string script = "function ShowAlert() { alert('Test'); }";
ClientScript.RegisterClientScriptBlock(someType, "TESTSCRIPT", script, true);
}
// Register more here... etc...
}
Just make sure you don't include it the portion of your Page_Load that is wrapped with the if (!IsPostBack) check or else your scripts will not get registered after any postbacks.
The correct answer is - at any point within page_load.

ASP.net Server controls: How to handle ID of dynamic controls [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
.Net Changes the element IDs
I have the following problem (I'll explain it simplified because otherwise it would get too complicated/long).
I need to create an ASP.net server control that is inherited from Panel. Simple enough. When the custom control renders, it should dynamically create a Button, associate an event handler to it (which will also be defined inside the server control) and add this button to the rendered control. The click event handler defined inside the server control does some job which for the moment isn't interesting.
I already coded an example and that works fine. In the constructor of the server control I create a new button control, give it an ID, associate an event handler, on the OnInit of the server control I add the button to the Panel controls (remember, my control inherits from Panel) and then everything gets rendered. This looks something like the following:
public class MyCustomControl: Panel
{
private Button myButton;
public MyCustomControl()
{
myButton = new Button();
myButton.ID = "btnTest";
myButton.Click += new EventHandler(btnTest_Click);
}
protected void btnTest_Click(object sender, EventArgs e)
{
//do something...
}
//...
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
this.Controls.AddAt(0, myButton);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
//...
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
}
protected override void Render(HtmlTextWriter output)
{
base.RenderContents(output);
}
}
This works quite fine. The button gets rendered correctly and when I click on it, the appropriate handler inside this customer server control is invoked and executed. Now the problem however starts when I would like to add multiple instances of this server control to my page because all of my generated buttons will have the same id "btnTest" which results in a conflict. So what I tried is to associate a random number to the id of the button, s.t. it end up being "btnTest1235512" or something similar. This solves my problem with the multiple IDs, but results in the problem that my event handler when clicking on the button is no more called correctly. I guess this is due to the problem that my button always gets another id when entering in the constructor and so the appropriate callback (btnTest_Click event handler) isn't found.
Can someone give me a suggestion how I could handle the problem. Is there some way for remembering the ID of the button and re-associating it. As far as I know however this has to happen in the OnInit, where the ViewState isn't yet available. So storing the id in the ViewState wouldn't work.
Any suggestions??
Implementing INamingContainer should fix the problem. You can then keep naming all your buttons btnTest.
This is just a marker interface so you don't have to implement any methods or properties.
Inherit from INamingContainer like:
public class MyCustomControl : Panel, INamingContainer
{
}
This is a marker interface and requires no implementation. I just verified with your code and it solves your problem.
Implementing INamingContainer will do it for you. It will automatically name the buttons with unique ID's. Just a note, if you need to use any of those buttons in any JavaScript, you'll need to use the ClientID property:
function getButton() {
var myButton = document.getElementById('<%=btnTest.ClientID %>');
}

Resources