Dela via


How to: Customize the Rendering of a Field on a List View

Applies to: SharePoint Foundation 2010

This topic explains how you can define how your custom field is rendered on list views.

Overview of Custom Field Rendering on List Views

Microsoft SharePoint Foundation uses XSLT style sheets to render list views. (For a discussion of the system, see Overview of XSLT List View Rendering System.) A list is rendered as an HTML table, and the value of a field is rendered into the appropriate cell of the table by a simple XSLT template from the fldtypes.xsl file located in %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\LAYOUTS\XSL. There are many templates in that file for rendering field values. The template that is used depends on several factors, the most important of which is the type of the field as defined by the SPFieldType enumeration. For example, there is a FieldRef_Number_body template for Number fields and a FieldRef_Text_body template for Text fields.

Note

There is not a one-to-one correspondence of field types to templates. For example, there are several different templates for computed fields, and the FieldRef_Number_body template is used for Currrency() fields in addition to Number fields.

For example, the following is the FieldRef_Text_body template.

<xsl:template name="FieldRef_Text_body" ddwrt:dvt_mode="body" match ="FieldRef" mode="Text_body">
  <xsl:param name="thisNode" select="."/>
  <xsl:choose>
    <xsl:when test="@AutoHyperLink='TRUE'">
      <xsl:value-of select="$thisNode/@*[name()=current()/@Name]" disable-output-escaping ="yes"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$thisNode/@*[name()=current()/@Name]"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

The thisNode parameter that is passed to this template consists of dsQueryResponse markup that contains the items from the list (up to the maximum allowed per page). For an example of this markup, see Examples of Input and Result Node Trees in XSLT Transformations.

Notice also that the template branches depending on whether the field is defined to automatically format strings that look like URLs as HTML <a> links. If it is, when the XSLT processor renders the HTML for the field, it does not replace significant characters, such as "<" and "&", with their character entity equivalents (&lt; and &amp;), which a compliant XSLT processor would ordinarily do by default. Aside from this complication, the template emits the value of the field with the following line.

<xsl:value-of select="$thisNode/@*[name()=current()/@Name]"/>

Because this line appears in virtually all field rendering XSLT templates, it deserves a closer look. The first step of the XPath that identifies the node whose value is to be rendered is simply a reference to the thisNode parameter, so the remainder of the steps are interpreted relative to the root of the markup in that parameter. The next step, before predicates are applied, is /@*. This refers to all attributes of the root and its descendants; therefore in this case, it refers to all the attributes of all the elements in thisNode. The predicated [name()=current()/@Name] further narrows the selection. On the left side of the "=" is the XSLT name() function. It represents the name of a node in the sequence produced by the "$thisNode/@*" part. In other words, it represents the name of an attribute in some element of thisNode. If you apply this to the example thisNode from Examples of Input and Result Node Trees in XSLT Transformations, you can see that some of the attributes in it are named after the internal names of fields of the list item; for example, Attachments, Title, and the custom field ISBN. The right side of the "=" is a reference to the Name attribute of the current node in the source node tree: current()/@Name. Because this template is declared with match ="FieldRef", it is applied only to FieldRef elements; therefore, the current() function refers to a FieldlRef element. Therefore, the entire xsl:value-of element is saying, "emit the value of the attribute from thisNode whose name is the same as the name of the current FieldRef in the source node tree."

So when the Title FieldRef of the example source node tree is being processed, the XSLT processor emits for it the value of the Title attribute of the current Row element in the thisNode markup. (This would be "Theories of Truth", for the first row). Similarly, it renders the value of the ISBN attribute of that same Row when it processes the ISBN FieldRef of the source node tree ("0-262-61107-4").

In most cases, the default rendering that your custom field will get, which is determined primarily by its base type, is just what you want. In the case of Text field, for example, the value is rendered as plain text in the appropriate cell of the HTML table. If you want your custom field to render in a special way, you must use an XSLT style sheet to define how it is rendered. The next section shows how to do this.

Defining Custom Rendering for a Custom Field

The following procedure shows how to create a custom XSLT style sheet to render a custom field type.

