ACF 2010: Announcing the Advanced Computed Field for SharePoint 2010


May 26, 2011 - 16:07, by Steven Van de Craen - 1 Comments

Finally available: the SharePoint 2010 version of the Advanced Computed Field. Don’t know what it is ? Check this out:

http://ventigrate.codeplex.com/wikipage?title=Advanced%20Computed%20Field

ACF2010

(the Advanced Computed Field rendering a highlighted/italic item title)


Custom Lookup Field Types: migration from 2007 to 2010


March 4, 2011 - 17:23, by Steven Van de Craen - 4 Comments

Custom Field Types are a rather advanced topic, but very powerful as well. They allow for real integration of custom components inside standard List and Library rendering. (See my other posts on Custom Field Types)

There are some things you can run into. Especially if you have custom Lookup Field Types like Cascading Lookup or Connected Lookup. Here’s what I’ve learned from a recent issue migration one from 2007 to 2010.

Custom Properties

Storing additional information in your Custom Field Types was always a real pain. The mechanism had serious flaws and it required some really dirty programming to work with. So instead I store my custom properties the same way SharePoint does. The involved API is internal but can be exposed through reflection.

I’m adding the this ‘reimplementation’ to every Custom Field Type of mine. When providing a null value to SetCustomProperty, the attribute is removed from the field XML.

1 #region Reimplementation of Get/Set Custom Property 2 public new void SetCustomProperty(string propertyName, object propertyValue) 3 { 4 Type type = typeof(SPField); 5 if (propertyValue != null) 6 { 7 MethodInfo set = type.GetMethod("SetFieldAttributeValue", BindingFlags.NonPublic | BindingFlags.Instance); 8 set.Invoke(this, new object[] { propertyName, propertyValue.ToString() }); 9 } 10 else 11 { 12 MethodInfo remove = type.GetMethod("RemoveFieldAttributeValue", BindingFlags.NonPublic | BindingFlags.Instance); 13 remove.Invoke(this, new object[] { propertyName }); 14 } 15 } 16 public new string GetCustomProperty(string propertyName) 17 { 18 Type type = typeof(SPField); 19 MethodInfo getField = type.GetMethod("GetFieldAttributeValue", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(String) }, null); 20 object o = getField.Invoke(this, new object[] { propertyName }); 21 return o as String; 22 } 23 #endregion

Now there’s also no more need to predefine custom properties in the FLDTYPES_*.xml either. This applies to any type of Custom Field Type.

AllowMultipleValues

You might see in some cases that when you set the value of SPFieldLookup.AllowMultipleValues to true, your custom field is reverted to a standard Lookup field.

Multi Lookup Custom Field Type reverted to Lookup

If this is the case, you could override that Property and implement it accordingly.

1 private bool _allowMulti = false; 2 public override bool AllowMultipleValues 3 { 4 get 5 { 6 bool.TryParse(GetCustomProperty("AllowMultipleValues"), out _allowMulti); 7 return _allowMulti; 8 } 9 set 10 { 11 if (this.AllowMultipleValues != value) 12 { 13 _allowMulti = value; 14 SetCustomProperty("AllowMultipleValues", _allowMulti.ToString()); 15 if (value) 16 { 17 SetCustomProperty("Mult", "TRUE"); 18 SetCustomProperty("Sortable", "FALSE"); 19 } 20 else 21 { 22 SetCustomProperty("Mult", "FALSE"); 23 SetCustomProperty("Sortable", "TRUE"); 24 } 25 } 26 } 27 }

This applies to custom Multi Lookup Field Types.

Note: could not reproduce the issue with a new custom lookup field type in a clean SharePoint 2010 environment…

AllowBaseTypeRendering

We’ve seen this issue in a recent migration from SharePoint 2007 to SharePoint 2010 for a custom developed Connected Lookup Field (Single and Multi Valued). The field was ported from SharePoint 2007 with the following FLDTYPES_*.xml configuration:

FLDTYPES_MyCFT.xml

The AllowBaseTypeRendering attribute will default to the rendering of the Base Type (in this case Lookup) when the Custom Field Type cannot be determined.

This had the weird effect that these fields would not render or store *any* value selected in a Multi Valued Field of this type. This behaviour might only be occuring when there’s a custom RenderPattern defined for the Custom Field Type.

In either way we had to remove the attribute for SharePoint 2010, since we really required the RenderPattern. Problem solved for new fields, but there was still an issue for existing fields (existing SharePoint 2007 content migrated to SharePoint 2010).

For those existing fields, we wrote a script that would remove the attribute causing the problem:

