Adding sub-directory to "View/Shared" folder in ASP.Net MVC and calling the view

43,733

Solution 1

Solved this. To add the "Shared/Partials" sub directory I created to the list of locations searched when trying to locate a Partial View in Razor using:

@Html.Partial("{NameOfView}")

First create a view engine with RazorViewEngine as its base class and add your view locations as follows. Again, I wanted to store all of my partial views in a "Partials" subdirectory that I created within the default "Views/Shared" directory created by MVC.

public class RDDBViewEngine : RazorViewEngine
{
    private static readonly string[] NewPartialViewFormats = 
    { 
        "~/Views/{1}/Partials/{0}.cshtml",
        "~/Views/Shared/Partials/{0}.cshtml"
    };

    public RDDBViewEngine()
    {
        base.PartialViewLocationFormats = base.PartialViewLocationFormats.Union(NewPartialViewFormats).ToArray();
    }       

}

Note that {1} in the location format is the Controller name and {0} is the name of the view.

Then add that view engine to the MVC ViewEngines.Engines Collection in the Application_Start() method in your global.asax:

ViewEngines.Engines.Add(new RDDBViewEngine()); 

Solution 2

Thank you for your answers. This has organized my Shared folder, but why do you create a new type of view engine? I just made a new RazorViewEngine, set it's PartialViewLocationFormats and added it to the list of ViewEngines.

ViewEngines.Engines.Add(new RazorViewEngine
{
    PartialViewLocationFormats = new string[]
    {
        "~/Views/{1}/Partials/{0}.cshtml",
        "~/Views/Shared/Partials/{0}.cshtml"
    }
});

Solution 3

It´s nice to custom the view engine, but if you just want to have a subfolder por partials you don´t need that much...

Just use the full path to the partial view, as done for the Layout View:

@Html.Partial("/Views/Shared/Partial/myPartial.cshtml")

Hope it helps someone...

Solution 4

If you are doing this in ASP.NET Core, simple go to the Startup class, under ConfigureServices method, and put

services.AddMvc()
    .AddRazorOptions(opt => {
        opt.ViewLocationFormats.Add("/Views/{1}/Partials/{0}.cshtml");
        opt.ViewLocationFormats.Add("/Views/Shared/Partials/{0}.cshtml");
    });

Solution 5

I've updated lamarant's excellent answer to include Areas:

public class RDDBViewEngine : RazorViewEngine
{
    private static readonly string[] NewPartialViewFormats =
    {
        "~/Views/{1}/Partials/{0}.cshtml",
        "~/Views/Shared/Partials/{0}.cshtml"
    };

    private static List<string> AreaRegistrations;

    public RDDBViewEngine()
    {
        AreaRegistrations = new List<string>();

        BuildAreaRegistrations();

        base.PartialViewLocationFormats = base.PartialViewLocationFormats.Union(NewPartialViewFormats).ToArray();
        base.PartialViewLocationFormats = base.PartialViewLocationFormats.Union(areaRegistrations).ToArray();
    }

    private static void BuildAreaRegistrations()
    {
        string[] areaNames = RouteTable.Routes.OfType<Route>()
            .Where(d => d.DataTokens != null && d.DataTokens.ContainsKey("area"))
            .Select(r => r.DataTokens["area"].ToString()).ToArray();

        foreach (string areaName in areaNames)
        {
            AreaRegistrations.Add("~/Areas/" + areaName + "/Views/Shared/Partials/{0}.cshtml");
            AreaRegistrations.Add("~/Areas/" + areaName + "/Views/{1}/Partials/{0}.cshtml");
        }
    }
}

Then don't forget to include in your application start:

public class MvcApplication : System.Web.HttpApplication
{

    protected void Application_Start()
    {
        ...

        ViewEngines.Engines.Add(new RDDBViewEngine()); 
    }
}
Share:
43,733
lamarant
Author by

lamarant

Wizard

Updated on April 19, 2020

Comments

  • lamarant
    lamarant about 4 years

    I'm currently developing a site using ASP.Net MVC3 with Razor. Inside the "View/Shared" folder, I want to add a subfolder called "Partials" where I can place all of my partial views (for the sake of organizing the site better.

    I can do this without a problem as long as I always reference the "Partials" folder when calling the views (using Razor):

    @Html.Partial("Partials/{ViewName}")
    

    My question is if there is a way to add the "Partials" folder to the list that .Net goes through when searching for a view, this way I can call my view without having to reference the "Partials" folder, like so:

    @Html.Partial("{ViewName}")
    

    Thanks for the help!

  • SLaks
    SLaks over 13 years
    You don't need to inherit the class.
  • ses011
    ses011 about 13 years
    I wanted to do the same thing in my project. Thanks for the clear solution.
  • Jim D'Angelo
    Jim D'Angelo over 12 years
    Creating your own class will allow you to reuse it in the future instead of remembering to handwrite those string literals every time.
  • Piotr Kula
    Piotr Kula almost 11 years
    +1 Thanks. That is really easy doing that. And the proper way with creating a reusable class :)
  • Zapnologica
    Zapnologica almost 11 years
    Where do you add the class "RDDBViewEngine" that you made in step 2?
  • lamarant
    lamarant almost 11 years
    I created a new .cs file and then added the class to my main {Project}.Models namespace. In VS, just right click on your Models directory, click Add->Class and name the class FooViewEngine. You'll get the stubbed out class that you can copy the above code into.
  • Jaans
    Jaans almost 11 years
    Both approaches will work, and I guess each has it's own pros & cons. To me this is the better answer because we are not changing the behaviour already available in the RazorViewEngine.
  • Alex
    Alex over 10 years
    Area is available as well as {2} in the PartialViewLocationFormats.
  • mcfea
    mcfea about 10 years
    @lamarant, See my answer below for Areas.
  • Chris Moschini
    Chris Moschini over 9 years
    Note that you can just as easily run the code in this answer from a Helper method, that captures this just the once all the same, rather than building an additional class with just this one detail in it.
  • Dzianis Yafimau
    Dzianis Yafimau about 9 years
    Thanks, really simple and WORKING solution!
  • Phate01
    Phate01 about 8 years
    I did this but I'm getting an ArgumentNullException on the virtualPath, what am I wrong?
  • Santosh
    Santosh almost 8 years
    For MVC 5, just put @Html.Partial("Partial/myPartial.cshtml")
  • Santosh
    Santosh almost 8 years
    For MVC 5, just put @Html.Partial("Partial/myPartial.cshtml")
  • Christopher King
    Christopher King over 3 years
    And for those who aren't sure what the {0} and {1} placeholders represent, {1} would be the controller-name and {0} would be the view-name. And I would add that instead of hard-coding the '.cshtml' extension you could use the ViewLocationFormats.ViewExtension property.