In my previous post I described how to get started and work with items and fields usign xWrap.
There is also a very convenient way to implement MVC renderings with using the framework, including both controller and view renderings and also access custom rendering parameters properties in a strongly-typed way.

Understanding ViewModel

In Sitecore MVC rendering engine there is an object that is being passed to view renderings by default – Sitecore.Mvc.Presentation.RenderingModel and it has two key properties:

  • .Item – the item which is being rendered (or rendering item), – equals to data source item or if the data source is not set – falls back to .PageItem
  • .PageItem – the context page item PageContext.Current.Item, usually same item as Sitecore.Context.Item.

xWrap framework provides a similar object, but a generic one – IViewModel, with srtongly-typed wrappers for key properties.
So if you have a simple rendering which needs to output properties of the data source item, your view should declare a model of type IViewModel

@model Xwrap.Mvc.IViewModel<YourDataItem>

And then you can access your wrapped data source item by .RenderingItem property:

@Model.RenderingItem.YourStronglyTypedField

The page item has been left as just Sitecore.Data.Item object, as it is not being used by renderings that often on practice. However you can always wrap the page item with an extension method if you need to access it for example:

@{ var pageMetadata = Model.PageItem.WrapItem<_MetaDataItem>(); }

Or if your rendering does not use the data source, you can still use same .RenderingItem property as it will fallback to the page item (that is why .PageItem is being used not that often)

Implement controller rendering

To create an instance of IViewModel type there is a factory object in the framework – IViewModelFactory. Again, it can be injected as an interface into your controller type if you are using the default Sitecore DI container.
It is also accessible as a singleton property Xwrap.Mvc.ViewModelFactory.Instance and is configured in the influde config file as:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <xWrap>
      <mvc>
        <viewModelFactory type="Xwrap.Mvc.ViewModelFactory, Xwrap.Mvc" singleInstance="true">
          <param name="itemWrapperFactory" ref="xWrap/itemWrapperFactory"/>
        </viewModelFactory>
      </mvc>
    </xWrap>
  </sitecore>
</configuration>

The IViewModelFactory provides main functionality for getting ViewModel objects with next signature:

public interface IViewModelFactory
{
	IViewModel<TRenderingItem> GetViewModel<TRenderingItem>() where TRenderingItem : ItemWrapper;
	IViewModel<TRenderingItem, TRenderingParameters> GetViewModel<TRenderingItem, TRenderingParameters>() 
		where TRenderingItem : ItemWrapper
		where TRenderingParameters : RenderingParametersWrapper;
}

For example, I need to implement a very simple rendering “Fact box” which is supposed to render fields from the data source item of data template “_Fact box” which looks like this:

I have a feature project with name “Pintle.Feature.ContentModules” – modules that are supposed only to render Sitecore data source items without any additional business logic, so I normally group them into one project as they are very simple.
So my controller for that module will look like this:

namespace Pintle.Feature.ContentModules.Controllers
{
	using System.Web.Mvc;
	using Xwrap.Mvc;

	public class ContentModulesController : Controller
	{
		private readonly IViewModelFactory viewModelFactory;

		public ContentModulesController(IViewModelFactory viewModelFactory)
		{
			this.viewModelFactory = viewModelFactory;
		}

		public ActionResult FactBox()
		{
			var viewModel = this.viewModelFactory.GetViewModel<_FactBoxItem>();
			return this.View(viewModel);
		}
	}
}

And then my view will look like this:

@model Xwrap.Mvc.IViewModel<Pintle.Feature.ContentModules.SitecoreTemplates._FactBoxItem>
<div class="factbox">
  @Model.RenderingItem.FactBoxTagline.Render(new { EnclosingTag = "small" })
  @Model.RenderingItem.FactBoxTitle.Render(new { EnclosingTag = "h3" })
  @Model.RenderingItem.FactBoxText
</div>

Looks very simple, however the properties are being rendered in a native Sitecore way, they all rendered as editable by default and the native Sitecore pipeline is being called inside.

Render method options

As you may have noticed, I do not call the .Value property of the field in the view. It is because there is implicit cast operator to IHtmlString is implemented. Each field wrapper has a .Render() method which will execute native Sitecore rendering pipeline for this specific field type. Also there is an option to pass parameters to .Render() method which are just being passed to the Sitecore pipeline.
Let’s see some examples of .Render() method usage:


<!--Render an image with attributes -->
@Model.RenderingItem.ContentImage.Render(new { @class = "img-responsive", mw = 350 })

<!-- Render link field to be opened in new browser window -->
@Model.RenderingItem.ContentLink.Render(new { target = "_blank" })

<!-- Or render link field with inner html which will be displayed even when the link is not set -->
@Model.RenderingItem.ContentLink.RenderBeginField(new { @class = "link", target = "_blank" })
<div>Inner html here</div>
@Model.RenderingItem.ContentLink.RenderEndField()

<!-- Render field and forbid its editing in edit mode -->
@Model.RenderingItem.ContentTitle.Render(editing: false)

<!-- Reder field with Enclosing Tag. Tag won't be displayed if the field is empty. -->
@Model.RenderingItem.ContentTitle.Render(new { EnclosingTag = "span" })

<!-- Render field with Enclosing html. The 'before' and 'after' html won't appear if the field is empty -->
@Model.RenderingItem.ContentTitle.Render(new { Before = "<div class=\"my-class\">", After = "</div>" })

<!-- Render date time field with specific date format -->
@Model.RenderingItem.CreatedDate.Render(new { format = "MMM dd, yyyy"})

So basically this code:

@Model.RenderingItem.ContentImage.Render(new { @class = "img-responsive", mw = 350 })

