Can I specify a custom location to “search for views” in ASP.NET MVC?

后端 未结 10 2207
故里飘歌
故里飘歌 2020-11-22 06:08

I have the following layout for my mvc project:

  • /Controllers
    • /Demo
    • /Demo/DemoArea1Controller
    • /Demo/DemoArea2Controller
    • etc
相关标签:
10条回答
  • 2020-11-22 06:16

    Now in MVC 6 you can implement IViewLocationExpander interface without messing around with view engines:

    public class MyViewLocationExpander : IViewLocationExpander
    {
        public void PopulateValues(ViewLocationExpanderContext context) {}
    
        public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
        {
            return new[]
            {
                "/AnotherPath/Views/{1}/{0}.cshtml",
                "/AnotherPath/Views/Shared/{0}.cshtml"
            }; // add `.Union(viewLocations)` to add default locations
        }
    }
    

    where {0} is target view name, {1} - controller name and {2} - area name.

    You can return your own list of locations, merge it with default viewLocations (.Union(viewLocations)) or just change them (viewLocations.Select(path => "/AnotherPath" + path)).

    To register your custom view location expander in MVC, add next lines to ConfigureServices method in Startup.cs file:

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<RazorViewEngineOptions>(options =>
        {
            options.ViewLocationExpanders.Add(new MyViewLocationExpander());
        });
    }
    
    0 讨论(0)
  • 2020-11-22 06:16

    I did it this way in MVC 5. I didn't want to clear the default locations.

    Helper Class:

    namespace ConKit.Helpers
    {
        public static class AppStartHelper
        {
            public static void AddConKitViewLocations()
            {
                // get engine
                RazorViewEngine engine = ViewEngines.Engines.OfType<RazorViewEngine>().FirstOrDefault();
                if (engine == null)
                {
                    return;
                }
    
                // extend view locations
                engine.ViewLocationFormats =
                    engine.ViewLocationFormats.Concat(new string[] {
                        "~/Views/ConKit/{1}/{0}.cshtml",
                        "~/Views/ConKit/{0}.cshtml"
                    }).ToArray();
    
                // extend partial view locations
                engine.PartialViewLocationFormats =
                    engine.PartialViewLocationFormats.Concat(new string[] {
                        "~/Views/ConKit/{0}.cshtml"
                    }).ToArray();
            }
        }
    }
    

    And then in Application_Start:

    // Add ConKit View locations
    ConKit.Helpers.AppStartHelper.AddConKitViewLocations();
    
    0 讨论(0)
  • 2020-11-22 06:17

    Last I checked, this requires you to build your own ViewEngine. I don't know if they made it easier in RC1 though.

    The basic approach I used before the first RC was, in my own ViewEngine, to split the namespace of the controller and look for folders which matched the parts.

    EDIT:

    Went back and found the code. Here's the general idea.

    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName)
    {
        string ns = controllerContext.Controller.GetType().Namespace;
        string controller = controllerContext.Controller.GetType().Name.Replace("Controller", "");
    
        //try to find the view
        string rel = "~/Views/" +
            (
                ns == baseControllerNamespace ? "" :
                ns.Substring(baseControllerNamespace.Length + 1).Replace(".", "/") + "/"
            )
            + controller;
        string[] pathsToSearch = new string[]{
            rel+"/"+viewName+".aspx",
            rel+"/"+viewName+".ascx"
        };
    
        string viewPath = null;
        foreach (var path in pathsToSearch)
        {
            if (this.VirtualPathProvider.FileExists(path))
            {
                viewPath = path;
                break;
            }
        }
    
        if (viewPath != null)
        {
            string masterPath = null;
    
            //try find the master
            if (!string.IsNullOrEmpty(masterName))
            {
    
                string[] masterPathsToSearch = new string[]{
                    rel+"/"+masterName+".master",
                    "~/Views/"+ controller +"/"+ masterName+".master",
                    "~/Views/Shared/"+ masterName+".master"
                };
    
    
                foreach (var path in masterPathsToSearch)
                {
                    if (this.VirtualPathProvider.FileExists(path))
                    {
                        masterPath = path;
                        break;
                    }
                }
            }
    
            if (string.IsNullOrEmpty(masterName) || masterPath != null)
            {
                return new ViewEngineResult(
                    this.CreateView(controllerContext, viewPath, masterPath), this);
            }
        }
    
        //try default implementation
        var result = base.FindView(controllerContext, viewName, masterName);
        if (result.View == null)
        {
            //add the location searched
            return new ViewEngineResult(pathsToSearch);
        }
        return result;
    }
    
    0 讨论(0)
  • 2020-11-22 06:28

    Note: for ASP.NET MVC 2 they have additional location paths you will need to set for views in 'Areas'.

     AreaViewLocationFormats
     AreaPartialViewLocationFormats
     AreaMasterLocationFormats
    

    Creating a view engine for an Area is described on Phil's blog.

    Note: This is for preview release 1 so is subject to change.

    0 讨论(0)
  • 2020-11-22 06:34

    Instead of subclassing the RazorViewEngine, or replacing it outright, you can just alter existing RazorViewEngine's PartialViewLocationFormats property. This code goes in Application_Start:

    System.Web.Mvc.RazorViewEngine rve = (RazorViewEngine)ViewEngines.Engines
      .Where(e=>e.GetType()==typeof(RazorViewEngine))
      .FirstOrDefault();
    
    string[] additionalPartialViewLocations = new[] { 
      "~/Views/[YourCustomPathHere]"
    };
    
    if(rve!=null)
    {
      rve.PartialViewLocationFormats = rve.PartialViewLocationFormats
        .Union( additionalPartialViewLocations )
        .ToArray();
    }
    
    0 讨论(0)
  • 2020-11-22 06:36

    Try something like this:

    private static void RegisterViewEngines(ICollection<IViewEngine> engines)
    {
        engines.Add(new WebFormViewEngine
        {
            MasterLocationFormats = new[] {"~/App/Views/Admin/{0}.master"},
            PartialViewLocationFormats = new[] {"~/App/Views/Admin//{1}/{0}.ascx"},
            ViewLocationFormats = new[] {"~/App/Views/Admin//{1}/{0}.aspx"}
        });
    }
    
    protected void Application_Start()
    {
        RegisterViewEngines(ViewEngines.Engines);
    }
    
    0 讨论(0)
提交回复
热议问题