To create a custom XSLT style sheet for field rendering

  1. Create a text file with an "xsl" extension. It must be named on the pattern fldtypes_*.xsl, where the "*" is any string of allowed file name characters. Consider using the name of the custom field, such as fldtypes_ISBN.xsl. If you will have multiple custom XSLT style sheets, consider using your company name, such as fldtypes_Contoso.xsl, and include them all in the same file. Avoid using a string that might be used by another solution provider.

  2. Add the following style sheet declaration to the file.

    <xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema"
                    xmlns:d="https://schemas.microsoft.com/sharepoint/dsp"
                    version="1.0"
                    exclude-result-prefixes="xsl msxsl ddwrt"
                    xmlns:ddwrt="https://schemas.microsoft.com/WebParts/v2/DataView/runtime"
                    xmlns:asp="https://schemas.microsoft.com/ASPNET/20"
                    xmlns:__designer="https://schemas.microsoft.com/WebParts/v2/DataView/designer" 
                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                    xmlns:SharePoint="Microsoft.SharePoint.WebControls"
                    xmlns:ddwrt2="urn:frontpage:internal">
    
    
    </xsl:stylesheet>
    
  3. Open the file named fldtypes.xsl in %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\LAYOUTS\XSL, and find the template that renders your field by default. If, for example, your field's base type is Currency, the template is FieldRef_Number_body. Copy the template as a child of your xsl:stylesheet element. The following is an example using the FieldRef_Number_body template.

    <xsl:stylesheet > <!-- stylesheet attributes omitted for brevity -->
    
      <xsl:template name="FieldRef_Number_body" ddwrt:dvt_mode="body" match="FieldRef" mode="Number_body">
        <xsl:param name="thisNode" select="."/>
        <xsl:choose>
          <xsl:when test="$FreeForm">
            <xsl:call-template name="FieldRef_ValueOf_DisableEscape">
              <xsl:with-param name="thisNode" select="$thisNode"/>
            </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>
            <div align="right">
              <xsl:call-template name="FieldRef_ValueOf_DisableEscape">
                <xsl:with-param name="thisNode" select="$thisNode"/>
              </xsl:call-template>
            </div>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:template>
    
    </xsl:stylesheet>
    
  4. Delete the name attribute from the xsl:template element (in this case name="FieldRef_Number_body"). Also, delete the ddwrt:dvt_mode attribute if there is one.

  5. Change the match attribute, so that the template matches only fields with the exact internal name of your custom field. You do this with an XSLT predicate that specifies the required value of the Name attribute of the FieldRef element. For example, suppose that you have a Net Profit/Loss field whose internal name is "Net_x0020_Profit_x002f_Loss": Your template start tag should now look like the following.

    <xsl:template match="FieldRef[@Name='Net_x0020_Profit_x002f_Loss']" mode="Number_body">
    

    This ensures that you are customizing rendering of only your custom field type, not all fields that have Currency or Number as the base type.

  6. Edit, and add to, the markup of the template to produce the rendering effect you want. For an example, see the following section.

  7. Save and copy the fldtypes_*.xsl file to %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\LAYOUTS\XSL on all the servers.

  8. Reset the web application so that the files in %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\LAYOUTS\XSL are reloaded. Your custom file overrides any of the built-in files.

Example: Customize Currency Field

As a detailed example of step 6, this section shows how to render the value of the Net Profit/Loss field in red whenever it is a negative number. After step 5, your template should be as follows.

<xsl:stylesheet > <!-- stylesheet attributes omitted for brevity -->

  <xsl:template match="FieldRef[@Name='Net_x0020_Profit_x002f_Loss']" mode="Number_body">
    <xsl:param name="thisNode" select="."/>
    <xsl:choose>
      <xsl:when test="$FreeForm">
        <xsl:call-template name="FieldRef_ValueOf_DisableEscape">
          <xsl:with-param name="thisNode" select="$thisNode"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <div align="right">
          <xsl:call-template name="FieldRef_ValueOf_DisableEscape">
            <xsl:with-param name="thisNode" select="$thisNode"/>
          </xsl:call-template>
        </div>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

Note first that the default rendering branches depending on the test of the FreeForm parameter. This test returns true if the Type attribute of the Toolbar element in the source node tree is "FreeForm". If the attribute value is "Standard," or if the attribute is simply not present, the template adds HTML markup to right-justify the field value within its cell. (See Examples of Input and Result Node Trees in XSLT Transformations for an example of a source node tree and its Toolbar element. See also Toolbar Element (View) for more information.) Because negative values should be red regardless of the toolbar type, you must make markup changes in both branches.

Note also, that the template calls another template, named FieldRef_ValueOf_DisableEscape, to actually render the value. The latter template is also in fldtypes.xsl and is defined as follows.