1 SPList list = web.GetList(listUrl); 2 SPField field = list.Fields["MyMULTI"]; 3 MyField clf = field as MyField; 4 clf.SetCustomProperty("BaseRenderingType", null); 5 clf.Update();

This is where the custom SetCustomProperty came in handy for removing the BaseRenderingType attribute (added because the Custom Field Type had the AllowBaseTypeRendering attribute).

In our case we knew exactly where the fields were that had our Custom Field Type, but it might be more difficult to write a generic script to find all fields based on the Custom Field Type…

Note: could not reproduce the issue with a new custom lookup field type in a clean SharePoint 2010 environment…

Enforce unique values

You can force unique values on fields in SharePoint 2010, however is is not compatible when storing multiple values.

Enforce unique values

If this is the case your custom “edit field control” should disable the option to “Enforce unique values” via JavaScript.

 

Oh, and for those wondering: you still can’t do cross site collection lookups with a lookup field. Which makes me wonder how that works in regard to the Content Type Hub.


Conditional Formatting using the Advanced Computed Field


November 7, 2009 - 11:31, by Steven Van de Craen - 2 Comments

I got a question on how to use the Advanced Computed Field for conditional formatting and when I finished writing up the response I figured I might as well share it with the community, being you all :)

Here’s the config I used:

<FieldRefs>
<FieldRef Name="TestField" /> </FieldRefs> <DisplayPattern>

<Switch> <Expr>
    <Field Name="TestField" /> </Expr>
   <Case Value="value1">
     <HTML><![CDATA[<div style='color: green;'><b>GREEN</b></div>]]></HTML> </Case> <Case Value="value2"> <HTML><![CDATA[<div style='color: blue;'><b>BLUE</b></div>]]></HTML> </Case> <Case Value="value3"> <HTML><![CDATA[<div style='color: red;'><b>RED</b></div>]]></HTML>
  </Case>
   <Default> <HTML><![CDATA[<div style='color: brown;'><b>BROWN</b></div>]]></HTML> </Default>
</Switch> </DisplayPattern>


Ellipsis on list item body using jQuery


October 1, 2009 - 22:35, by Steven Van de Craen - 0 Comments

A common question to receive is on list items with a long body (eg. Announcements) and then show only X characters and optionally a ‘read more’ link. Many ways to solve this but I went for the following: a Custom Field Type that renders the short text with ellipsis using jQuery.

This is another example using the Advanced Computed Field in a creative way. The ACF allows you to create computed fields referencing other fields or data in your list and manipulating them through CAML or JavaScript. It has the advantage that this can be done from within the browser, however I admit CAML isn’t the easiest of things to comprehend in the SharePoint technology stack.

Ellipsis sample

This ellipsis displays the first 20 characters of the plain text version of the Announcement Body

<FieldRefs> 
     <FieldRef Name="Body" />
     <FieldRef Name="ID" />
</FieldRefs>

<DisplayPattern>
     <HTML>&lt;span style="display:none;" id="el_</HTML>
     <GetVar Name="WPQ" />
     <HTML>_</HTML>
     <Field Name="ID" />
     <HTML>"&gt;</HTML>
     <Field Name="Body" />
     <HTML>&lt;/span&gt;</HTML>
     <HTML>
     &lt;script type="text/javascript"&gt;
     $(function()
     { var mySpan = $('#el_</HTML>
     <GetVar Name="WPQ" />
     <HTML>_</HTML>
     <Field Name="ID" />
     <HTML>');
       var myShortText = $(mySpan).text().substring(0, 20); 
       if ($(mySpan).text().length &gt; 20)
         myShortText += '...';

       $(mySpan).show().html(myShortText); });
     &lt;/script&gt;</HTML>
</DisplayPattern>

Notes
  • If you prefer to not escape the brackets you can use CDATA tags: <HTML><![CDATA[<b>hello</b>]]></HTML>
  • GetVar ‘WPQ’ returns the unique Web Part ID. This takes care of multiple List View Web Parts on the same page
  • You could optimize the output by extracting most of the script and placing it in an externally referenced JavaScript file
  • My JavaScript/jQuery skills aren’t the best. I’ll have to ask Jan for some jQuery pointers next time :)


Advanced Computed Field


March 31, 2009 - 21:34, by Steven Van de Craen - 31 Comments

Introduction

This project originally started as ‘TitleLinkField’ because I needed a way to display the Title field as a hyperlink to the document in a document library, but it ended up being more than just that so I chose a more generic name for it.

