Location Formats for ASP.NET MVC
ASP.NET MVC is a wonderful thing. One of the many great features is
the ability to customize all of the .NET framework's code by swapping
out one implementation and using another. One such instance is
creating a custom view engine, which you can do as illustrated in this
example: http://www.singingeels.com/Articles/Creating_a_Custom_View_Engine_in_ASPNET_MVC.aspx.
The point of my article is not to illustrate how this can be done, but
about how to customize it for your needs. By default, the web forms
view engine looks for views in the folder of the controller or the
shared folder. So if you try to trigger an action method "Index"
within the controller of type CustomerController, a partial view
(.ascx) or the view (.aspx) is sought for in the ~/Shared folder or
~/Customer folder.
Now, I tend to like to use partial views in order to separate and
reuse functionality a lot. So I tend to have a lot of partial views
that tend to get reused across pages and I don't want everything to be
in the shared folder (by default, partial views have to be in the
shared folder or in the same folder as the controller). So I added
some code to the view engine that allowed me to create subfolders
within the shared folder and for the view engine to look for the
classes there. Imagine this folder structure:
Shared
Customers
Orders
Products
So the shared folder breaks up my partial views into the folder above.
Thinking long-term, rather than hard-coding all these folder
references and assigning them to the ViewLocationFormats and
PartialViewLocationFormats properties, I wanted something that I
wouldn't have to worry about changing later. So in true ASP.NET MVC
framework form, I created some extra code to create the ability to
automatically add references to subfolders too. In order to do this,
it's required to use the VirtualPathProvider class to extract the URL,
as in the following code:
public MyViewEngine() {
var locations = new List<string>
{
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx"
};
var dir = this.VirtualPathProvider.GetDirectory("~/Views/Shared");
var subs = dir.Directories.OfType<VirtualDirectory>();
foreach (var sub in subs)
{
locations.Add("~" + sub.VirtualPath.Substring(sub.VirtualPath.IndexOf("/", 2)) + "{0}.ascx");
}
base.ViewLocationFormats = locations.ToArray();
base.PartialViewLocationFormats = base.ViewLocationFormats;
}
This is the constructor for the custom view engine. It contains
some additional code to use the VirtualPathProvider property (a
property of our custom view engine) to extract the subdirectories of
the shared folder. You see the four hard-coded references at the
beginning, and so we need to create virtual path strings (which start
with "~" and work from the beginning of the virtual directory) to add
to the custom list. When working with folders using
VirtualPathProvider, the issue becomes the way paths are referenced.
By default, the path may be:
/MyVirtualFolder/Views/Shared/Customers/
When you need:
~/Views/Shared/Customers/
And so some additional work to format the path is needed (the
substring strips off the virtual directory folder. Now we have a
component that will allow the MVC framework to look for partial views
in all subdirectories in the shared folder.