I want to set the masterpage dynamically, But currently I have a base page with Page_PreInit that initializes/performs varioustasks needed for memberpages.
Now I know you could override the base page by putting (new protected void Page_OnInit(..)) in the member page but I don't want to as said the base page has got its job to do.
"this.MasterPageFile ="
Is there any way to set the masterpage after or before the Page_PreInit?
Or utilize both Page PreInt and BasePage PreInt ?
Thanks
EDIT: Rereading your question, I see your problem:
public class BasePage : WebPage {
protected void Page_PreInit(object sender, EventArgs e){
//do stuff here
}
}
public class MyPage : BasePage {
protected void Page_PreInit(object sender, EventArgs e){
//overwrites base class functionality
//Pretty sure you can:
base.Page_PreInit(sender,e);
}
}
Original answer
http://msdn.microsoft.com/en-us/library/c8y19k6h.aspx#sectionToggle1
According to MSDN, you can assign it during Page_PreInit.
Is there a reason to do it before or after Page_PreInit? I'm not entirely sure anything useful comes before pre-init in the page life-cycle anyways.
(source: microsoft.com)
There isn't an earlier hook, except the page constructor, if you could do it there. But you wouldn't have access to any of the page values yet.
Related
I want to execute a function on Page_load event of every System.Web.UI.Page from which derives my own CustomPage class (which obviously inherits from Page class as well)
what I have done so far it that I created CustomPage class like this:
public class CustomPage : System.Web.UI.Page
{
protected virtual void Page_Load(object sender, EventArgs e)
{
CallTOTheDesiredFunction(); //this is the call to the function I want
}
}
And in the derived Page classes I am doing this:
public class DerivedPage : CustomPage
{
protected override void Page_Load(object sender, EventArgs e)
{
base.Page_Load(sender, e);
//the rest of the page load event which executes from here on
}
}
As it is obvious, this approach is working but it is not the best solution since I have to call base.Page_Load(sender, e) on every derived page.
Is there a better solution to what I am trying to achieve?
Thank you in advance
Yes. It is better to override the Onload method rather than relying on deriving classes to call the base method.
You can still hook on the Load event in every page, but use the method in the base class.
public class CustomPage : System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
CallTOTheDesiredFunction(); //this is the call to the function I want
base.OnLoad(e);
}
}
What is the best way to assign security logic to a method in ASP.NET WebForms? Where instead of checking under each method if the user is logged in, can't we use method attributes?
Example, instead of doing this:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (!UserLoggedIn)
{
Response.Redirect("/login");
}
//Do stuff
}
I would like to do something like below. I've seen it done in ASP.NET MVC apps but I wonder if I can pull it off with webforms. And also what would be the best practice for ensuring only an authenticated user can continue and others get redirected to login page?
Ex: Desired. Where Secure is a method attribute:
[Secure]
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
//Do stuff
}
How do I go about creating such method attribute? And if that is not possible, how would you recommend I do it? I have many usercontrols that need this on page_load or oninit and I am looking for a better way to do it.
Declare your attribute
[AttributeUsage(AttributeTargets.Class)]
public class SecureAttribute: Attribute
{
}
Create custom base page class for all forms
public class PageBase: Page
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
var secureAttr = Attribute.GetCustomAttribute(this.GetType(), typeof (SecureAttribute));
if (secureAttr != null)
{
bool UserLoggedIn = false; // get actual state from DB or Session
if (!UserLoggedIn)
{
Response.Redirect("/login");
}
}
}
}
Inherit all your forms from the PageBase
[Secure]
public partial class Profile: PageBase
{
}
Create similar UserControlBase for user controls.
One possible Solution would be a PageBase helper class to avoid check that condition on every single pages on your ASP.NET web forms and just inherits the page-base in your aspx.cs classes. something like the code below:
for example you want to make sure that some web forms are only accessible by Admin users then you could have a AdminPageBase class to check this condition for all of your web pages.
your base class:
public class AdminPageBase : System.Web.UI.Page
{
protected void Page_Init(object sender, EventArgs e)
{
if (!Context.User.Identity.IsAuthenticated ||
!HttpContext.Current.User.IsInRole(Roles.Admin.ToString()))
{
this.RedirectToLogin();
}
}
protected void RedirectToLogin()
{
//...
Response.Redirect("~/SignIn.aspx");
}
}
Note: Roles.Admin.ToString() is an enum, but you can also use a plain string if you like
and in your web form classes you only inherits this base class like this:
e.g. AdminPage1.aspx.cs
public partial class AdminPage1: AdminPageBase
{
//....
}
e.g. AdminPage2.aspx.cs
public partial class AdminPage2: AdminPageBase
{
//....
}
and you could always do the same for all other pages in your solution.
you could also change Page_Init to Page_Load on your PageBase class but the reason I have chosen the Page_Init is because you may need Page_Load event to check other things on your page so it's a good place to check your website security.
In order to intercept method calls, I would recommend utilizing some AOP framework, e.g. PostSharp, which allows easily inject behaviors before and after method execution by declaring custom aspect:
[Serializable]
public class SecureAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
bool UserLoggedIn = false; // get from DB
if (!UserLoggedIn)
{
HttpContext.Current.Response.Redirect("/login");
}
}
}
And applying that attribute to any method
public partial class Profile : Page
{
[Secure]
protected void Page_Load(object sender, EventArgs e)
{
}
[Secure]
protected void Button1_Click(object sender, EventArgs e)
{
}
}
As far as I know, PostSharp incures minor performance hit, or incures no performance hit at all, as PostSharp emits MSIL instructions.
In trying to determine why my app (which came into this brave new world based on thte "Blank App" template) isn't working like I would expect (http://stackoverflow.com/questions/14467756/why-would-my-event-handler-not-get-called), I started a new Blank project, and then deleted MainPage and added a new Basic (not Blank) page which I named MainPage in honor of the dearly departed page (and as a nod to tradition and laziness - so I wouldn't have to change the code in app.xaml.cs which navigates to that page).
The Blank app created the original MainPage.xaml.cs like this (auto-generated comment elided):
namespace AsYouWish
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
}
}
...I replaced it with a BasicPage (not BlankPage), and it generated this:
namespace AsYouWish
{
public sealed partial class MainPage : AsYouWish.Common.LayoutAwarePage
{
public MainPage()
{
this.InitializeComponent();
}
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
}
protected override void SaveState(Dictionary<String, Object> pageState)
{
}
}
So the Basic Page gets LoadState() and SaveState(), whereas the Blank Page's MainPage had OnNavigatedTo(). Why does the Basic Page not also have an OnNavigatedTo() event? It seems as if every page has the possibility of being navigated to (and from, but that event I can see as more likely being optional/unnecessary).
This is just a matter of the page template that is being used. OnNavigatedTo virtual method is implemented in Page class therefore it can be overriden in any class inherited directly or indirectly from it. The only difference is that the template used for MainPage.xaml.cs already has an empty OnNavigatedTo method in it and the BasicPage template doesn't.
There's nothing stopping you from overriding the method by adding the following code:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// add you own code here
}
Just make sure you keep the base.OnNavigatedTo(e) call or you'll lose the functionality that's already implemented in LayoutAwarePage (enabling LoadState/SaveState).
In case you don't know, it's really easy to add overrides to your class in Visual Studio. Just type override and press space and a dropdown will open with all the methods that you can override in your class. Once you select one of them the complete empty method will be added to your class, just like the one I've included in my answer above.
Take the following scenario. I have multiple ASPX pages. Login, Logout, Main, Messages, etc... They all inherit from System.Web.UI.Page of course. For all the pages, I want to override the Render method from the Page class. I could easily copy and paste the same code into each page like so:
protected override void Render(HtmlTextWriter writer)
{
//Code Logic Here
}
But if I had many pages, lets say 20, maintaining the code in each page could get very time consuming and error prone.
That made me think a bit and I thought okay lets try this...override the function in each page but call a static function. That way changing the static function would result in a change for every page. Which works fine... But its not really nice and clean, having to override like that on every single page. Anybody have any ideas or thoughts on this one? Perhaps something simple I am overlooking? Thanks
EDIT: Some pages use the System.Web.UI.Page class and some pages inherit from another class called ModifiedPage which inherits and overridies other functions of the System.Web.UI.Page class. So its not as simple as inheriting all the pages from one class.
EDIT2: All pages need this behavior, some already derive from another class, and I am unable to change the implementation or inheritance hierarchy of that other class.
Instead of inheriting from System.Web.UI.Page, have them all inherit from MyProject.MyBasePage which inherits from Page:
public abstract class MyBasePage : System.Web.UI.Page
{
protected override void Render(HtmlTextWriter writer)
{
//Code Logic Here
}
}
and...
public partial class MySpecificPage : MyBasePage
{
}
Edit
Clarification added to the question now points out the real puzzle - the pages which all need this common Render logic have different inheritance paths. That is more tricky in c#, and you won't be able to avoid at least a little bit of redundant plumbing code. There's plenty of different ways to handle this - here's one approach I have taken in the past:
1) Create an interface for this common functionality. For example, IOverrideRender:
public interface IOverrideRender
{
void Register(OverrideRender render);
}
public delegate void OverrideRender(HtmlTextWriter writer, Action<HtmlTextWriter> original);
2) Each page which needs this functionality gets the interface and wires it like so:
public partial class MyPage : Page, IOverrideRender
{
void IOverrideRender.Register(OverrideRender render)
{
this.overrideRender = render;
}
private OverrideRender overrideRender;
protected override void Render(HtmlTextWriter writer)
{
if(overrideRender != nul)
{
overrideRender(writer, base.Render);
}
else
{
base.Render(writer);
}
}
}
3) In an HttpModule, check to see if the handler is IOverrideRender and if so, pass in your custom render method:
public class OverrideRenderModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += this.HandlePreRequestExecute;
}
private void HandlePreRequestExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
IOverrideRender overridable = app.Context.CurrentHandler as IOverrideRender;
if(overridable != null)
{
overridable.Register(
(writer, original) => {
writer.Write("Hello world"); //custom write
original(writer); //calls base.Render
});
}
}
}
You should create a BasePage which inherits from System.Web.UI.Page. Within the BasePage you could override the Render method and then have all your pages inherit from BasePage.
Add a level in your hierarchy and make a BasePage and do your override there, then inherit all other page from BasePage
I have created a class called BasePage which inherits System.Web.UI.Page. On this page I've declared a property called UserSession which I want to be able to access from any Page/MasterPage.
public class BasePage : System.Web.UI.Page
{
string UserSession { get; set; }
public BasePage()
{
base.PreInit += new EventHandler(BasePage_PreInit);
}
private void BasePage_PreInit(object sender, EventArgs e)
{
UserSession = "12345";
}
}
My Default.aspx.cs page inherits the BasePage class and allows me to access the UserSession property as expected.
public partial class Default : BasePage
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(UserSession);
}
}
However, even though Default.aspx has MasterPage.Master assigned correctly, when I try and access the UserSession property from MasterPage.Master.cs it can't find it and won't build.
So what am I trying to achieve by doing this? I want to expose a UserSession object to every page in my application. Pretty simple you would have thought? Nope.
MasterPage is separate in the heirarchy to Page so you cannot access the property directly from that. However, MasterPage does have a Page property that returns the Page object, which you can cast to your BasePage class. Then as long as UserSession is public (or internal, or protected internal, which might make good sense here) it can access that property. Unless you've only one master page codebehind, you may want to similarly create a BaseMasterPage and do something like:
public BaseMasterPage : MasterPage
{
protected string UserSession
{
get
{
return (Page as BasePage).UserSession;
}
set
{
(Page as BasePage).UserSession = value;
}
}
}
It is simple. Declare your string variable as public.
EDIT: TheGeekYouNeed's answer is correct... I should know better than to try to answer questions this early on a Monday.
I think you'll need to create a base master page class which holds your UserSession property. This would allow you to access the property from the masterpage as well as the pages that inherit from it.