I had some experience with Custom Field Types but event then I spent too many hours (even days) on figuring this one out. It started with standard functionality such as Calculated Field and Computed Field, both having their flaws and limitations. I quickly realised that Custom Field Types might be the only way to tackle the scenario at hand.

Use

When creating a field based on this type (“Advanced Computed Field”) you need to provide two properties; FieldRefs (MSDN FieldRefs Element (List)) and DisplayPattern (MSDN: DisplayPattern Element (List)). The former requires a list of referenced fields that exist in the collection of our field while the latter contains CAML used for displaying our field. See the MSDN pages on both elements for the schema and possible values.

What better way than demonstrating by showing ?

Sample 1: Title linked to document

<FieldRefs>
     <FieldRef Name="ServerUrl" />
     <FieldRef Name="Title" />
</FieldRefs>

<DisplayPattern>
     <HTML><![CDATA[<a href="]]></HTML>
     <Field Name="ServerUrl" />
     <HTML><![CDATA[" title="]]></HTML>
     <Field Name="ServerUrl" />
     <HTML><![CDATA[">]]></HTML>
     <Field Name="Title" />
     <HTML><![CDATA[</a>]]></HTML>
</DisplayPattern>

Sample 2: Title with ECB

<FieldRefs>
     <FieldRef Name="Title" />
     <FieldRef Name="_EditMenuTableStart" />
     <FieldRef Name="_EditMenuTableEnd" />
</FieldRefs>

<DisplayPattern>
     <FieldSwitch>
         <Expr>
              <GetVar Name="FreeForm" />
          </Expr>
          <Case Value="TRUE">
               <HTML>-</HTML>
          </Case>
          <Default>
               <Field Name="_EditMenuTableStart" />
               <SetVar Name="ShowAccessibleIcon" Value="1" />
               <Field Name="Title" />
               <SetVar Name="ShowAccessibleIcon" Value="0" />
               <Field Name="_EditMenuTableEnd" />
          </Default>
     </FieldSwitch>
</DisplayPattern>

Sample 3: Random formatting

<FieldRefs>
     <FieldRef Name="MyChoiceField" />
</FieldRefs>

<DisplayPattern>
     <HTML><![CDATA[<span style="background-color: red;">]]></HTML>
     <Field Name="MyChoiceField" />
     <HTML><![CDATA[</span><input type="button" value="Click me" />]]></HTML>
</DisplayPattern>

You can learn a lot by examining the default fields in a document library or list. A tool such as Stramit SharePoint 2007 Caml Viewer is invaluable here.

Technical

A Custom Field Types derives from a parent class (MSDN: Custom Field Classes). Furthermore you have to define the ParentType field declaratively in the FieldTypes XML definition. It is advised (maybe even required) for them to be the same type.

Choose the ‘parent class’ that most closely matches your requirements. If you need a ‘text field with regular expression validation’ then go for SPFieldText because that has a lot of textbox relation functionality (especially on the New, Edit and Display Forms).

The ‘ParentType’ field seems to be the key for CAML related functionality (such as the AllItems View). In my case setting it to “Text” meant that my field didn’t retrieve the values of the referenced fields so it needed to be “Computed”.

The main issues with the standard Computed Field is that it is invisible from any view; Site Columns, Field in a Content Type, etc. and can only be created declaratively via XML/CAML or through code. Other than these issues the Computed Field did exactly what was needed; render columns in a way that I wanted via the RenderPattern. It turns out that the visibility of a Custom Field Type can be controlled in the declarative XML via the ”ShowOn…” fields.

For storing and retrieving the DisplayPattern and FieldRefs properties my custom field type exposes two methods that make calls to internal SharePoint methods:

public void SetInnerXmlForNode(string nodeName, string innerXml)
{
     Type fldType = this.GetType();
     XmlDocument doc = new XmlDocument();
     doc.LoadXml(String.Format("<{0}>{1}</{0}>", nodeName, innerXml));

     MethodInfo miSetXml = fldType.GetMethod("SetInnerXmlForNode", BindingFlags.Instance | BindingFlags.NonPublic);
     miSetXml.Invoke(this, new object[] { nodeName, doc.DocumentElement.InnerXml });
}

public string GetInnerXmlForNode(string nodeName)
{
     string result = null;
     Type fldType = this.GetType();

     MethodInfo miGetXml = fldType.GetMethod("GetNodeFromXmlDom", BindingFlags.Instance | BindingFlags.NonPublic);
     XmlNode resultNode = miGetXml.Invoke(this, new object[] { nodeName }) as XmlNode;
     if (resultNode != null)
     {
          StringBuilder sb = new StringBuilder();
          XmlWriterSettings settings = new XmlWriterSettings();
          settings.Indent = true;
          settings.ConformanceLevel = ConformanceLevel.Fragment;
          using (XmlWriter writer = XmlTextWriter.Create(sb, settings))
          {
               resultNode.WriteContentTo(writer);
          }

          result = sb.ToString();
     }
     return result;
}

Download and installation

If you’re interested in reusing or modifying the code feel free to do so. If you just want this installed and available on your SharePoint farm then go for the WSP and deploy via STSADM.

STSADM -o addsolution -filename VNTG.CustomFieldTypes.AdvancedComputedField.wsp
STSADM -o deploysolution -name VNTG.CustomFieldTypes.AdvancedComputedField.wsp -allowgacdeployment -immediate -allcontenturls

Word of caution

There isn’t a lot of validation on the input of the XML properties. It has to be valid XML but that’s about it. Please follow the schema for FieldRefs and DisplayPattern to make sure you don’t break other functionality. It cannot ‘bring down the farm’ or anything, but it could definitely mess up the rendering of the View.

Have fun with it !


View Action Button


June 20, 2008 - 17:24, by Steven Van de Craen - 13 Comments

Introduction

Don't you just miss the possibility to have buttons on a SharePoint List or Library View similar to an ASP.NET GridView ? You could add Edit or Delete functionality or even cooler things such as navigating to a LAYOUTS page rather than having to use the item context menu drop down:

I've been playing with Custom Field Types in order to have this kind of functionality and I'm offering the result to you for free !!

Solution

The ViewActionButton Custom Field Type allows you to render a button or hyperlink to a Web Relative Url. The Item ID and List ID are automatically appended for each item so the receiving page can interact with them accordingly.

Some standard actions include:

  • Workflows: /_layouts/Workflow.aspx?ID=1&List={4F56D0B2-1F4E-46FE-A794-5A6BAAC0CACD}
  • Version History: _layouts/Versions.aspx?list={4F56D0B2-1F4E-46FE-A794-5A6BAAC0CACD}&ID=1

But you could also provide your own actions via custom LAYOUT pages:

  • Delete item: /_layouts/Vandest/DeleteItem.aspx?ID=1&List={4F56D0B2-1F4E-46FE-A794-5A6BAAC0CACD}
  • Generate PDF: /_layouts/Vandest/GeneratePDF.aspx?ID=1&List={4F56D0B2-1F4E-46FE-A794-5A6BAAC0CACD}

Once installed you can add as many fields of this type as you want and configure them accordingly:

  • Action URL: The Web Relative Url to a page. The field will automatically append the ListID and ItemID QueryString parameters
  • Format: Specify to render either a button or hyperlink
  • CSS-Class: The CSS class for the rendered item. Use this to further style the button or hyperlink

Notes

Issue #1

There were several headaches involved with this project. The first one occurred when I found out that the Custom Property mechanism doesn't really work the way it is supposed to. Normally you declare your CustomProperties in the Xml and then use this.SetCustomProperty(name, value) but there's a huge workaround required to make this work:

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1109290&SiteID=1

I'm not using the workaround described as above but I use the saving mechanism from the out of the box field properties...

Issue #2

After that the last remaining obstacle was CAML (Collaborative Application Markup Language). The List View doesn't work with User Controls but requires you to write CAML, HTML and JavaScript and it can be quite scary at first. Good places to start:

  • C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML\FLDTYPES.XML (Take a look at the existing fields how they are rendered)
  • http://msdn.microsoft.com/en-us/library/ms443288.aspx
  • For an overview of CAML Variables go here: http://msdn.microsoft.com/en-us/library/ms480526.aspx
    • Quick Reference:
      • GetVar: can be used in combination with SetVar to store variables on the server.
      • Column: when providing the 'Name' it can be used to get the values of other columns for the item (eg. <Column Name="ID" />)
      • Property: get the value of the field property such as name and custom properties (eg. <Property Select="DisplayName" />)
      • ListProperty: get the value of the list property (eg. <ListProperty Select="DefaultViewUrl" />)
      • ProjectProperty: get the value of the web property (eg. <ProjectProperty Select="Url" />)
      • URL: get the URL for a specific Command (eg. <URL Cmd='New' />)
      • ...
Invaluable tools and resources

Download

Installation using the WSP should be a breeze using addsolution and deploysolution. It will place the assembly in the GAC and the resource files in the correct location in the 12 hive.

 

Feel free to use and improve this :)