Dynamically changing Master Template in ASP.NET MVC - asp.net-mvc

I have the requirement to support different Master pages on my application (ASP.NET MVC).
What is the recommended way to:
Pass the master page name to the view from.
Store the master page (in session, or something) so it sticks during a user's visit.

Use a custom base controller and inherit from it instead:
Public Class CustomBaseController
Inherits System.Web.Mvc.Controller
Protected Overrides Function View(ByVal viewName As String, ByVal masterName As String, ByVal model As Object) As System.Web.Mvc.ViewResult
Return MyBase.View(viewName, Session("MasterPage"), model)
End Function
End Class
I set my Session variable in the global.asax Session_Start:
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
//programming to figure out your session
Session("MasterPage")="MyMasterPage"
End Sub

you could throw the master page name into the session, but sessions are unreliable. i'd recommend throwing it in a db instead.
once you're in the page, you can change/set the master page by accessing page.masterpagefile. it's a string; just pass the .master name in.

Why not keep the Master Page on the user profile?
Then just change it on the PreLoad event.
http://www.odetocode.com/articles/440.aspx

Related

Handling FormView databinding event on MasterPage from ContentPage

Hoping this is simple. My goal is to have my masterpage carry a formview which can bind data retrieved based on variable held in my content page, or in the alternative based on variable held as a session variable. As a test, I set up a masterpage including a formview id=testerFV. There is also a label on page id=receiveno. From the masterpage I made a public property:
Public ReadOnly Property testerFVx() As FormView
Get
Return Me.testerFV
End Get
End Property
Similarly, a public property for the receiveno label.
I added <%# MasterType VirtualPath="~/masterpages/testmaster.master" %> to my content page. On my content page, I included a button titled testit. Backcode for the testit button is this:
Protected Sub testit_Click(sender As Object, e As System.EventArgs) Handles testit.Click
Dim tester1FVx As FormView = Master.testerFVx
If tester1FVx IsNot Nothing Then
Master.receivenox.Text = "testerfvx is not nothing"
Else
Master.receivenox.Text = "testerfvx is nothing"
End If
End Sub
The label displays "testerfvx is nothing" so apparently the receiveno label is findable in the masterpage through the public property, but I'm not finding the formview.
In another quick test, I tried binding the formview on the masterpage on the page load with a dummy collection like this:
Protected Sub page_load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim thisthing As New List(Of fakenumbers)
Dim thisone As New fakenumbers
thisone.Thisnumber = 42
thisthing.Add(thisone)
testerFV.DataSource = thisthing
testerFV.DataBind()
End Sub
But I get an object not found error.
So: bottom line, how do I access a formview located on a masterpage from an event happening on my content page and using variables currently held on the content page. Also, if I include a testerFV_databound handler in the master page, will it respond correctly when I've triggered the databinding event from my content page?
Any help would be appreciated. Thanks!

Check session on every page

I'm new in mvc and i try to figured out something.
I have intranet application with login page. I put some stuff in Session and in masterpage on page init events i check
If Page.User.Identity.IsAuthenticated Then
If Session("someThing") Is Nothing Then Me.SetupSession()
End If
This is from web forms, and this is for all page.
How can I do same stuff in MVC 3.
you can use an Action filter to secure the action method on your controller
if you have a base controller just add the below attribute otherwise you need to add this attribute on all the controller you want to secure
[Authorize]
public class SomeController : DefaultController
{
public ActionResult SomeAction(){
}
}
this attribute allow you to specify a message as well
[Authorize(Message = "Access to the blah blah function requires login. Please login or create an account")]
for more info:
http://www.asp.net/mvc/tutorials/authenticating-users-with-forms-authentication-cs
Depending on where you need this, you may write a Global filter to set up the session, or do it in the controller's Initialize() method.

MVC2 Post using sub-classes

