AI OnAI Off
You can control how EPiServer tries to resolve paths to blocks etc by the IViewTemplateRegistrator like:
[ServiceConfiguration(typeof(IViewTemplateModelRegistrator))] public class TemplateCoordinator : IViewTemplateModelRegistrator { public const string BlockFolder = "~/Views/Shared/Blocks/"; public const string PagePartialsFolder = "~/Views/Shared/PagePartials/"; public static void OnTemplateResolved(object sender, TemplateResolverEventArgs args) { //Disable DefaultPageController for page types that shouldn't have any renderer as pages if (args.ItemToRender is IContainerPage && args.SelectedTemplate != null && args.SelectedTemplate.TemplateType == typeof(DefaultPageController)) { args.SelectedTemplate = null; } } /// <summary> /// Registers renderers/templates which are not automatically discovered, /// i.e. partial views whose names does not match a content type's name. /// </summary> /// <remarks> /// Using only partial views instead of controllers for blocks and page partials /// has performance benefits as they will only require calls to RenderPartial instead of /// RenderAction for controllers. /// Registering partial views as templates this way also enables specifying tags and /// that a template supports all types inheriting from the content type/model type. /// </remarks> public void Register(TemplateModelCollection viewTemplateModelRegistrator) { viewTemplateModelRegistrator.Add(typeof(JumbotronBlock), new TemplateModel { Tags = new[] { Global.ContentAreaTags.FullWidth }, AvailableWithoutTag = false, Path = BlockPath("JumbotronBlockWide.cshtml") }); viewTemplateModelRegistrator.Add(typeof(TeaserBlock), new TemplateModel { Name = "TeaserBlockWide", Tags = new[] { Global.ContentAreaTags.TwoThirdsWidth, Global.ContentAreaTags.FullWidth }, AvailableWithoutTag = false, Path = BlockPath("TeaserBlockWide.cshtml") }); var partialControllers = typeof(SitePageData).GetCustomAttributes(true); var types = GetMvcPartialControllerTypes(); foreach (var t in types) { var pageType = t.BaseType.GenericTypeArguments.FirstOrDefault(); viewTemplateModelRegistrator.Add(pageType, new TemplateModel { Name = pageType.Name, TemplateType =t, TemplateTypeCategory = EPiServer.Framework.Web.TemplateTypeCategories.MvcPartialController, Path = $"/Views/{pageType.Name}/Index.cshtml" }); } viewTemplateModelRegistrator.Add(typeof(IContentData), new TemplateModel { Name = "NoRendererMessage", Inherit = true, Tags = new[] { Global.ContentAreaTags.NoRenderer }, AvailableWithoutTag = false, Path = BlockPath("NoRenderer.cshtml") }); } private static IEnumerable<Type> GetMvcPartialControllerTypes() { return System.Reflection.Assembly .GetExecutingAssembly() .GetTypes() .Where(t => t.GetCustomAttributes(typeof(MvcPartialControllerAttribute), true).Any() && IsSubclassOfRawGeneric(typeof(PageControllerBase<>), t)); } // TODO this can be moved to extension class static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) { if (generic.Equals(toCheck)) return false; while (toCheck != null && toCheck != typeof(object)) { var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; if (cur == toCheck) { return true; } //generic = generic.BaseType; toCheck = toCheck.BaseType; } return false; } private static string BlockPath(string fileName) { return string.Format("{0}{1}", BlockFolder, fileName); } private static string PagePartialPath(string fileName) { return string.Format("{0}{1}", PagePartialsFolder, fileName); } }
It really depends on how you want to structure your solution. If you are using tags - you can follow Daniel's answer - to register where is template for particular content within particular tag. When you are just thinking about strucutring templates into folder structure, then just make sure that there is a view engine in ViewEnginesCollection that is capable of finding partial views in your newly created folders. EPIServer will use all possible view engines registered to find template (by conventions - template name is the same as content type name -> TestBlock.cs => TestBlock.cshtml).
public class MyViewEngine : RazorViewEngine { public MyViewEngine() { PartialViewLocationFormats = PartialViewLocationFormats.Union(new[] { "~/Views/Shared/Blocks/{0}.cshtml", "~/Views/Shared/Partials/{0}.cshtml", "~/Views/Shared/PagePartials/{0}.cshtml", }).ToArray(); } }
and then (in Global.asax for instance):
protected void Application_Start() { ... ViewEngines.Engines.Insert(0, new MyViewEngine());
I have created a block type testblock.cs in the Models/Blocks folder. I could able to render the view only when I create testblock.cshtml in the Views/Shared folder.
1) Could it be possible to have the view in the path Views/Blocks or Views/Blocks/TestBlock?I would need to create many blocks and I do not want everything in the shared folder.
2) The error I am getting is 'TestBlock cannot be displayed', what config settings do I need to set to see the actual error rather than a generic message