J'ai la disposition suivante pour mon projet mvc:
Cependant, quand j'ai ceci pour DemoArea1Controller
:
public class DemoArea1Controller : Controller
{
public ActionResult Index()
{
return View();
}
}
J'obtiens l'erreur "La vue 'index' ou son maître est introuvable", avec les emplacements de recherche habituels.
Comment puis-je spécifier que les contrôleurs dans la recherche d'espace de noms "Demo" dans le sous-dossier de vue "Demo"?
Vous pouvez facilement étendre WebFormViewEngine pour spécifier tous les emplacements dans lesquels vous souhaitez rechercher:
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;
}
}
N'oubliez pas d'enregistrer le moteur de vue en modifiant la méthode Application_Start dans votre Global.asax.cs
protected void Application_Start()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());
}
Maintenant, dans MVC 6, vous pouvez implémenter l'interface IViewLocationExpander
sans déconner avec les moteurs de vue:
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
}
}
où {0}
est le nom de la vue cible, {1}
- le nom du contrôleur et {2}
- le nom de la zone.
Vous pouvez renvoyer votre propre liste d'emplacements, la fusionner avec la valeur par défaut viewLocations
(.Union(viewLocations)
) ou simplement les modifier (viewLocations.Select(path => "/AnotherPath" + path)
).
Pour enregistrer votre expanseur d'emplacement d'affichage personnalisé dans MVC, ajoutez les lignes suivantes à la méthode ConfigureServices
dans le fichier Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationExpanders.Add(new MyViewLocationExpander());
});
}
Il existe en fait une méthode beaucoup plus simple que de coder en dur les chemins dans votre constructeur. Vous trouverez ci-dessous un exemple d'extension du moteur Razor pour ajouter de nouveaux chemins. Une chose dont je ne suis pas tout à fait sûr est de savoir si les chemins que vous ajoutez ici seront mis en cache:
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();
}
}
Et votre 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);
}
Une chose à noter: votre emplacement personnalisé aura besoin du fichier ViewStart.cshtml à sa racine.
Si vous souhaitez simplement ajouter de nouveaux chemins, vous pouvez ajouter aux moteurs de vue par défaut et épargner quelques lignes de code:
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);
La même chose s'applique à WebFormEngine
Au lieu de sous-classer RazorViewEngine ou de le remplacer purement et simplement, vous pouvez simplement modifier la propriété PartialViewLocationFormats de RazorViewEngine existante. Ce code va dans 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();
}
Remarque: pour ASP.NET MVC 2, ils ont des chemins d'emplacement supplémentaires que vous devrez définir pour les vues dans "Zones".
AreaViewLocationFormats
AreaPartialViewLocationFormats
AreaMasterLocationFormats
La création d'un moteur de vue pour une zone est décrite sur le blog de Phil .
Remarque: il s'agit de la version 1 de l'aperçu. Elle peut donc être modifiée.
Essayez quelque chose comme ceci:
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);
}
La dernière fois que j'ai vérifié, cela vous oblige à créer votre propre ViewEngine. Je ne sais pas s'ils ont facilité les choses dans RC1.
L'approche de base que j'ai utilisée avant le premier RC était, dans mon propre ViewEngine, de diviser l'espace de noms du contrôleur et de rechercher les dossiers qui correspondaient aux parties.
MODIFIER:
Je suis revenu et j'ai trouvé le code. Voici l'idée générale.
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;
}