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