Можно ли указать форматы местоположения для поиска для макета MVC Razor?

В Razor при загрузке частичного представления можно просто указать имя частичного представления, а механизм просмотра Razor будет искать RazorViewEngine.PartialViewLocationFormats:

@Html.RenderPartial("_PartialView", Model);

будет фактически искать местоположения, указанные в PartialViewLocationFormats в механизме просмотра, например,

~/Views/Home/_PartialView.cshtml
~/Views/Shared/_PartialView.cshtml

Однако при указании макета мне кажется, что я вынужден указать конкретный путь к макету:

@Layout = "~/Views/Shared/MyLayout.cshtml";

То, что я хотел бы сделать, это указать макет только по имени и найти фактический макет, выполнив поиск списка общих местоположений:

@Layout = "MyLayout";

... но я не могу найти никаких возможностей для этого. Поскольку я не мог найти никакой документации относительно этого, я попытался играть с настройкой RazorViewEngine.MasterLocationFormats, но это свойство не используется при поиске макетов.

Кто-нибудь знает, как это сделать?

Ответ 1

Недавно я боролся с аналогичной проблемой, когда в тематическом приложении, которое использует настраиваемый ViewEngine для поиска местоположения темы сначала для представлений, я пытался переопределить некоторые основные файлы. Один из способов заставить местоположение макета пройти через ViewEngine FindView - указать имя главного представления в действии контроллера при возврате:

return View("myView", "myViewMaster");

Однако, если "myViewMaster" также имеет макет (вложенные макеты), этот метод не работает для вложенного макета. На мой взгляд, самое лучшее решение - вызвать механизм просмотра непосредственно в представлении:

@{
    Layout = (ViewEngines.Engines.FindView(this.ViewContext.Controller.ControllerContext, "myViewMaster", "").View as RazorView).ViewPath;
}

Это работает, но, безусловно, может быть инкапсулировано в метод расширения, чтобы избежать повторения и абстрактного кода.

Надеюсь, что это поможет!

Ответ 2

Я пробовал пару разных вещей и не мог найти способ делать то, что вы хотели. Однако вы можете просто оставить макет пустым и использовать файл _ViewStart.cshtml для назначения макета для каждой страницы с использованием свойства ViewBag.

Итак, если у вас есть перечисление (например), и вы устанавливаете свойство dynamicviewbag CustomLayout на значение этого перечисления, вы можете использовать файл _ViewStart, чтобы сделать что-то вроде этого.

@{  
    MvcApplication12.Controllers.CustomLayouts customLayout = this.ViewContext.Controller.ViewBag.CustomLayout;

    switch (customLayout)
    {
        case MvcApplication12.Controllers.CustomLayouts.Blue: Layout = "~/Views/Shared/Blue.cshtml"; break;
        case MvcApplication12.Controllers.CustomLayouts.Green: Layout = "~/Views/Shared/Green.cshtml"; break;
        case MvcApplication12.Controllers.CustomLayouts.Default:
        default: Layout = "~/Views/Shared/Default.cshtml"; break;
    }   
}

Не уверен, помогает ли это.

Ответ 3

Я принял принятый ответ от @sowee15 и добавил метод расширения, чтобы немного его обернуть:

using System;
using System.Web.Mvc;

namespace MyApp.Classes
{
    public static class ViewEngineCollectionExtensions
    {
        public static string FindViewPath(this ViewEngineCollection viewEngines, ControllerContext controllerContext, string viewName, string masterName = null)
        {
            var viewResult = ViewEngines.Engines.FindView(controllerContext, viewName, masterName ?? string.Empty);
            if(viewResult == null)
                throw new Exception(string.Format("The specified view {0} could not be found.", viewName));

            var view = viewResult.View as RazorView;
            if(viewResult == null)
                throw new Exception(string.Format("The specified view {0} must be a Razor view.", viewName));

            return view.ViewPath;
        }
    }
}

Затем вы можете вызывать расширение из своего представления или viewstart.cshtml следующим образом:

@{
    Layout = ViewEngines.Engines.FindViewPath(ViewContext.Controller.ControllerContext, "_PopupLayout");
}