EPiServer MVC content rendering – Part 2
Some days ago I wrote the part 1 of rendering MVC content. I got a comment about the example content types, that they were strange implemented. I agree that it looks strange, how I use the block in the example, but it should be looked at as an “Hello World” example, and I just want to demonstrate how the rendering of a block works.
One thing I forgot in the last post was that we register an IControllerFactory in the CMS, to return controllers we have resolved with the “TemplateResolver” directly. You can find the logic in the “EPiServer.Web.Mvc.ControllerTypeControllerFactory.GetControllerType” with the reflector if you’re interested.
Render controller action
When the index action in the controller “BasicPageController” gets called, MVC will try to resolve the “BasicPage” type argument. This would not have worked out of the box without the “ContentDataModelBinder” (in the “EPiServer.Web.Mvc namespace”).
The ContentDataModelBinder will look if the name of the parameter is “currentPage”, “currentBlock”, or “currentContent”. If it is one of them, it will try to get the current Page/Block/Content from the “DataTokens” in the “RouteData”.
In this case, we use the “currentPage” as parameter name, and the current page will be found in the “DataTokens” list. The current page will then simply be loaded from the content area, and the PageData will be returned from the “BindModel” method.
In the Index action, we start by creating an instance of the “BasicPageModel” and set it’s properties. Then we create an editing hits instance, by calling the extension method “GetEditHints<TModel, TContentType>”. We do this because the “MainBody” property in the content type has another name in the model (“Body”). By adding a connection between those, we make sure the on page edit will work as the editor expect.
Calling the view
When calling the default view (“Index"), with the view model, MVC will look for a folder with the same name as the controller name in the “RouteData” instance. In this case the folder name it looks for is “BasicPage”, and it will find a index view in that folder.
The view contains three different “PropertyFor” calls, one for a string, one for a XhtmlString”, and one for a block.
The “PropertyFor” extension method are a wrapper of the MVC build in helper “DisplayFor”. Everything you normaly can do with “DisplayFor” are possible to do with “PropertyFor”. The difference are mainly that the “PropertyFor” will make the “on page edit” work.
MVC will look after a partial view handling “string”. By default, we have no partial view for this type, and we will therefor make an “ToString()” call for the property instead. This works for this type. behaviour can be overridden by adding a partial view with the name “String” in your MVC project that handles string”.
MVC will look after a partial view handling “XhtmlString”. We have a default partial view with that name, that will call the extension method “RenderXhtmlString”. This behaviour can be overridden by adding a partial view with the name “XhtmlString” in your MVC project that handles “EPiServer.Core.XhtmlString”.
MVC will look after a partial view handling “BlockData”. We have a default partial view named “ContentData” that handles “ContentData”. This will be called becouse BlockData inherits from ContentData. This behaviour can be overridden by adding a partial view with the name “BlockData” or ContentData in your MVC project that handles BlockData/ContentData.
The "ContentData” view will call the html helper “RenderContentData”.
By default, the html helper “RenderContentData” will be used when DisplayFor or PropertyFor are used for a property in a view.
The extension method will first use the “TemplateResolver” to figure out what template that should handle the content. Then it will call the “MvcContentRenderer” class (EPiServer.Web.Mvc namespace), which will use the template to render the content.
The render method in the “MvcContentRenderer” will first look if the template the “TemplateResolver” returned are a partial view. If it is a partial view, it means that it doesn't use any controller, and the view should be called directly.
In case the tamplate is a partial view, the “ViewEngine” will be used to call it. This is the fast way of rendering content. If it’s not a partial view, we will call the html helper method “RenderAction” instead, which will look for the controller, resolved by the “TemplateResolver”. When calling the “RenderAction”, we will add the content (in this case the block) to the “RouteValueCollection” with key “currentContent”.
Just as for the page controller, the block controller have the block type as a parameter. The parameter are named “currentBlock” in this example, but it could also have hade the name “currentContent”. The same model binder as the one resolved the PageData object will now resolve the BlockData object in almost the same way.
This time, the model binder will not find any node to use, instead it will find a value for the key “currentContent”, and the binder will return the content (in this case the block) to the parameter.
The block controller will look for a partial view in the same way as it does for a view. The block view in this example just outputs the Heading and the Body, just as the page, and that is done exactly in the same way as for the page.