<xsl:template name="FieldRef_ValueOf_DisableEscape" ddwrt:dvt_mode="body">
  <xsl:param name="thisNode" select="."/>
  <xsl:value-of disable-output-escaping="yes" select="$thisNode/@*[name()=current()/@Name]" />
</xsl:template>

As shown in the example, this template differs from the standard field rendering template that uses the line <xsl:value-of select="$thisNode/@*[name()=current()/@Name]" /> to render the field value—only in that it adds the attribute disable-output-escaping="yes", which is explained earlier in this topic.

Within the <xsl:when test="$FreeForm"> element, add a choose-when-otherwise structure, and move the existing call to the FieldRef_ValueOf_DisableEscape template into the <xsl:otherwise> element as shown in the following example.

<xsl:when test="$FreeForm">
  <xsl:choose> 
    <when test=""> 

    </when> 
    <otherwise>
        <xsl:call-template name="FieldRef_ValueOf_DisableEscape">
          <xsl:with-param name="thisNode" select="$thisNode"/>
        </xsl:call-template>
    </otherwise> 
  </choose>
</xsl:when>

The test value should be true whenever the value of the current Net_x0020_Profit_x002f_Loss field is negative; that is, whenever the value is less than zero. You have already seen that $thisNode/@*[name()=current()/@Name] evaluates to the current field. However, currency fields in SharePoint Foundation get a low-level formatting even before any XSLT formatting is applied. Specifically, a negative currency value is expressed with parentheses instead of minus sign. For example, a loss of $497,882.87 is expressed as ($497,882.87) rather than -$497,882.87. This creates a problem because the XSLT processor treats any parenthetical expression as being greater than 0, so you cannot directly compare the value of $thisNode/@*[name()=current()/@Name] with zero. Fortunately, for Currency field types, SharePoint Foundation adds to the markup in the thisNode parameter a second version of the field value as a simple double type value which, when negative, is expressed with a minus sign. Specifically, this version is the value of an attribute of the Row element that has the same name as the Net_x0020_Profit_x002f_Loss attribute, except that a single "." is appended to the end of the attribute name. For example, compare the "Retail_x0020_Price" and "Retail_x0020_Price." attributes of the Row element in the example of "thisNode" in Examples of Input and Result Node Trees in XSLT Transformations.

Use this attribute with the dot-ended name to test for a negative value. The test expression should read as follows.

<xsl:when test="$thisNode/@*[name()=concat(current()/@Name, '.')] &lt; 0">

Tip

The "&lt;" must be used instead of "<" because the latter would appear to the XSLT processor to be the beginning of an element. If you prefer, you can reverse the operands, which allows you to use a simple ">" sign: "0 > $thisNode/@*[name()=concat(current()/@Name, '.')]".

Now, inside the inner when structure, copy the call to the FieldRef_ValueOf_DisableEscape template, but wrap it in HTML literals that color the value red. The following shows what the outer when structure should now look like.

<xsl:when test="$FreeForm">
  <xsl:choose> 
    <xsl:when test="$thisNode/@*[name()=concat(current()/@Name, '.')] &lt; 0"> 
      <span style="color:red">
        <xsl:call-template name="FieldRef_ValueOf_DisableEscape">
          <xsl:with-param name="thisNode" select="$thisNode"/>
        </xsl:call-template>
      </span>
    </xsl:when> 
    <xsl:otherwise>
      <xsl:call-template name="FieldRef_ValueOf_DisableEscape">
        <xsl:with-param name="thisNode" select="$thisNode"/>
      </xsl:call-template>
    </xsl:otherwise> 
  </xsl:choose>
</xsl:when>

As noted earlier, the field should render the same color regardless of the type of toolbar, so the entire inner choose structure should become the contents of the div element inside the otherwise part of the outer choose structure. Doing so produces the following as the template definition.

