2009-03-11 10 views
90

Ho il seguente schema per il mio progetto MVC:Posso specificare un percorso personalizzato per "cercare le viste" in ASP.NET MVC?

  • /Controller
    • /Demo
    • /Demo/DemoArea1Controller
    • /Demo/DemoArea2Controller
    • ecc ...
  • /Visualizzazioni
    • /Demo
    • /Demo/DemoArea1/Index.aspx
    • /Demo/DemoArea2/Index.aspx

Tuttavia, quando ho questo per DemoArea1Controller:

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

Viene visualizzato l'errore "L'indice di visualizzazione" o il suo oggetto principale non è stato trovato ", con le solite posizioni di ricerca.

Come è possibile specificare i controller nella ricerca dello spazio dei nomi "Demo" nella sottocartella "Demo"?

+0

Ecco un altro esempio di una semplice ViewEngine da Rob Connery MVC Commercio app: [Visualizza codice motore] (http://mvcsamples.codeplex.com/SourceControl/changeset/view/17126#286681) E il codice Global.asax.cs per impostare ViewEngine: [Global.asax.cs] (http: //mvcsamples.codeplex. it/SourceControl/changeset/view/17126 # 286569) Spero che questo aiuti. –

risposta

111

Si può facilmente estendere la WebFormViewEngine per specificare tutti i luoghi che si desidera guardare in:

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

Ricordarsi di registrare il motore di visualizzazione modificando il metodo Application in Global.asax cs

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

Come si può accedere al percorso di una pagina master da una pagina Nested Master? Come nell'impostazione del layout della pagina master nidificato da cercare all'interno dei percorsi del CustomViewEngine – Drahcir

+4

Non è meglio saltare i motori già registrati e aggiungere quello nuovo e viewLocations avrà solo i nuovi? – Prasanna

+2

Strumenti senza ViewEngines.Engines.Clear(); Tutto funziona bene. Se si desidera utilizzare * .cshtml è necessario ereditare da RazorViewEngine – KregHEk

3

Ultimo controllo, questo richiede di creare il proprio ViewEngine. Non so se hanno reso più facile in RC1 però.

L'approccio di base che ho usato prima del primo RC era, nel mio ViewEngine, per dividere lo spazio dei nomi del controller e cercare cartelle che corrispondessero alle parti.

EDIT:

Sono tornato e ha trovato il codice. Ecco l'idea generale.

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

In realtà è molto più semplice. Sottoclassi WebFormsViewEngine e poi aggiungi alla matrice di percorsi già presenti nel tuo costruttore. –

+0

Sì, quello che ha detto Craig! – Haacked

+0

Buono a sapersi. L'ultima volta che ho avuto bisogno di modificare quella raccolta, non era possibile in quel modo. – Joel

3

provare qualcosa di simile:

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

Nota: per ASP.NET MVC 2 hanno percorsi di localizzazione aggiuntivi che è necessario impostare per le viste in "Aree".

AreaViewLocationFormats 
AreaPartialViewLocationFormats 
AreaMasterLocationFormats 

Creazione di un motore di visualizzazione per un'area è described on Phil's blog.

Nota: questa è per la versione di anteprima 1, quindi è soggetta a modifiche.

36

In realtà esiste un metodo molto più semplice rispetto a codificare i percorsi nel costruttore. Di seguito è riportato un esempio di estensione del motore Razor per aggiungere nuovi percorsi. Una cosa che non sono del tutto sicuro è se i percorsi si aggiungono qui verranno memorizzati nella 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(); 
    } 
} 

E il tuo 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); 
} 

Una cosa da notare: la vostra posizione sarà personalizzato è necessario il file ViewStart.cshtml nella sua radice.

20

Se si desidera aggiungere nuovi percorsi, è possibile aggiungere ai motori di visualizzazione predefinite e risparmiare alcune righe di codice:

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

Lo stesso vale per WebFormEngine

+2

Per Visualizzazioni: utilizzare razorEngine.ViewLocationFormats. – Aldentev

12

Invece di sottoclassi della RazorViewEngine, o sostituendolo a titolo definitivo, puoi semplicemente modificare la proprietà PartialViewLocationFormats di RazorViewEngine esistente. Questo codice va 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

Questo ha funzionato per me, con l'eccezione che il tipo di motore del rasoio era "FixedRazorViewEngine" invece di "RazorViewEngine". Inoltre lancio un'eccezione se il motore non è stato trovato poiché impedisce la corretta inizializzazione dell'applicazione. – Rob

26

Ora in MVC 6 è possibile implementare IViewLocationExpander interfaccia senza fare in giro con i motori di vista:

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

dove {0} è bersaglio nome della vista, {1} - regolatore di nome e {2} - nome della zona.

È possibile restituire il proprio elenco di posizioni, unirlo con il valore predefinito viewLocations (.Union(viewLocations)) o semplicemente modificarle (viewLocations.Select(path => "/AnotherPath" + path)).

Per registrare la vostra abitudine vista posizione di espansione situata MVC, aggiungere righe successive al ConfigureServices metodo Startup.cs di file:

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

Vorrei poter votare questo su 10 voti. È esattamente ciò che è necessario in Asp.net 5/MVC 6. Bello. Molto utile nel mio caso (e altri) quando si desidera raggruppare aree in super aree per siti più grandi o raggruppamenti logici. – drewid

+0

La parte Startup.cs dovrebbe essere: services.Configure Va in questo metodo: public void ConfigureServices (servizi IServiceCollection) – OrangeKing89

Problemi correlati