SharePoint 2010 renders list views (and fields) with the use of XSLT, which is a huge improvement over the original CAML rendering model. As developers there is no longer a need to construct awkward, and in some cases overly complex, CAML structures in order to render a field correctly in a list view.
The Football Result Field
(Soccer if you are from the USA!)
A football result generally consists of two values, a home score and an away score. This can be represented best in SharePoint with the use of a SPFieldMultiColumn field type, with 2 columns for each part of the score. The image below shows this field in edit mode.
This tutorial won't cover in detail how this custom field is created - this is focusing purely on rendering the field using a custom XSLT as opposed to the OOTB one.
By default, without any rendering customisation whatsoever, our field is rendered like this in a list view:
From CAML to XSLT
XSLT is nothing particularly new, and certainly not in the SharePoint world (take search result rendering for example) but it is new in the list view sphere. Previously we would have managed the view rendering of custom fields directly in the fldtypes_***.xml file, in this case we would have had something similar to this:
1: <RenderPattern Name="DisplayPattern">
2: <Column SubColumnNumber="0" HTMLEncode="TRUE"/>
3: <HTML><![CDATA[ - ]]></HTML>
4: <Column SubColumnNumber="1" HTMLEncode="TRUE"/>
For backwards compatability the above CAML is still valid and can still be used, see this article
for more information
In order to customise the view display we first have to create an XSL file that will transform the list view XML. This XSL file must be located in the \14\TEMPLATE\LAYOUTS\XSL folder and be named fldtypes_***.xsl (similar to the XML - consistency!). You will notice that there are already two XSL's in this folder containing the transformation code for all OOTB field types.
In order to best describe how the XSLT needs to be formatted let's take a look at the complete XSLT that will render our result field:
1: <xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0"
2: exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"
3: xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer"
4: xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt"
5: xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal" ddwrt:oob="true">
6: <xsl:output method="html" indent="no"/>
7: <xsl:template match="FieldRef[@FieldType='Result']" mode="Note_body">
8: <!--Set the thisNode parameter to the current node-->
9: <xsl:param name="thisNode" select="." />
10: <!--Store the current element-->
11: <xsl:variable name="curElement" select="current()" />
13: <!--Store the field value we are rendering in a variable for easier access-->
14: <xsl:variable name="fldVal">
15: <!--We want to return the value of the field that has the same name as the current element being processed-->
16: <xsl:value-of select="$thisNode/@*[name()=curElement/@Name]"/>
19: <!--Render our result field with the home score - away score construct-->
20: <xsl:value-of select="substring-before($fldVal, ', ')"/> - <xsl:value-of select="substring-after($fldVal, ', ')"/>
So, what is this doing? Basically we want to create an XSL template that will match all rows in the incoming XML that contain a Result fieldtype, this type name maps directly to the TypeName attribute specified in the fldtypes_***.xml file you would have created for your custom field type.
1: <xsl:template match="FieldRef[@FieldType='Result']" mode="Note_body">
Note: ALL fldtypes_***.xsl files will be used when rendering lists whether our custom field type is used in a list view or not, so matching on the FieldType attribute is the recommended way of ensuring we only execute the custom template when necessaryThe mode attribute
The mode must be set correctly dependent on the field's base parent type. As we are using a MultiColumn the underlying parent type is Note therefore we render using the Note_body mode of the main field rendering XSLT. Different modes apply to different field types, this article will not cover all the modes available however it would be a good idea to look in the fldtypes.xsl file to see the various types of render mode available.
Next we populate the $thisNode parameter with the current row being processed. thisNode is a parameter used throughout the various XSLT's and keeps track of the current row in the list view. For simplicity we also store the current() node which contains information about the current field (in this case the Result field type).
1: <!--Set the thisNode parameter to the current node-->
2: <xsl:param name="thisNode" select="." />
3: <!--Store the current element-->
4: <xsl:variable name="curElement" select="current()" />
At the time of writing changing the thisNode parameter to another name, or not using it at all will render your XSL invalid and will simply not render correctly
Having retrieved the necessary XML fragments we can begin rendering the value. In this example the selection is being stored in a variable so we can easily modify it.
1: <xsl:variable name="fldVal">
2: <!--We want to return the value of the field that has the same name as the current element being processed-->
3: <xsl:value-of select="$thisNode/@*[name()=$curElement/@Name]"/>
The code excerpt above performs the selection of the Result field value in the current list view row, in order to understand more clearly what it is selecting and how, this is a slimmed down version of what $thisNode contains (the actual <row/> element contains every field available in the list - but displaying this would take up half the article!):
1: <row title="test" id="10" Round.="" Round="" FullTime="3, 2" Competition.="2;#Division 1" />
1: <fieldref id="713d86d6-93d8-474b-ab32-9d34e175bb33" Type="Note" FieldType="Result" DisplayName="FullTime" Name="FullTime"/>
So, our selection is taking the value from the <row /> (stored as $thisNode) element where the attribute name matches the FieldType attribute in the <fieldref /> element (stored as $curElement).
After getting the raw value of the field we can now render it with some custom formatting:
1: <!--Render our result field with the home score - away score construct-->
2: <xsl:value-of select="substring-before($fldVal, ', ')"/> - <xsl:value-of select="substring-after($fldVal, ', ')"/>
Our final result renders our field like this:
- CAML is no longer used to render a field value in list views but can still be specified for backwards compatability
- XSL files for custom fields are store in the \14\TEMPLATE\LAYOUTS\XSL folder and named fldtypes_***.xsl
- $thisNode parameter must be populated with the current node being processed and will contain a single <row /> element containing all fields available in the list
- The mode attribute on the template declaration is important, this must be set correctly otherwise the XSL will not be used to render the field value