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


/ Controllers


/ Demo
/ Demo / DemoArea1Controller
/ Demo / DemoArea2Controller
等...


/视图


/ Demo
/ Demo / DemoArea1 / Index .aspx
/Demo/DemoArea2/Index.aspx



但是,当我在DemoArea1Controller上有此功能时:

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


出现“找不到视图'索引'或它的主视图”错误,并带有通常的搜索位置。

如何在“演示”中指定控制器在“演示”视图子文件夹中进行名称空间搜索?

评论

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

#1 楼

您可以轻松地扩展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;
    }
}


请确保您记得通过修改自己的Application_Start方法来注册视图引擎。 Global.asax.cs

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


评论


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

–德拉希尔
2012-12-18 18:27

如果我们跳过“清除已注册的引擎”并仅添加新引擎,而viewLocations仅包含新引擎,这会更好吗?

– Prasanna
2014年9月1日上午11:43

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

– KregHEk
15年2月19日在11:40

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

– Neville Nazerane
16年11月19日,0:39

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

–霍曼·巴里尼(Hooman Bahreini)
1月26日4:28

#2 楼

现在,在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中注册自定义视图位置扩展器,请在ConfigureServices文件中的Startup.cs方法中添加下一行:

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


评论


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

–机敏
2015年11月19日在7:08

Startup.cs部分应该是:services.Configure 它采用这种方法:public void ConfigureServices(IServiceCollection services)

– OrangeKing89
16-10-20在13:08



#3 楼

实际上,有比将路径硬编码到构造函数中更简单的方法。以下是扩展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文件。

#4 楼

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

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
也是如此

评论


对于视图:使用razorEngine.ViewLocationFormats。

– Aldentev
2015年10月21日在9:36

#5 楼

您可以仅更改现有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();
}


评论


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

–Rob
16-4-5 6:00



#6 楼

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

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

编辑:

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

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


评论


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

– Craig Stuntz
09年3月11日在2:52

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

–乔尔
2009年3月11日13:57

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

–prototype14
16-10-31在22:38

#7 楼

尝试这样的事情:

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


#8 楼

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

 AreaViewLocationFormats
 AreaPartialViewLocationFormats
 AreaMasterLocationFormats


为Area创建视图引擎是

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

#9 楼

此处的大多数答案都可以通过调用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();   
    }
}


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

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


了解更多信息,请参见本教程。

#10 楼

我是在MVC 5中这样做的。我不想清除默认位置。
帮助程序类:
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();
        }
    }
}

然后在Application_Start中:
// Add ConKit View locations
ConKit.Helpers.AppStartHelper.AddConKitViewLocations();