<xsl:template match="FieldRef[@Name='Net_x0020_Profit_x002f_Loss']" mode="Number_body">
    <xsl:param name="thisNode" select="."/>

  <xsl:choose>
    <xsl:when test="$FreeForm">
      <xsl:choose> 
        <xsl:when test="$thisNode/@*[name()=concat(current()/@Name, '.')] &lt; 0"> 
          <span style="color:red">
            <xsl:call-template name="FieldRef_ValueOf_DisableEscape">
              <xsl:with-param name="thisNode" select="$thisNode"/>
          </xsl:call-template>
          </span>
        </xsl:when> 
        <xsl:otherwise>
          <xsl:call-template name="FieldRef_ValueOf_DisableEscape">
            <xsl:with-param name="thisNode" select="$thisNode"/>
          </xsl:call-template>
        </xsl:otherwise> 
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>    
      <div align="right">
        <xsl:choose> 
          <xsl:when test="$thisNode/@*[name()=concat(current()/@Name, '.')] &lt; 0"> 
            <span style="color:red">
              <xsl:call-template name="FieldRef_ValueOf_DisableEscape">
                <xsl:with-param name="thisNode" select="$thisNode"/>
            </xsl:call-template>
            </span>
          </xsl:when> 
          <xsl:otherwise>
            <xsl:call-template name="FieldRef_ValueOf_DisableEscape">
              <xsl:with-param name="thisNode" select="$thisNode"/>
            </xsl:call-template>
          </xsl:otherwise> 
        </xsl:choose>
      </div>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template

You can make this more maintainable by using XSLT variables and parameters to encapsulate the repetitive parts, and by nesting templates. The following is a version of the style sheet that uses these techniques.

<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema"
                xmlns:d="https://schemas.microsoft.com/sharepoint/dsp"
                version="1.0"
                exclude-result-prefixes="xsl msxsl ddwrt"
                xmlns:ddwrt="https://schemas.microsoft.com/WebParts/v2/DataView/runtime"
                xmlns:asp="https://schemas.microsoft.com/ASPNET/20"
                xmlns:__designer="https://schemas.microsoft.com/WebParts/v2/DataView/designer" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                xmlns:SharePoint="Microsoft.SharePoint.WebControls"
                xmlns:ddwrt2="urn:frontpage:internal">

  <xsl:template match="FieldRef[@Name='Net_x0020_Profit_x002f_Loss']" mode="Number_body">
    <xsl:param name="thisNode" select="."/>
    
    <xsl:variable name="FieldValue">   
      <xsl:call-template name="FieldRef_ValueOf_DisableEscape">
        <xsl:with-param name="thisNode" select="$thisNode" />
      </xsl:call-template>
    </xsl:variable>
           
    <xsl:variable name="ValueIsNegative">
      <xsl:value-of select="$thisNode/@*[name()=concat(current()/@Name, '.')]  &lt; 0" />
    </xsl:variable>
    
    <xsl:choose>
      <xsl:when test="$FreeForm">
        <xsl:call-template name="RedWhenNegative_ElseBlack" >  
             <xsl:with-param name="thisNode" select="$thisNode" />
             <xsl:with-param name="ValueIsNegative" select="$ValueIsNegative" />
               <xsl:with-param name="FieldValue" select="$FieldValue" />
          </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>    
        <div align="right">
          <xsl:call-template name="RedWhenNegative_ElseBlack" >  
        <xsl:with-param name="thisNode" select="$thisNode" />
        <xsl:with-param name="ValueIsNegative" select="$ValueIsNegative" />
        <xsl:with-param name="FieldValue" select="$FieldValue" />
              </xsl:call-template>
        </div>
      </xsl:otherwise>
    </xsl:choose>
    
  </xsl:template>
  
  <xsl:template name="FieldValueInRed">
    <xsl:param name="thisNode" select="." />
    <xsl:param name="FieldValue" select="." />
    
    <span style="color:red">
      <xsl:value-of select="$FieldValue" />
    </span>
    
  </xsl:template>
  
  <xsl:template name="RedWhenNegative_ElseBlack">
    <xsl:param name="thisNode" select="." />
    <xsl:param name="ValueIsNegative" select="." />
    <xsl:param name="FieldValue" select="." />
    
    <xsl:choose> 
      <xsl:when test="$ValueIsNegative='true'">
          <xsl:call-template name="FieldValueInRed">
            <xsl:with-param name="thisNode" select="$thisNode" />
                <xsl:with-param name="FieldValue" select="$FieldValue" />
          </xsl:call-template>      
      </xsl:when> 
      <xsl:otherwise>
        <xsl:value-of select="$FieldValue" />
      </xsl:otherwise> 
    </xsl:choose>

  </xsl:template>
  
</xsl:stylesheet>

Figure 1 shows how the field appears on a list.

Figure1.: A currency field with negative values in red

Currency column with a negative value in red.

See Also

Tasks

Walkthrough: Creating a Custom Field Type

Concepts

Custom Field Types