Category Archives: MVC

Organize MVC views like Sitecore renderings with a Razor View Engine

Organise your renderings, organize your views

The nice thing about Sitecore is that is allows you to structure items in a tree. One of the things I like to structure are my renderings. In Sitecore this is not a problem, but I find it difficult to structure the views in my Visual Studio solution.
I like to put renderings that belong to the same functionality together. Renderings I put together are things like views for components used for the Experience Editor, module views, etc. In Sitecore this is easy since you are free to structure the tree as you see fit. The visual studio part is somewhat different. It is easy to structure the controllers folder to match the Sitecore structure. This is also easily done for the views. But when your view is not in the expected folder (/views/controllername/viewname.cshtml), you have to provide a concrete path to the view. If I don’t do this I get this message: The view ‘ViewName’ or its master was not found or no view engine supports the searched locations.

Educating the Razor View Engine

Luckily we can educate the view engine to also look in other locations. We do this by creating our own razor view engine that knows about our other locations and registering it in the global.asax. In this view engine amongst other things you can define in which locations the razor view engine should look when rendering views and partials. By doing this you can have the razor view engine look in these locations and that results in just using the name of a view when returning it from a controller action.

Example

I have created a razor view engine in which I registered some of the other locations I wish to use

public class MyRazorViewEngine : RazorViewEngine
{
  // An array of folders under the /Views folder the View Engine should look
  private readonly string[] _viewPathParts = new[]{
    "Components",
    "Modules",
    "Company/Modules"
  };

  // An array of folders under the folders declared above the View Engine should look
  private readonly string[] _partialPathParts = new[]{
    "Partials",
    "Parts"
  };     

  public MyRazorViewEngine()
  {
    ViewLocationFormats = GetViewLocationFormats();
    PartialViewLocationFormats = GetPartialViewLocationFormats();
  }

  private string[] GetPartialViewLocationFormats()
  {
    var basePath = new[]
    {
        "~/Views/%1/{1}/%2/{0}.cshtml",
        "~/Views/{1}/%2/{0}.cshtml"
    };
    var viewPaths = GetLocationFormats(basePath, "%1", _viewPathParts);
    var paths = GetLocationFormats(viewPaths, "%2", _partialPathParts);

    return paths;
  }

  private string[] GetViewLocationFormats()
  {
    var basePath = new[] { "~/Views/%1/{1}/{0}.cshtml" };
    var paths = GetLocationFormats(basePath, "%1", _viewPathParts);

    return paths;
  }

  private static string[] GetLocationFormats(IEnumerable<string> basePaths, string token, string[] viewPathParts)
  {
    var paths = new List<string>();
    foreach (string basePath in basePaths)
    {
      foreach (string part in viewPathParts)
      {
        paths.Add(basePath.Replace(token, part));
        if (!basePath.Contains(token))
        {
          break;
        }
      }
    }

    return paths.ToArray();
  }
}

This razor view engine add these paths to the ViewLocationFormats
~/Views/Components/{1}/{0}.cshtml
~/Views/Modules/{1}/{0}.cshtml
~/Views/Company/Modules/{1}/{0}.cshtml

This razor view engine add these paths to the PartialViewLocationFormats
“~/Views/Components/{1}/Partials/{0}.cshtml”
“~/Views/Components/{1}/Parts/{0}.cshtml”
“~/Views/Modules/{1}/Partials/{0}.cshtml”
“~/Views/Modules/{1}/Parts/{0}.cshtml”
“~/Views/Company/Modules/{1}/Partials/{0}.cshtml”
“~/Views/Company/Modules/{1}/Parts/{0}.cshtml”
“~/Views/{1}/Partials/{0}.cshtml”
“~/Views/{1}/Parts/{0}.cshtml”

This view engine can be registered in the Global.asax like this.

 protected void Application_Start() { AreaRegistration.RegisterAllAreas(); ViewEngines.Engines.Clear(); // optional ViewEngines.Engines.Add(new MyRazorViewEngine()); RouteConfig.RegisterRoutes(RouteTable.Routes); } 

Result

Using a Razor View Engine like this makes it possible to go from this

return View("~/views/Company/Modules/MyRazorView.cshtml");

to this:

return View("MyRazorView");
The code in this post is inspired by:
http://blog.thekfactor.info/posts/asp-net-mvc-custom-view-engines-using-the-razor-view-engine-as-the-base/
http://theshravan.net/blog/configure-the-views-search-locations-in-asp-net-mvc/