Five New Optimizely Certifications are Here! Validate your expertise and advance your career with our latest certification exams. Click here to find out more
AI OnAI Off
Five New Optimizely Certifications are Here! Validate your expertise and advance your career with our latest certification exams. Click here to find out more
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