I'm building one page to edit various product types. Each product type has a view model (TentProductVM, BootProductVM) that inherits from ProductVM. My MVC2 View checks the model type and adds fields as appropriate. For example, if the model is of type BootProductVM I call Html.TextBoxFor to add a field for the boot's foot size. Page displays fine.
The problem is the post. I've declared a function (in VB) as follows:
<HttpPost()>Function Edit(byval prod as ProductVM) As ActionResult
Of course, this function only receives the form data from the base class ProductVM. So instead I added a function for each product type:
<HttpPost()>Function EditTent(byval prod as TentProductVM) As ActionResult
<HttpPost()>Function EditBoot(byval prod as BootProductVM) As ActionResult
and point the View to the appropriate post function:
Using Html.BeginForm("Edit" & Model.ObjectTypeName, "Catalog")
However, when EditTent or EditBoot gets called, the prod parameter only contains data from the base class. All the fields declared in the subclass view models are left at default values.
What am I doing wrong? Or is there a better approach? (The obvious solution is multiple pages but, since ProductVM has many fields relative to the subclasses, I'd rather not.)
After much experimentation, I've decided not to use this approach. First, I couldn't get it to work without resorting to having an Action parameter of type FormCollection. Second, the obvious solution I discarded is appealing if I use a partial view. The partial view has all the fields associated with the base class (ProductVM), leaving only the fields associated with the derived classes (TentProductVM, BootProductVM) in the regular views.
Felt like I was fighting against the MVC auto-magic, which is never the right approach.
The thing to remember about MVC is that it's based on the "Convention over Configuration" mindset. So if you're passing a strongly typed class class instance to your action method, it expects it to be named "model".
Try changing your declarations to look like this:
<HttpPost()> Function EditTent(byval model as TentProductVM) As ActionResult
<HttpPost()> Function EditBoot(byval model as BootProductVM) As ActionResult
The other (less ideal) option would be to expect a FormCollection object in your action method.
<HttpPost()> Function EditTent(byval form as FormCollection) as ActionResult
Update
Just updating to include some of the discussion points below... In order to post a strongly typed object to a controller action method, the types need to match up.
Assuming your controller's action method looks like this:
<HttpPost()> Function EditTent(byval model as ProductVM) As ActionResult
Your view should be typed accordingly:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Your.Namespace.ProductVM>" %>

ASP.net MVC - request-scoped global variable

I have a value which I want to be vaild during a single request. I am not using Session, as this would make the value global for the entire navigation session.
So I have put thie value in a static field of a class. Great, but then I discovered that such fields are even more global, that is, they stay set for the entire application! This means that there could be random interaction among navigation sessions.
So the question is: is there a safe place I can put a global variable, which will be
global throughout the request
reset after the request is completed
not affected by any other request, either of the same user or by other users
Thanks
Palantir
EDIT
I'll elaborate. I have a piece of code in my master page, which I need to hide on certain conditions, of which I am aware in the controller only. I thought about setting a static variable in the controller, which then would be queried by the master page, but now I see there could be a better way...
Use HttpContext.Items - a per-request cache store. Check out this article on 4guysfromrolla for more details.
It should work fine in ASP.NET MVC. You may wish to derive your master page from a base class (either via code-behind or using the Inherits directive) and have a protected method on the base class that inspects HttpContext.Items and returns, e.g. true/false depending whether you want to display the conditional code.
TempData lasts until the next request as already noted.
But there are also two other dictionaries scoped to the single request.
{Controller,ViewPage}.ViewData
Context.Items
To communicate from controller to (master) page ViewData is probably the better choice.
Two approaches come to mind:
Create a base controller where you set this variable, and then have all your controllers inherit from that.
Use TempData - the problem here being that it sticks around for the next request. But maybe knowing that, you can work around it by using a GUID key to determine that you are, in fact, getting a new value when you need it.
I would probably go with 1).
The common way to access data in a MasterPage that is set in Controller (Action) is via ViewData["TheDataKey"] = "SomeValue".
This is relatively easy and there are a couple of ways that you can do it - depending on how your site works.
I'm interpreting your request as that you want a property or variable that exists for the duration of the request and is visible to the controller, model and master.
A static property is visible to the current application in ASP this means a load of users connecting at once, but not necessarily all of them. IIS will spawn new ASP applications as it needs to.
So the ways you can do this:
You can have a custom base class for your master page or a code-behind page (as all the WebForms stuff still works)
You can have a custom base class for your controllers.
You can get to one from the other, so:
void Page_Init( object sender, EventArgs e )
{
var ctrl = this.ViewContext.Controller as MyBaseController;
if ( ctrl != null )
{
MyLocalProp = ctrl.PropOnMyController;
}
}
This will then be available in the controller and the master page on a per Request basis.
Did you look into the tempData that is attached to the controller class? it is a simple dictionary that preserves it's value through one single request.That would meant that your data can only be accessed in the controller but that should not be a problem.
public class MyController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult MyAction(string id)
{
this.TempData["Message"] = "YourData";
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult MyAction(string Id)
{
var myData = this.TempData["Message"];
}
}
This works for me. I use it only to display warning messages and stuff like that.

Can MVC routing be used to create a dynamic content management system with dynamic pages stored in db rather than in view pages

Are there any good examples of mvc routing wherein every 404 page not found request is routed to a standard view in MVC which basically pulls the content from the database.
Just add this route to the bottom of your RouteTable:
routes.MapRoute("DynamicPages", "{*page}", new { Controller = "DynamicPages", Action = "Show", Page = String.Empty });
And create a controller for displaying dynamic pages from db:
public class DynamicPagesController : Controller
{
public ActionResult Show(string page)
{
var pageContent = DB.GetContentForPage(page);
return Content(pageContent);
}
}
Here's one way to do this: In your global.asax file in Application_Start, you need to set the default controller factory. Override it with an instance of your own factory.
void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory());
}
MyControllerFactory should inherit from DefaultControllerFactory and when selecting the controller to use, look in your database for the appropriate page you want to display. If the page exists, select the appropriate controller and override the action in the requestContext.RouteData collection to point at the appropriate action for displaying dynamic pages.
If the requested page doesn't exist, pass back a call to the base method and let it do what it would normally do.
There are other ways you could do it, but this one should work and allows you to intercept the request before you hit the 404 page.
modify the web.config file, you may Reference to this page and look at the setting custom error pages in web.config section.

Resources