Disentangling Render Patterns

I few months ago I posted on RenderPatterns and how they are used when creating custom fields for SharePoint. In that post the point I was trying to make was that when viewing data in a list nothing else matters except the render pattern. None of your highfalutin C# SPField derived classes can save you from the madness that is the render pattern. Never mind what MSDN says about using an .ascx control - there is no other way.

So with that said, given that we’re stuck with the render pattern, it would be good to know a bit more about how it works and how we can use it to display data. First things first, lets call out the relevant documentation from MSDN:

HTML-Rendering Elements – This page lists all the elements that can be used when building rendering patterns. One thing to bear in mind when reviewing this list is that these elements have mostly been implemented for a specific purpose. For example the ForEach element is designed for enumerating choices in a choice based column.

Data-Defining Elements – This page lists all the elements that can be used to extract data from the column that were applying the render pattern to. Again bear in mind that these elements were implemented for a specific purpose and are not really extensible. IfNew for example, is used to add the ‘New’ icon next to recently added items.

MS-DOS for SharePoint

I’m sure if you’re reading this post, you’ve probably already read through the documentation on MSDN and like me, thought “what does all this actually mean?” In my opinion all this stuff is all the more confusing because it bears no resemblance to the object model. It’s practically impossible to see where changes that can be made via the object model correlate with the elements available in CAML. There’s a good reason for this though, CAML is the MS-DOS of SharePoint. It’s the underlying nuts and bolts that makes a lot of the basic operations work. Sure with WSS 3 and MOSS 2007 there’s a shiny new object model built on top in much the same way as Windows 95 (not so shiny these days!) was built on top of MS-DOS, but the important thing to remember is that CAML existed before the object model and has it’s own peculiar ways of doing some things.

Where’s the data?

Let’s take a look at the data that render patterns have to work with. If you create a custom list in SharePoint then export it using SharePoint Solution Generator (part of VSeWSS), you’ll notice that a schema.xml file is created. Examining this file you’ll find elements such as:

<List>
    <MetaData>
    <Views>
        <!--Snipped for brevity-->
    </Views>
    <Fields>
      <Field ID="{03e45e84-1992-4d42-9116-26f756012634}" RowOrdinal="0" Type="ContentTypeId" Sealed="TRUE" ReadOnly="TRUE" Hidden="TRUE" DisplayName="Content Type ID" Name="ContentTypeId" DisplaceOnUpgrade="TRUE" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="ContentTypeId" ColName="tp_ContentTypeId" FromBaseType="TRUE" />
        <!--- etc. -->
    </Fields>
    <!--Snipped for brevity-->
    </MetaData>
</List>

The Field element is the target of the render pattern. If you need to access a value in you render pattern it must be included in the field element, either as an attribute (which as well see is by far the easiest way to use), or as a child element.

Another useful page on MSDN is Field Element (List - Definition). This page lists all the attributes that can be used when creating list schemas. So in the snippet above, more information for each attribute on the Field element can be found here.

Although the documentation lists all the attributes used internally by CAML, custom attributes can be added, This technique is invaluable when it comes to creating custom render patterns.

How can we render the data?

Although the HTML-Rendering Elements and Data-Defining Elements pages document a load of elements that can be used for rendering data, probably the most useful are:

ForEach – This is used to iterate through child nodes. It was designed for use with Choice columns and therefore can only read two levels deep.

So if your field definition looked like this:

<Field Type="XmlFieldType" DisplayName="XmlField" Required="FALSE" ID="{27a82f4a-a76c-4e4e-aa69-ec2859c68971}" SourceID="{480dfeee-f583-4e49-9c15-02800d6f6042}" StaticName="XmlField" BaseRenderingType="Note" Name="XmlField" ColName="ntext2" RowOrdinal="0" Group="" Version="1">
<Customization>
  <ArrayOfProperty>
    <Property>
      <Name>ValidationSchemaXml</Name>
      <Value xmlns:q1="http://www.w3.org/2001/XMLSchema" p4:type="q1:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance" />
    </Property>
    <Property>
      <Name>RenderXml</Name>
      <Value xmlns:q2="http://www.w3.org/2001/XMLSchema" p4:type="q2:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">&lt;template&gt;This could be anything&lt;/template&gt;</Value>
    </Property>
  </ArrayOfProperty>
