是否可以在ASP.NET MVC中指定自定义位置以“搜索视图”?


105

我的mvc项目具有以下布局:

  • /控制器
    • /演示
    • / Demo / DemoArea1Controller
    • / Demo / DemoArea2Controller
    • 等等...
  • /观看
    • /演示
    • /Demo/DemoArea1/Index.aspx
    • /Demo/DemoArea2/Index.aspx

但是,当我有这个用于DemoArea1Controller

public class DemoArea1Controller : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

我收到“找不到视图'索引'或它的母版”错误,并带有通常的搜索位置。

如何在“ Demo”视图子文件夹中指定“ Demo”名称空间中的控制器?


这是Rob Connery的MVC Commerce应用程序中另一个简单ViewView的示例:View Engine代码和用于设置ViewEngine的Global.asax.cs代码:Global.asax.cs希望这会有所帮助。
罗伯特·迪恩

Answers:


121

您可以轻松扩展WebFormViewEngine以指定要查看的所有位置:

public class CustomViewEngine : WebFormViewEngine
{
    public CustomViewEngine()
    {
        var viewLocations =  new[] {  
            "~/Views/{1}/{0}.aspx",  
            "~/Views/{1}/{0}.ascx",  
            "~/Views/Shared/{0}.aspx",  
            "~/Views/Shared/{0}.ascx",  
            "~/AnotherPath/Views/{0}.ascx"
            // etc
        };

        this.PartialViewLocationFormats = viewLocations;
        this.ViewLocationFormats = viewLocations;
    }
}

确保记得通过修改Global.asax.cs中的Application_Start方法来注册视图引擎

protected void Application_Start()
{
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new CustomViewEngine());
}

如何从嵌套母版页访问母版页的路径?与设置嵌套母版页面布局以在CustomViewEngine的路径中进行搜索一样
Drahcir 2012年

6
如果我们跳过“清除已注册的引擎”并仅添加新引擎,而viewLocations仅包含新引擎,是否会更好?
Prasanna 2014年

3
没有ViewEngines.Engines.Clear()的实现;一切正常。如果要使用* .cshtml,则必须继承自RazorViewEngine
KregHEk

有什么方法可以将控制器的“添加视图”和“转到视图”选项链接到新视图位置?我正在使用Visual Studio 2012
Neville Nazerane,2016年

如@Prasanna所述,无需清除现有引擎即可添加新位置,有关更多详细信息,请参见此答案
Hooman Bahreini

45

现在,在MVC 6中,您可以实现IViewLocationExpander接口而不会弄乱视图引擎:

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
    }
}

{0}目标视图名称在哪里,{1}-控制器名称和{2}-区域名称。

您可以返回自己的位置列表,将其与默认位置viewLocations.Union(viewLocations))合并,也可以仅对其进行更改(viewLocations.Select(path => "/AnotherPath" + path))。

要在MVC中注册自定义视图位置扩展器,请ConfigureServicesStartup.cs文件中的方法中添加下一行:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.ViewLocationExpanders.Add(new MyViewLocationExpander());
    });
}

3
我希望我能以10票赞成。正是Asp.net 5 / MVC 6所需要的。漂亮。当您想将区域分为大型区域或逻辑分组的超级区域时,对我(或其他人)非常有用。
drewid

Startup.cs部分应为:services.Configure <RazorViewEngineOptions>它采用以下方法:public void ConfigureServices(IServiceCollection services)
OrangeKing89 '16

42

实际上,有比将路径硬编码到构造函数中更简单的方法。以下是扩展Razor引擎以添加新路径的示例。我不确定的一件事是,是否将缓存在此处添加的路径:

public class ExtendedRazorViewEngine : RazorViewEngine
{
    public void AddViewLocationFormat(string paths)
    {
        List<string> existingPaths = new List<string>(ViewLocationFormats);
        existingPaths.Add(paths);

        ViewLocationFormats = existingPaths.ToArray();
    }

    public void AddPartialViewLocationFormat(string paths)
    {
        List<string> existingPaths = new List<string>(PartialViewLocationFormats);
        existingPaths.Add(paths);

        PartialViewLocationFormats = existingPaths.ToArray();
    }
}

还有您的Global.asax.cs

