Чи можу я вказати спеціальне розташування для "пошуку переглядів" у 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"?


Ось ще один зразок простого ViewEngine з програми MVC Commerce Роб Коннері: Перегляд коду двигуна та коду Global.asax.cs для встановлення ViewEngine: Global.asax.cs Сподіваюся, що це допоможе.
— Роберт Дін

Відповіді:


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

Переконайтеся, що ви пам’ятаєте про реєстрацію механізму перегляду, змінивши метод Application_Start у своєму Global.asax.cs

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

Як ви можете отримати доступ до шляху до головного сторінки зі вкладеної сторінки "Master"? Як і в налаштуванні вкладеної верстки основної сторінки для пошуку в контурах CustomViewEngine
— Drahcir

6
Чи не краще, якщо ми пропустимо Очищення вже зареєстрованих двигунів і просто додамо новий і viewLocations матиме лише нові?
— Прасанна

3
Реалізує без ViewEngines.Engines.Clear (); Все добре працює. Якщо ви хочете використовувати * .cshtml, ви маєте успадкувати від RazorViewEngine
— KregHEk

чи можна зв’язати параметри "додати перегляд" та "перейти до перегляду" від контролерів до нових місць перегляду? я використовую візуальну студію 2012
— Neville Nazerane

Як згадував @Prasanna, немає необхідності очищати існуючі двигуни, щоб додати нові місця, див. Цю відповідь для отримання більш детальної інформації.
— Хоман Бахрейні

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, додайте наступні рядки до ConfigureServicesметоду у Startup.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)
— OrangeKing89

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.
— Алдентев

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.

Основний підхід, який я використовував до першого 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;
}

1
Насправді набагато простіше. Підклас WebFormsViewEngine, а потім просто додайте до масиву шляхів, які він вже шукає у вашому конструкторі.
— Крейг Стунц

Добре знати. Востаннє, коли мені потрібно було змінити цю колекцію, таким чином це було неможливо.
— Джоель

Варто зазначити, що вам потрібно встановити змінну "baseControllerNamespace" на простір імен базового контролера (наприклад, "Project.Controllers"), але в іншому випадку зробив саме те, що мені потрібно, через 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

Створення двигуна перегляду для області описано у блозі Філа .

Примітка. Це для попереднього випуску 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
}

Дивіться цей посібник для отримання додаткової інформації.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.