is the same as if you would use default Sitecore helper method:

@Html.Sitecore().Field("Content image", Model.Item, new { @class="img-responsive", @mw="350" })

Implement view rendering

The controller that I have implemented above does not make much sence as it just passes the view model object to my view where the view expects the data source item to be of “_Fact box” template. This sounds like a view rendering really.
And xWrap can handle the view renderings with strongly typed view models. So I will keep my view as is, but will change the rendering type to be “View rendering” and it will work out of the box.

@model Xwrap.Mvc.IViewModel<Pintle.Feature.ContentModules.SitecoreTemplates._FactBoxItem>

<div class="factbox">
  @Model.RenderingItem.FactBoxTagline.Render(new { EnclosingTag = "small" })
  @Model.RenderingItem.FactBoxTitle.Render(new { EnclosingTag = "h3" })
  @Model.RenderingItem.FactBoxText
</div>

This is being done by xWrap injecting into <mvc.getModel> Sitecore pipeline with Xwrap.Mvc.Pipelines.Mvc.GetModel.GetFromView; processor.
The processor sees the expected type of the model object, and if it is of IViewModel type, it will create an instance of it using same IViewModelFactory as if it was the Controller I implemented in the beginning.

Access rendering parameters

Let’s say I want to assign custom rendering parameters template to my rendering and use in a strongly-typed way. So I have a template “Split box module parameters”:

My code-generation t4 template has created me a class for it (see previous post for code generation):

namespace Pintle.Feature.ContentModules.SitecoreTemplates
{
	using Sitecore.Mvc.Presentation;
	using Xwrap.FieldWrappers.Abstractions;
	using Xwrap.Mvc.RenderingParameters;

	/// <summary>
	/// Rendering parameters wrapper for template 'Split text box module parameters'.
	/// Template ID: {0A836317-E940-45FE-86F7-E8DEA4D6D73F}.
	/// Template path: /sitecore/templates/Feature/ContentModules/Rendering parameters/Split text box module parameters.
	/// </summary>

	// ReSharper disable once InconsistentNaming
	// ReSharper disable once PartialTypeWithSinglePart
	public partial class SplitTextBoxModuleParameters : RenderingParametersWrapper
	{
		public SplitTextBoxModuleParameters(RenderingParameters parameters) : base(parameters)
		{
		}

		/// <summary>
		/// A wrapped 'checkbox' parameter for field with name 'Flip horisontally'.
		/// <returns><see cref="ICheckboxFieldWrapper"/></returns>
		/// </summary>

		public ICheckboxFieldWrapper FlipHorisontally => this.CheckboxField(FieldNames.FlipHorisontally);

		/// <summary>
		/// A wrapped 'checkbox' parameter for field with name 'Padding bottom'.
		/// <returns><see cref="ICheckboxFieldWrapper"/></returns>
		/// </summary>

		public ICheckboxFieldWrapper PaddingBottom => this.CheckboxField(FieldNames.PaddingBottom);

		public static class FieldNames
		{
			public const string FlipHorisontally = "Flip horisontally"; 
			public const string PaddingBottom = "Padding bottom"; 
		}
	}
}

Quick notes. This type is now inherited from Xwrap.Mvc.RenderingParameters.RenderingParametersWrapper type, as it is not really a normal Sitecore item. Therefore amount of supported fields is a little limited there as parameters are not supposed to hold any content. Render methods for this type are also not available as it is not the content item to be rendered really.

In order to access the parameters in your view rendering, simply extend the model to be IViewModel<TRenderingItem, TRenderingParameters>, for example:

@using Xwrap.Mvc
@using Pintle.Feature.ContentModules.SitecoreTemplates
@model IViewModel<_SplitTextBoxModuleItem, SplitTextBoxModuleParameters>
  
<section class="split-text-box @(Model.RenderingParameters.PaddingBottom.Value ? "padding-bottom" : "")">
    <div class="split-text-box__image">
	  @Model.RenderingItem.SplitTextBoxImage
    </div>
    <div class="split-text-box__text">
	  @Model.RenderingItem.SplitTextBoxTagline.Render(new { EnclosingTag = "small" })
	  @Model.RenderingItem.SplitTextBoxTitle.Render(new { EnclosingTag = "h2" })
	  @Model.RenderingItem.SplitTextBoxText.Render(new { EnclosingTag = "p" })
    </div>
</section>

Within the controller rendering there is an option to get view model with rendering parameters like this:

var viewModel = this.viewModelFactory.GetViewModel<_SplitTextBoxModuleItem, SplitTextBoxModuleParameters>();

Also there is a way to get parameters object only:

var parameters = this.viewModelFactory.GetRenderingParameters<SplitTextBoxModuleParameters>();

I guess this is it for now. As I told before, I have been using xWrap on at least 5 projects now in both helix based architecture and also on old solutions which were not “helixified” yet. Works for me, hopefully will work for you.

Share article
See also
Content migration sitecore

Sitecore content migration – a technical guide

Read more Volodymyr Hil 04.11.2019
Sitecore benefits in the AWS cloud

Sitecore to AWS Case: Cloud as a Transition

Read more Motiejus Bagdonas 21.10.2019
Pintle.Packager – Sitecore Package Generator

Pintle.Packager – Sitecore Package Generator

Read more Volodymyr Hil 07.10.2019

Introducing xWrap framework – Sitecore Experience Wrapper

Read more Volodymyr Hil 09.12.2018
pintle-at-sitecore-symposium

A recap from Sitecore Symposium 2018 and MVP summit

Read more Volodymyr Hil 25.10.2018