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.
Related
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.
void RecordButton_Click(object sender, EventArgs e)
{
NavigationService.Navigate(new Uri("/RecordAudio.xaml",UriKind.Relative));
}
In the above code , is NavigationService a class or is it an object of type NavigationService ? .
if it is a class . then is Navigate() a static method ?
and
if it is an object . Why have we not instantiated NavigateService class using the new operator ?
From what i know: The NavigationService class "belongs" to the recent page you're on and it is never used "on its own". You use its methods instead, like with navigate. The page gets informed when sth. new is to display.
As #VahidNd said - use F12 (if you use VS). To clarify it's like this:
PhoneApplicationPage (which you use) base class is Page:
public class PhoneApplicationPage : Page
{
// content
}
public class Page : UserControl
{
public NavigationCacheMode NavigationCacheMode { get; internal set; }
public NavigationContext NavigationContext { get; }
public NavigationService NavigationService { get; }
public string Title { get; set; }
protected virtual void OnFragmentNavigation(FragmentNavigationEventArgs e);
protected virtual void OnNavigatedFrom(NavigationEventArgs e);
protected virtual void OnNavigatedTo(NavigationEventArgs e);
protected virtual void OnNavigatingFrom(NavigatingCancelEventArgs e);
}
And you can see that it has a property NavigationService of type NavigationService which is a clss in System.Windows.Navigation.
You can see these dependences when you use this F12 key - just set cursor's position on type/property/what you want, and hit the key.
NavigationService is a non static class and so is its method Navigate. And as the method is non static, you need to create an object of the class. But the class NavigationService gets instantiated automatically when the app is run and gets attached as a property to the Frame object and hence available commonly to all the Page Object in a single frame.
refer
For Understanding The Difference between Page Frame Content and Role of the class NavigationService.
Link to a related question One
Link to a related question Two
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.
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.