Hi,
For our new product, URL Plus Field, we needed to render a field value different depending on field settings. For instance, you can set to open links in a new window or replace the description text with an icon. In 2007, this is pretty easy, once you define your custom property as an attribute of field schema (do not use PropertySchema, you must set you custom values as attributes, you can do that by directly manipulating the underlying XML in your field class). In 2010, there is no way (as far as I can tell, please enlighten me) to know what the field values are in the XSLT rendering.
First we tried to fall back to old school CAML rendering by setting CAMLRendering property to true. That basically brought the whole list rendering down with an error, it is something specific to URL fields, as it works fine with text or number fields.
So I needed to come up with a way to get the property values and keep the XSLT rendering. After spending an entire day looking for a solution, approaching it from different angle and basically pulling my hair out, I still was nowhere near the solution.
The break came the next day, when I had an idea - if I cannot read the properties, maybe I can incorporate the property values as part of the item value (the URL itself). AND IT WORKED!!!
What I did was override the GetFieldValue method of my field class:
public override object GetFieldValue(string value)
{
if (IsSharePoint2010)
{
if (string.IsNullOrEmpty(value))
return value;
return value + string.Format(",{0},{1},{2},{3}", DisplayFormat, ShowAsIcon, IconUrl, OpenInNewWindow);
}
else
return base.GetFieldValue(value);
}
Here I'm basically checking if the current SharePoint version is 2010 (using a helper method that compare the build version of the local farm to 12, which is 2007), if not, I attach my additional property values to the URL itself. Now we are getting the property value in XSLT through the back door :)
So now we can use these values in our XSLT template.
Using the following two helper templates I can split my field value into parts and get value of each separate part:
This template split the value into sort of an array (in the XSL world):
<xsl:template name="IWURLPLUStokens">
<xsl:param name="str" select="."/>
<xsl:param name="splitString" select="','"/>
<xsl:choose>
<xsl:when test="contains($str,$splitString)">
<token>
<xsl:value-of select="substring-before($str,$splitString)"/>
</token>
<xsl:call-template name="IWURLPLUStokens">
<xsl:with-param name="str" select="substring-after($str,$splitString)"/>
<xsl:with-param name="splitString" select="$splitString"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<token>
<xsl:value-of select="$str"/>
</token>
</xsl:otherwise>
</xsl:choose>
</xsl:template>And this one will return value according to index:
<xsl:template name="IWURLPLUSGetPartAtIndex">
<xsl:param name="splits"/>
<xsl:param name="index"/>
<xsl:value-of select="msxsl:node-set($splits)/token[position()=$index]"/>
</xsl:template>
So now we can split our value into the URL itself (index=1) and the property values (the other 4 parts in our example).
Using the regular XSLT template we can now use these values to produce the exact outcome that we need in any XSLT view!
<xsl:template match="FieldRef[@FieldType='UrlPlusField']" mode="URL_body">
<xsl:param name="thisNode" select="." />
<xsl:variable name="thisValue" select="$thisNode/@*[name()=current()/@Name]"/>
<xsl:variable name="url">
<xsl:choose>
<xsl:when test="contains($thisValue, ',')">
<xsl:value-of select="substring-before($thisValue, ',')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$thisValue" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="splits">
<xsl:call-template name="IWURLPLUStokens">
<xsl:with-param name="str" select="$thisValue"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="displayFormat">
<xsl:if test="contains($thisValue, ',')">
<xsl:call-template name="IWURLPLUSGetPartAtIndex">
<xsl:with-param name="splits" select="$splits"/>
<xsl:with-param name="index" select="2"/>
</xsl:call-template>
</xsl:if>
</xsl:variable>
<xsl:variable name="openInNewWindow">
<xsl:if test="contains($thisValue, ',')">
<xsl:call-template name="IWURLPLUSGetPartAtIndex">
<xsl:with-param name="splits" select="$splits"/>
<xsl:with-param name="index" select="5"/>
</xsl:call-template>
</xsl:if> </xsl:variable>
<xsl:variable name="showAsIcon">
<xsl:if test="contains($thisValue, ',')">
<xsl:call-template name="IWURLPLUSGetPartAtIndex">
<xsl:with-param name="splits" select="$splits"/>
<xsl:with-param name="index" select="3"/>
</xsl:call-template>
</xsl:if>
</xsl:variable>
<xsl:variable name="iconUrl">
<xsl:if test="contains($thisValue, ',')">
<xsl:call-template name="IWURLPLUSGetPartAtIndex">
<xsl:with-param name="splits" select="$splits"/>
<xsl:with-param name="index" select="4"/>
</xsl:call-template>
</xsl:if>
</xsl:variable>
<xsl:variable name="desc" select="$thisNode/@*[name()=concat(current()/@Name, '.desc')]" />
<xsl:choose>
<xsl:when test="$url=''">
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$displayFormat='Image'">
<img src="{$url}" alt="{$desc}" />
</xsl:when>
<xsl:otherwise>
<a href="{$url}" >
<xsl:if test="$openInNewWindow='Always'">
<xsl:attribute name="target">_new</xsl:attribute>
</xsl:if>
<xsl:choose>
<xsl:when test="$showAsIcon='True'">
<img border="0">
<xsl:attribute name="alt">
<xsl:choose>
<xsl:when test="$desc=''">
<xsl:value-of select="$url"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$desc"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="src">
<xsl:choose>
<xsl:when test="$iconUrl='Link'">/_layouts/images/LINK.GIF</xsl:when>
<xsl:when test="$iconUrl='Help'">/_layouts/images/hhelp.gif</xsl:when>
<xsl:when test="$iconUrl='Warning'">/_layouts/images/WARN16.GIF</xsl:when>
</xsl:choose>
</xsl:attribute>
</img>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$desc=''">
<xsl:value-of select="$url"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$desc"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</a>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And that's it. Pretty easy, huh?!