</Customization>
</Field>

In order to render the Name elements it would be necessary to use three nested ForEach elements:

<ForEach Select="Customization/ArrayOfProperty">
    <ForEach Select="Property">        
      <ForEach Select="Name">
        Render Pattern           
      </ForEach>          
    </ForEach>
</ForEach>

Why three you say? Surely two would do the job if ForEach can read two levels deep? Of course this would be true if the Select attribute contained an XPath statement, but sadly it doesn’t. Since ForEach was designed for iterating through Choice elements where there is always a single parent with multiple child elements, ForEach doesn’t properly handle multiple parent elements. Using Select=Property/Name would iterate through the Name elements of the first Property element only.

Property – The Property element is used to read attribute values from the Field element or the currently selected child element. Also using <Property Select=”.”> will output the inner text of the currently selected element.

For example, if the field element looked like this:

<Field Type="XmlFieldType" DisplayName="XmlField" Required="FALSE" ID="{27a82f4a-a76c-4e4e-aa69-ec2859c68971}" SourceID="{480dfeee-f583-4e49-9c15-02800d6f6042}" StaticName="XmlField" BaseRenderingType="Note" Name="XmlField" ColName="ntext2" RowOrdinal="0" Group="" Version="1">
    <CustomValues>
      <Foo bar="abc">def</Foo>
      <Foo bar="ghi">jkl</Foo>
    </CustomValues>
</Field>

Using a render pattern like this:

<ForEach Select="CustomValues/Foo">
    <Property Select="bar"/>
    <Property Select="."/>
</ForEach>

 

Would output abcdefghijkl.

I’ve defined custom properties on my field. How can I use them in my render pattern?

You’d expect it to be simple to use custom properties in a render pattern. After all they’re defined using CAML in the fldtypes_whatever.xml file. However for some bizarre reason, when custom property values are attached to field definitions they are implemented as a series of Name/Value pairs like the example above. Of course this wouldn’t be a problem if we had the luxury of XPath but given the limited syntax of the ForEach element it’s practically impossible to extract the value for a particular custom property.

In the example above, to extract the value of the RenderXml property we could try something like this:

<ForEach Select="Customization/ArrayOfProperty">
    <ForEach Select="Property">        
      <ForEach Select="Name">            
        <IfEqual>
          <Expr1>
            <Property Select="."/>
          </Expr1>
          <Expr2>RenderXml</Expr2>
          <Then>
            Do Something                   
          </Then>             
        </IfEqual>            
      </ForEach>          
    </ForEach>
</ForEach>

 

However this doesn’t solve the problem. Even though we’ve identified the Name element with the value RenderXml, there’s no way to navigate back up the tree to the parent Property element in order to get the value of the Value element!

In short, there’s no way to access these values without making some modifications. (Not strictly speaking true, I suppose you could use SetVar and GetVar to implement a counter or something but I’ll leave that as an exercise for the truly masochistic!)

All fine and well, but how do I use this information?

You’ve basically got two choices, either manually edit the field elements in schema.xml to suit your requirements or use the object model to edit the underlying schema. To use the object model, the schema for each field can be accessed via the SPField.SchemaXml property.

Ultimately this problem occurs because of the default implementation of GetCustomProperty and SetCustomProperty on the SPField object. By overriding these implementations is should theoretically be possible to persist custom properties in a manner that can be more easily accessed using render patterns. (I say theoretically because I haven’t tried it yet and I’ve leaned never to make assumptions when it comes to the internal workings of SharePoint!)

I hope this information makes render patterns a bit more accessible. If i get some time I’ll try out my theory and post the findings. For now it’s onward with chapter 16 of my new SharePoint 2010 book.