Kann ich einen benutzerdefinierten Speicherort für die Suche nach Ansichten in ASP.NET MVC angeben?


105

Ich habe das folgende Layout für mein MVC-Projekt:

  • / Controller
    • /Demo
    • / Demo / DemoArea1Controller
    • / Demo / DemoArea2Controller
    • etc...
  • / Ansichten
    • /Demo
    • /Demo/DemoArea1/Index.aspx
    • /Demo/DemoArea2/Index.aspx

Wenn ich dies jedoch habe für DemoArea1Controller:

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

Ich erhalte den Fehler "Der Ansichtsindex oder sein Master konnte nicht gefunden werden" mit den üblichen Suchpositionen.

Wie kann ich festlegen, dass Controller in der Namespace-Suche "Demo" im Unterordner der Ansicht "Demo" angezeigt werden?


Hier ist ein weiteres Beispiel einer einfachen ViewEngine aus der MVC Commerce-App von Rob Connery: View Engine Code und der Global.asax.cs-Code zum Festlegen der ViewEngine: Global.asax.cs Hoffe, dies hilft.
Robert Dean

Antworten:


121

Sie können die WebFormViewEngine einfach erweitern, um alle Speicherorte anzugeben, an denen Sie suchen möchten:

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

Stellen Sie sicher, dass Sie daran denken, die View Engine zu registrieren, indem Sie die Application_Start-Methode in Ihrer Global.asax.cs ändern

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

Wie können Sie von einer verschachtelten Masterseite aus auf den Pfad einer Masterseite zugreifen? Wie beim Festlegen des verschachtelten Masterseitenlayouts für die Suche in den Pfaden der CustomViewEngine
Drahcir

6
Ist es nicht besser, wenn wir das Löschen der bereits registrierten Engines überspringen und nur die neue hinzufügen und viewLocations nur die neuen haben soll?
Prasanna

3
Implementiert ohne ViewEngines.Engines.Clear (); Alle funktionieren gut. Wenn Sie * .cshtml verwenden möchten, müssen Sie von RazorViewEngine
KregHEk

Gibt es eine Möglichkeit, die Optionen "Ansicht hinzufügen" und "Zur Ansicht wechseln" von den Controllern mit den neuen Ansichtspositionen zu verknüpfen? Ich benutze Visual Studio 2012
Neville Nazerane

Wie von @Prasanna erwähnt, müssen die vorhandenen Engines nicht gelöscht werden, um neue Standorte hinzuzufügen. Weitere Informationen finden Sie in dieser Antwort .
Hooman Bahreini

44

Jetzt können Sie in MVC 6 die IViewLocationExpanderSchnittstelle implementieren , ohne mit View Engines herumzuspielen:

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

Wo {0}ist der Name der Zielansicht, {1}- Name des Controllers und {2}- Name des Bereichs.

Sie können Ihre eigene Liste von Speicherorten zurückgeben, mit default viewLocations( .Union(viewLocations)) zusammenführen oder einfach ändern ( viewLocations.Select(path => "/AnotherPath" + path)).

Fügen Sie der ConfigureServicesMethode in der Startup.csDatei die folgenden Zeilen hinzu, um Ihren benutzerdefinierten Ansichtsstandort-Expander in MVC zu registrieren :

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

3
Ich wünschte, ich könnte dies mit 10 Stimmen abstimmen. Ist genau das, was in Asp.net 5 / MVC 6 benötigt wird. Schön. Sehr nützlich in meinem Fall (und anderen), wenn Sie Bereiche für größere Sites oder logische Gruppierungen in Superbereiche gruppieren möchten.
Drawid

Der Startup.cs-Teil sollte sein: services.Configure <RazorViewEngineOptions> Er geht in diese Methode: public void ConfigureServices (IServiceCollection-Dienste)
OrangeKing89

42

Es gibt tatsächlich eine viel einfachere Methode, als die Pfade in Ihren Konstruktor fest zu codieren. Unten finden Sie ein Beispiel für die Erweiterung der Razor-Engine, um neue Pfade hinzuzufügen. Ich bin mir nicht ganz sicher, ob die hier hinzugefügten Pfade zwischengespeichert werden:

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

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

Beachten Sie Folgendes: Ihr benutzerdefinierter Speicherort benötigt die Datei ViewStart.cshtml im Stammverzeichnis.


23

Wenn Sie nur neue Pfade hinzufügen möchten, können Sie die Standard-Ansichtsmodule hinzufügen und einige Codezeilen sparen:

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

Gleiches gilt für WebFormEngine


2
Für Ansichten: Verwenden Sie razorEngine.ViewLocationFormats.
Aldentev

13

Anstatt die RazorViewEngine in Unterklassen zu unterteilen oder sie vollständig zu ersetzen, können Sie einfach die PartialViewLocationFormats-Eigenschaft der vorhandenen RazorViewEngine ändern. Dieser Code geht 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();
}

2
Dies funktionierte für mich mit der Ausnahme, dass der Typ der Rasiermaschine "FixedRazorViewEngine" anstelle von "RazorViewEngine" war. Außerdem löst ich eine Ausnahme aus, wenn die Engine nicht gefunden wurde, da dadurch verhindert wird, dass meine Anwendung erfolgreich initialisiert wird.
Rob

3

Zuletzt habe ich überprüft, dass Sie Ihre eigene ViewEngine erstellen müssen. Ich weiß nicht, ob sie es in RC1 einfacher gemacht haben.

Der grundlegende Ansatz, den ich vor dem ersten RC verwendet habe, bestand darin, in meiner eigenen ViewEngine den Namespace des Controllers aufzuteilen und nach Ordnern zu suchen, die den Teilen entsprechen.

BEARBEITEN:

Ging zurück und fand den Code. Hier ist die allgemeine Idee.

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
Es ist eigentlich viel einfacher. Unterklasse WebFormsViewEngine und fügen Sie dann einfach das Array von Pfaden hinzu, die bereits in Ihrem Konstruktor gesucht werden.
Craig Stuntz

Gut zu wissen. Das letzte Mal, als ich diese Sammlung ändern musste, war dies nicht möglich.
Joel

Erwähnenswert ist, dass Sie die Variable "baseControllerNamespace" auf Ihren Basiscontroller-Namespace (z. B. "Project.Controllers") setzen müssen, aber ansonsten genau das getan haben, was ich 7 Jahre nach der Veröffentlichung benötigt habe.
Prototyp

3

Versuchen Sie so etwas:

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

Hinweis: Für ASP.NET MVC 2 verfügen sie über zusätzliche Standortpfade, die Sie für Ansichten in "Bereiche" festlegen müssen.

 AreaViewLocationFormats
 AreaPartialViewLocationFormats
 AreaMasterLocationFormats

Das Erstellen einer Ansichts-Engine für einen Bereich wird in Phils Blog beschrieben .

Hinweis: Dies ist für die Vorschau-Version 1 vorgesehen und kann sich ändern.


1

Bei den meisten Antworten hier löschen Sie die vorhandenen Standorte, indem Sie anrufen, ViewEngines.Engines.Clear()und fügen Sie sie dann wieder hinzu. Dies ist nicht erforderlich.

Wir können die neuen Standorte einfach zu den vorhandenen hinzufügen, wie unten gezeigt:

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

Jetzt können Sie Ihr Projekt so konfigurieren, dass es RazorViewEnginein Global.asax Folgendes verwendet:

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

Weitere Informationen finden Sie in diesem Tutorial .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.