The XSL customizations of the XsltListViewWebPart in SharePoint 2010 are probably not a trivial thing. If you want to do something more complex with that the best starting point for reference materials is of course the SharePoint 2010 SDK - http://msdn.microsoft.com/en-us/library/ff604021.aspx. It provides extensive coverage on the topic, so I won’t repeat any of that in this posting. Here, I will concentrate on two things: the first one is a practical issue, which is probably the first one that you will encounter when customizing the XsltListViewWebPart’s XSL – how to hook your custom XSL to the XsltListViewWebPart. The thing is that you have not just one but four options for that – the XsltListViewWebPart itself exposes two properties for setting the custom XSL – XsltListViewWebPart.Xsl and XsltListViewWebPart.XslLink and also the SPView class has two properties with the very same names and obviously the same purpose. Well, that’s plenty and to quickly answer the two questions that may already have arisen – first – what has the SPView class to do with the XsltListViewWebPart – the answer is simple: the two classes are actually two representations of the same internal SharePoint entity (check a previous posting of mine on the subject - http://stefan-stanev-sharepoint-blog.blogspot.com/2010/02/listviewwebpart-spview-two-sides-of.html). And the second question – if the two classes are indeed representations of one and the same thing do these two sets of properties map to the same internal properties too – the answer here is no, the “Xsl” and “XslLink” properties of the XsltListViewWebPart are actually inherited from a base class (DataFormWebPart) that has no direct relation to list views and indeed the four properties are really independent from one another (and there are some differences in their usage despite the matching names as you will see below). One important thing here is that there is a specific precedence for their usage by the XsltListViewWebPart – the exact evaluation order is this:
- XsltListViewWebPart.XslLink
- XsltListViewWebPart.Xsl
- SPView.Xsl
- SPView.XslLink
Before starting with the exact specifics of using these properties I want to mention one other property of the XsltListViewWebPart class – CacheXslTimeOut. This is an integer property that specifies the cache time in seconds for the XslTransform object used by the XsltListViewWebPart. The caching of the XslTransform object is very useful because, of course, it boosts the performance. And the caching of the XslTransform also may be used or not used altogether depending on which of the “Xsl” or “XslLink” properties you use, which I think is important to know beforehand. One other thing here – when I tested the “caching” behavior of these properties I either changed the value of the properties in the case of the “Xsl” ones or changed the underlying XSL file in the case of the “XslLink” properties. The immediate reflection of the change to the rendering of the web part doesn’t necessarily mean that the cache is not used because it may simply mean that the change of the property invalidates the XslTransform cache. So in the short descriptions of the properties’ usage below I won’t mention that the XslTransform cache is not applied but simply that you see or don’t see immediately the change applied in the rendering of the XsltListViewWebPart. Of course for testing purposes you can always set the value of the CacheXslTimeOut property to 1 second so that you can quickly change the XSL and see the result immediately.
And now the details and specifics about the usage of the “XSL” properties:
- XsltListViewWebPart.XslLink – you can change this with either the SharePoint UI (it appears in the XsltListViewWebPart’s toolpart) or programmatically with the SharePoint object model. Note here that unlike the XslLink property of the SPView class you can’t specify simply the name of a custom XSL file that resides in the system TEMPLATE\LAYOUTS\XSL folder (e.g. the standard main.xsl or a custom “custom.xsl”) – this won’t work (it may be a bit surprising). You have two options for specifying the path to your custom XSL file here – the first one is to specify a file under the TEMPLATE\LAYOUTS folder (it can be directly in that folder or any sub-folder below it, not just the “XSL” one) – and the path (rather URL in this case) should mandatorily start with “/_layouts/”. The other option is to reference an XSL file that resides on your site, for instance in a document library – then you can use either the site relative URL of the file or the server relative one. For example, if you have a “custom.xsl” in a library whose URL is “documents”, then the site relative URL will be “documents/custom.xsl” (no starting slash) and the server relative URL will be something like “/sites/mysite/documents/custom.xsl” (note the starting slash – the starting part of the URL depends on the server relative URL of your site). And about the caching behavior – interestingly enough it is different depending on whether you use an XSL file from the LAYOUTS folder or in a document library in the site – in the first case the changes to the referenced XSL file won’t be visible immediately, and in the second – they will be.
- XsltListViewWebPart.Xsl – you can change this property with the object model, but the easier way to do this is with the SharePoint Designer. With it it should be easy to change the Xsl property even without deep understanding of XSL – you can use the enhanced UI of the SharePoint Designer to modify the styling and rendering of individual list columns or to apply conditional rendering on whole rows in the XsltListViewWebPart. The SharePoint Designer automatically populates the Xsl property with an XSL snippet containing one or several XSL templates depending on your exact customizations (this XSL also references the standard “main.xsl” and the templates in it are rather “overrides” of the standard row and column rendering XSL templates). The changes to the “Xsl” property are applied immediately regardless of the value of the CacheXslTimeOut property. If you think that this may pose a performance issue for you, you can always save the XSL contained in the “Xsl” property to an external XSL file which you can then reference with the XsltListViewWebPart.XslLink or SPView.XslLink properties.
- SPView.Xsl – you can again change this property programmatically – for that you will need to get the hidden SPView instance associated with your XsltListViewWebPart (see the link to my posting on the subject above) or again the easier way to achieve that is to set the property in the view schema in the “schema.xml” file of your custom list template (custom “schema.xml” files can also be specified in ListInstance feature elements via the “CustomSchema” attribute). Note that below the “View” element in the “schema.xml” file you can have both “Xsl” and “XslLink” elements. One important note - you should provide the XSL in the “Xsl” element in the “schema.xml” and also in the SPView.Xsl property in an XML CDATA section. The changes to the SPView.Xsl property (those applied with code) are immediately visible in the XsltListViewWebPart regardless of the value of the CacheXslTimeOut property.
- SPView.XslLink – you can change this property programmatically and also can set its value in the XslLink element in the view schema in the “schema.xml” file of your SharePoint list. Normally you provide only a file name in this property and the referenced XSL file with that name should exist in the system TEMPLATE\LAYOUTS\XSL folder. You can also reference files in sub-folders of the LAYOUTS\XSL folder and even files in the LAYOUTS folder itself – in the first case you set the “XslLink” property to “sub\custom.xsl” and in the second case - to “..\custom.xsl”. The XmlTransform caching is fully applied for the SPView.XslLink property depending on the value of the CacheXslTimeOut property. There is one extra thing here compared to the XsltListViewWebPart.XslLink property – if you set the CacheXslTimeOut property to 1 second you will need additionally to recycle the application pool (which will force the invalidation of the cache) and only after that you will see the changes to the underlying XSL file immediately in the rendering of the XsltListViewWebPart. This is also true if you have the CacheXslTimeOut set to 1 second and you see the immediate changes but then set it to some higher value and then again reset it to 1 second – then you will no longer see the immediate changes until you recycle the application pool again. This means that the changing of the CacheXslTimeOut property itself doesn’t invalidate the caching of the XmlTransform in the case of the SPView.XslLink property.
So, this was the first topic that I wanted to talk about and about the second one I will demonstrate a short XSL snippet:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:mycontrols="http://mycontrols" >
<xsl:output method="html" indent="no"/>
<xsl:decimal-format NaN=""/>
<xsl:param name="XmlDefinition" select="."/>
<xsl:template match="/">
<xsl:value-of disable-output-escaping="yes" select="'<%@ Register Tagprefix="asp" Namespace="mycontrols.WebControls" Assembly="mycontrols, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d03859869fe4a098" %>'"/>
<mycontrols:MyControl runat="server" ListUrl="docs" />
<table cellpadding="3">
<tr>
<xsl:for-each select="$XmlDefinition/ViewFields/FieldRef">
<th><xsl:value-of select="@DisplayName"/></th>
</xsl:for-each>
</tr>
<xsl:for-each select="/dsQueryResponse/Rows/Row">
<tr>
<xsl:variable name="row" select="." />
<xsl:for-each select="$XmlDefinition/ViewFields/FieldRef">
<xsl:variable name="fieldName" select="@Name" />
<td>
<xsl:choose>
<xsl:when test="$fieldName='LinkFilenameNoMenu'">
<xsl:value-of select="$row/@FileLeafRef"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$row/@*[name() = $fieldName]" disable-output-escaping="yes"/>
</xsl:otherwise>
</xsl:choose>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
You can see in the snippet that it makes its own custom rendering of the underlying SharePoint list data (not very good at that) and doesn’t even include the standard SharePoint “vwstyles.xsl” and “fldtypes.xsl” (it’s only for demonstration purposes and should be as short as possible). The important thing in it is in the very beginning of the body of its single XSL template definition – you can see there a somewhat concealed asp.net page “Register” directive and immediately after it an asp.net markup declaration of a server control. Note also that the “mycontrols” namespace of the server control element is declared in the root element of the XSL file. So, basically this means that you can place server controls inside the XSL and these will be instantiated by the XsltListViewWebPart and their logic will be executed server-side. As you see in the snippet I placed only one server control before the actual rendering of the list data, but you can place controls in every row or even cell that you render and you can provide some list item data to the properties of the server control or controls (there may be of course performance considerations because of the number of controls that can be created in this manner).
And to answer the question – how does the XsltListViewWebPart instantiate the server controls that you may place in the XSL. It is actually simple – the XsltListViewWebPart uses the XSL transformation to produce HTML markup from the source XML, it then checks whether there are occurrences of the “runat=server” token inside the HTML markup: if there are no occurrences, the HTML markup is simply rendered, otherwise the web part instantiates a server control using the asp.net TemplateControl.ParseControl method providing the raw HTML markup to its single parameter. The TemplateControl.ParseControl method as it name suggests parses asp.net markup and creates a server controls tree from it much like it happens when a normal “aspx” or “ascx” file gets compiled. So, with this small “trick” the XsltListViewWebPart allows you to place server controls and implement more complex server side logic inside your custom XSL. And one limitation to this approach – you can’t use user controls (ascx files in the CONTROLTEMPLATES or LAYOUTS system folders) inside the XSL (server controls that use user controls internally with the TemplateControl.LoadControl method will also fail to instantiate their user controls).