protected void Application_Start()
{
    ViewEngines.Engines.Clear();

    ExtendedRazorViewEngine engine = new ExtendedRazorViewEngine();
    engine.AddViewLocationFormat("~/MyThemes/{1}/{0}.cshtml");
    engine.AddViewLocationFormat("~/MyThemes/{1}/{0}.vbhtml");

    // Add a shared location too, as the lines above are controller specific
    engine.AddPartialViewLocationFormat("~/MyThemes/{0}.cshtml");
    engine.AddPartialViewLocationFormat("~/MyThemes/{0}.vbhtml");

    ViewEngines.Engines.Add(engine);

    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
}

需要注意的一件事:您的自定义位置在其根目录中将需要ViewStart.cshtml文件。


23

如果只想添加新路径,则可以添加到默认视图引擎并保留一些代码行:

ViewEngines.Engines.Clear();
var razorEngine = new RazorViewEngine();
razorEngine.MasterLocationFormats = razorEngine.MasterLocationFormats
      .Concat(new[] { 
          "~/custom/path/{0}.cshtml" 
      }).ToArray();

razorEngine.PartialViewLocationFormats = razorEngine.PartialViewLocationFormats
      .Concat(new[] { 
          "~/custom/path/{1}/{0}.cshtml",   // {1} = controller name
          "~/custom/path/Shared/{0}.cshtml" 
      }).ToArray();

ViewEngines.Engines.Add(razorEngine);

同样适用于 WebFormEngine


2
对于视图:使用razorEngine.ViewLocationFormats。
Aldentev

13

您可以仅更改现有RazorViewEngine的PartialViewLocationFormats属性,而不必继承RazorViewEngine的子类或直接替换它。此代码位于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();
}

2
这对我有用,但剃刀引擎类型是“ FixedRazorViewEngine”而不是“ RazorViewEngine”。如果找不到引擎,我也会抛出一个异常,因为它阻止了我的应用程序成功初始化。
罗布

3

最后我检查了一下,这需要您构建自己的ViewEngine。我不知道他们是否使RC1更容易。

在我自己的ViewEngine中,我在第一个RC之前使用的基本方法是拆分控制器的名称空间,并查找与部件匹配的文件夹。

编辑:

回去找到代码。这是一般的想法。

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;
}

1
实际上,这要容易得多。子类化WebFormsViewEngine,然后将其添加到它已经在构造函数中搜索的路径数组。
2009年

很高兴知道。我上一次需要修改该集合时,无法以这种方式进行。
乔尔

值得一提的是,您需要在基本控制器名称空间(例如“ Project.Controllers”)中设置“ baseControllerNamespace”变量,但是在发布7年后,它确实满足了我的需要。
原型14年

3

尝试这样的事情:

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);
}

3

注意:对于ASP.NET MVC 2,您需要为“区域”中的视图设置其他位置路径。

 AreaViewLocationFormats
 AreaPartialViewLocationFormats
 AreaMasterLocationFormats

Phil的博客中介绍了为Area创建视图引擎。

注意:这是针对预览版本1的,可能会有所更改。


1

此处的大多数答案都可以通过调用清除现有位置ViewEngines.Engines.Clear(),然后重新添加回去...无需执行此操作。

我们可以简单地将新位置添加到现有位置,如下所示:

// note that the base class is RazorViewEngine, NOT WebFormViewEngine
public class ExpandedViewEngine : RazorViewEngine
{
    public ExpandedViewEngine()
    {
        var customViewSubfolders = new[] 
        {
            // {1} is conroller name, {0} is action name
            "~/Areas/AreaName/Views/Subfolder1/{1}/{0}.cshtml",
            "~/Areas/AreaName/Views/Subfolder1/Shared/{0}.cshtml"
        };

        var customPartialViewSubfolders = new[] 
        {
            "~/Areas/MyAreaName/Views/Subfolder1/{1}/Partials/{0}.cshtml",
            "~/Areas/MyAreaName/Views/Subfolder1/Shared/Partials/{0}.cshtml"
        };

        ViewLocationFormats = ViewLocationFormats.Union(customViewSubfolders).ToArray();
        PartialViewLocationFormats = PartialViewLocationFormats.Union(customPartialViewSubfolders).ToArray();

        // use the following if you want to extend the master locations
        // MasterLocationFormats = MasterLocationFormats.Union(new[] { "new master location" }).ToArray();   
    }
}

现在,您可以将项目配置为RazorViewEngine在Global.asax中使用以上内容:

protected void Application_Start()
{
    ViewEngines.Engines.Add(new ExpandedViewEngine());
    // more configurations
}

有关更多信息,请参见本教程

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.