A Custom View Engine with Dynamic View Location
- by imran_ku07
Introduction: One of the nice feature of ASP.NET MVC framework is its pluggability. This means you can completely replace the default view engine(s) with a custom one. One of the reason for using a custom view engine is to change the default views location and sometimes you need to change the views location at run-time. For doing this, you can extend the default view engine(s) and then change the default views location variables at run-time. But, you cannot directly change the default views location variables at run-time because they are static and shared among all requests. In this article, I will show you how you can dynamically change the views location without changing the default views location variables at run-time.
Description:
Let's say you need to synchronize the views location with controller name and controller namespace. So, instead of searching to the default views location(Views/ControllerName/ViewName) to locate views, this(these) custom view engine(s) will search in the Views/ControllerNameSpace/ControllerName/ViewName folder to locate views.
First of all create a sample ASP.NET MVC 3 application and then add these custom view engines to your application,
public class MyRazorViewEngine : RazorViewEngine
{
public MyRazorViewEngine() : base()
{
AreaViewLocationFormats = new[] {
"~/Areas/{2}/Views/%1/{1}/{0}.cshtml",
"~/Areas/{2}/Views/%1/{1}/{0}.vbhtml",
"~/Areas/{2}/Views/%1/Shared/{0}.cshtml",
"~/Areas/{2}/Views/%1/Shared/{0}.vbhtml"
};
AreaMasterLocationFormats = new[] {
"~/Areas/{2}/Views/%1/{1}/{0}.cshtml",
"~/Areas/{2}/Views/%1/{1}/{0}.vbhtml",
"~/Areas/{2}/Views/%1/Shared/{0}.cshtml",
"~/Areas/{2}/Views/%1/Shared/{0}.vbhtml"
};
AreaPartialViewLocationFormats = new[] {
"~/Areas/{2}/Views/%1/{1}/{0}.cshtml",
"~/Areas/{2}/Views/%1/{1}/{0}.vbhtml",
"~/Areas/{2}/Views/%1/Shared/{0}.cshtml",
"~/Areas/{2}/Views/%1/Shared/{0}.vbhtml"
};
ViewLocationFormats = new[] {
"~/Views/%1/{1}/{0}.cshtml",
"~/Views/%1/{1}/{0}.vbhtml",
"~/Views/%1/Shared/{0}.cshtml",
"~/Views/%1/Shared/{0}.vbhtml"
};
MasterLocationFormats = new[] {
"~/Views/%1/{1}/{0}.cshtml",
"~/Views/%1/{1}/{0}.vbhtml",
"~/Views/%1/Shared/{0}.cshtml",
"~/Views/%1/Shared/{0}.vbhtml"
};
PartialViewLocationFormats = new[] {
"~/Views/%1/{1}/{0}.cshtml",
"~/Views/%1/{1}/{0}.vbhtml",
"~/Views/%1/Shared/{0}.cshtml",
"~/Views/%1/Shared/{0}.vbhtml"
};
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
var nameSpace = controllerContext.Controller.GetType().Namespace;
return base.CreatePartialView(controllerContext, partialPath.Replace("%1", nameSpace));
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
var nameSpace = controllerContext.Controller.GetType().Namespace;
return base.CreateView(controllerContext, viewPath.Replace("%1", nameSpace), masterPath.Replace("%1", nameSpace));
}
protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
{
var nameSpace = controllerContext.Controller.GetType().Namespace;
return base.FileExists(controllerContext, virtualPath.Replace("%1", nameSpace));
}
}
public class MyWebFormViewEngine : WebFormViewEngine
{
public MyWebFormViewEngine() : base()
{
MasterLocationFormats = new[] {
"~/Views/%1/{1}/{0}.master",
"~/Views/%1/Shared/{0}.master"
};
AreaMasterLocationFormats = new[] {
"~/Areas/{2}/Views/%1/{1}/{0}.master",
"~/Areas/{2}/Views/%1/Shared/{0}.master",
};
ViewLocationFormats = new[] {
"~/Views/%1/{1}/{0}.aspx",
"~/Views/%1/{1}/{0}.ascx",
"~/Views/%1/Shared/{0}.aspx",
"~/Views/%1/Shared/{0}.ascx"
};
AreaViewLocationFormats = new[] {
"~/Areas/{2}/Views/%1/{1}/{0}.aspx",
"~/Areas/{2}/Views/%1/{1}/{0}.ascx",
"~/Areas/{2}/Views/%1/Shared/{0}.aspx",
"~/Areas/{2}/Views/%1/Shared/{0}.ascx",
};
PartialViewLocationFormats = ViewLocationFormats;
AreaPartialViewLocationFormats = AreaViewLocationFormats;
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
var nameSpace = controllerContext.Controller.GetType().Namespace;
return base.CreatePartialView(controllerContext, partialPath.Replace("%1", nameSpace));
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
var nameSpace = controllerContext.Controller.GetType().Namespace;
return base.CreateView(controllerContext, viewPath.Replace("%1", nameSpace), masterPath.Replace("%1", nameSpace));
}
protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
{
var nameSpace = controllerContext.Controller.GetType().Namespace;
return base.FileExists(controllerContext, virtualPath.Replace("%1", nameSpace));
}
}
Here, I am extending the RazorViewEngine and WebFormViewEngine class and then appending /%1 in each views location variable, so that we can replace /%1 at run-time. I am also overriding the FileExists, CreateView and CreatePartialView methods. In each of these method implementation, I am replacing /%1 with controller namespace. Now, just register these view engines in Application_Start method in Global.asax.cs file,
protected void Application_Start()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new MyRazorViewEngine());
ViewEngines.Engines.Add(new MyWebFormViewEngine());
................................................
................................................
}
Now just create a controller and put this controller's view inside Views/ControllerNameSpace/ControllerName folder and then run this application. You will find that everything works just fine.
Summary: ASP.NET MVC uses convention over configuration to locate views. For many applications this convention to locate views is acceptable. But sometimes you may need to locate views at run-time. In this article, I showed you how you can dynamically locate your views by using a custom view engine. I am also attaching a sample application. Hopefully you will enjoy this article too.
SyntaxHighlighter.all()