I guess, you all know the Tags and Notes functionality of SharePoint 2010 by now. On the My Profile page you can see all your social comments (the tags and notes) you have given on the pages as well as the possibility to leave a note. This Note Board is actually a webpart (SocialCommentWebPart).
I was asked to add this webpart on the display form for a particular list, so users can comment on a list item. If you are not a developer you would probably start SharePoint Designer right now, create a custom Display Form and add the webpart to this form and voilá. But if you are a developer you want this solution to be deployable and maybe generic. So no SharePoint Designer (which, by the way, replaces the original code with it’s own DataViewWebPart and XSLT.)
RenderingTemplates
List forms are using RenderingTemplates. These templates define how controls are rendered. For instance, choice fields can be rendered as a dropdown list or radiobuttons. These templates can be found in the CONTROLTEMPLATES folder in the SharePoint root folder. Opening this folder will show you many ASCX files.
The one we need is the DefaultTemplates.ascx. This file contains many templates, such as CompositeField, BlogForm, CreatedModifiedInfo and ListForm. The last one mentioned is the template we need to customize and add the SocialCommentsWebPart.
In my Visual Studio 2010 SharePoint project I have added a SharePoint Mapped Folder item pointing to the CONTROLTEMPLATES folder. Then I created a new UserControl and deleted all the CodeBehind files. Then I modified the ASCX so no reference is present to the deleted CodeBehind:
<%@ Control Language="C#" AutoEventWireup="false" %>
From the original DefaultTemplates.ascx I copied the ListForm template and renamed it to CustomListForm:
<SharePoint:RenderingTemplate id="CustomListForm" runat="server"> <Template> <span id='part1'> <SharePoint:InformationBar runat="server"/> <div id="listFormToolBarTop"> <wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbltop" RightButtonSeparator="&#160;" runat="server"> <Template_RightButtons> <SharePoint:NextPageButton runat="server"/> <SharePoint:SaveButton runat="server"/> <SharePoint:GoBackButton runat="server"/> </Template_RightButtons> </wssuc:ToolBar> </div> <SharePoint:FormToolBar runat="server" /> <SharePoint:ItemValidationFailedMessage runat="server" /> <table class="ms-formtable" style="margin-top: 8px;" border="0" cellpadding="0" cellspacing="0" width="100%"> <SharePoint:ChangeContentType runat="server"/> <SharePoint:FolderFormFields runat="server"/> <SharePoint:ListFieldIterator TemplateName="ListFieldIterator" runat="server"/> <SharePoint:ApprovalStatus runat="server"/> <SharePoint:FormComponent TemplateName="AttachmentRows" runat="server"/> </table> <table cellpadding="0" cellspacing="0" width="100%"><tr><td class="ms-formline"><img src="/_layouts/images/blank.gif" width='1' height='1' alt="" /></td></tr></table> <table cellpadding="0" cellspacing="0" width="100%" style="padding-top: 7px"><tr><td width="100%"> <SharePoint:ItemHiddenVersion runat="server"/> <SharePoint:ParentInformationField runat="server"/> <SharePoint:InitContentType runat="server"/> <wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbl" RightButtonSeparator="&#160;" runat="server"> <Template_Buttons> <SharePoint:CreatedModifiedInfo runat="server"/> </Template_Buttons> <Template_RightButtons> <SharePoint:SaveButton runat="server"/> <SharePoint:GoBackButton runat="server"/> </Template_RightButtons> </wssuc:ToolBar> </td></tr> <tr><td width="100%" style="height:300px"><PortalWebControls:SocialCommentWebPart runat="server" /></td></tr> </table> </span> <SharePoint:AttachmentUpload runat="server"/> </Template> </SharePoint:RenderingTemplate>
Just before the closing table tag, I added an extra table row and cell to add my SocialCommentWebPart. Since this webpart is in another assembly you need to register it. I used PortalWebControls as the tagPrefix:
<%@ Register TagPrefix="PortalWebControls" Assembly="Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" namespace="Microsoft.SharePoint.Portal.WebControls"%>
(Note: in order to use this SocialCommentWebPart successfully, you’ll have to set up User Profiles for your webapplication)
It is important to deploy your ASCX file in the CONTROLTEMPLATES folder! Do not create a subfolder, it won’t work.
Configure the list definition
Next step is to configure the list to use this custom template. In my solution I have a list definition. In the schema.xml you have the <Forms> tag. Here are the forms defined for displaying, adding and editing items. There is a Template attribute to tell SharePoint which RenderingTemplate to use with that form.
<?xml version="1.0" encoding="utf-8"?> <List xmlns:ows="Microsoft SharePoint" Title="Berichten" FolderCreation="FALSE" Direction="$Resources:Direction;" Url="Lists/Berichten" BaseType="0" xmlns="http://schemas.microsoft.com/sharepoint/"> <MetaData> <ContentTypes> </ContentTypes> <Fields> </Fields> <Views> </Views> <Forms> <Form Type="DisplayForm" Url="DispForm.aspx" SetupPath="pagesform.aspx" WebPartZoneID="Main" Template="CustomListForm" /> <Form Type="EditForm" Url="EditForm.aspx" SetupPath="pagesform.aspx" WebPartZoneID="Main" /> <Form Type="NewForm" Url="NewForm.aspx" SetupPath="pagesform.aspx" WebPartZoneID="Main" /> </Forms> </MetaData> </List>
In my example I have the template CustomListForm set for the DisplayForm. By default, the template ListForm will be used for all list items.
Basically, this is it. However, there is one thing… You probably use content types. I do. That’s why it didn’t work out for me in the first place. So, if you do use content types, and you have a list definition based on a content type, you also have to set your custom Rendering Template for your content type:
<ContentType ID="0x01009A851469C3B5154C843876238F54D125" Name="Bericht" Group="Custom" Inherits="FALSE" Hidden="false" ReadOnly="false" Sealed="false"> <FieldRefs> <FieldRef ID="fa564e0f-0c70-4ab9-b863-0177e6ddd247" Name="Title" DisplayName="Titel" /> <FieldRef ID="d44cb4d4-a259-4a96-b859-dbc769dddf55" Name="Omschrijving" DisplayName="Omschrijving" /> </FieldRefs> <XmlDocuments xmlns="http://schemas.microsoft.com/sharepoint/"> <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"> <FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"> <Display>CustomListForm</Display> <Edit>ListForm</Edit> <New>ListForm</New> </FormTemplates> </XmlDocument> </XmlDocuments> </ContentType>
In the <FormTemplates> element, you have the three forms: <Display>, <Edit> and <New>. Set your custom rendering template and you’re good to go. Remember, this content type XML definition exists in both the element.xml of your Content Type as well as in the Schema.xml of your list definition!
So, how does it look like now? Here’s a screenshot of my Social Comments Webpart in the display form of a list item:
Override OOB templates?
Now, SharePoint offers a lot of rendering templates OOB. Is it possible to override, for example, the default template ListForm? The answer is… Yes! You can. But there’s a catch.
Now, every 2 weeks we Mavens get together to share our knowledge and experience. I was demonstrating this topic about Rendering Templates and my colleague Waldek Mastykarz also used this technique in one of our products (Mavention Anonymous Rating). During this night, it came clear you can override OOB templates simply by using the same name in your ControlTemplate file. So, if you use ListForm for your custom template, then every list would use your customized template. How cool is that? But what is the catch? That night we asked ourselves: what if another 3rd party solution also uses a custom ListForm template? Then you have 3 templates with the same name ListForm. What happens then? So, Waldek said his famous words: “One way to find out…” And he did. It turns out that SharePoint alphabetically sorts all the ControlTemplate files and the first one on that sorted list is the big winner! Hmmm… Conclusion: it is not 100% guaranteed that your overridden custom ListForm will be used. I guess you will need to be smart on how to name your ASCX file.
More information on Rendering Templates can be read here:
Good post. Glad to see more people are finally coming around to see the true power of RenderingTemplates. I’ve written a few posts about this as well. I used these techniques for a few clients.
Thank you, Brian. It is really a great approach for customizing your forms. Besides rendering forms, it is also used for rendering your custom field types. E.g. a numeric field that displays like stars… 😉
Nice post. But there is another alternative to perform the same task using dataform webpart and xslt as shown in this post:
http://blog.symprogress.com/2011/04/sharepoint-2010-add-custom-list-form-to-existing-list/
Do you know how the people picker is rendered? It doesn’t seem to use
The other regular controls use that template.
I’m interested in moving descriptions to the left — as is done in
http://officetoolbox.codeplex.com/
which primarily moves them via customizing CompositeField rendering template — which does not affect people pickers.
“During this night, it came clear you can override OOB templates simply by using the same name in your ControlTemplate file”
DO NOT – This is unsupported – Please see the Note in http://msdn.microsoft.com/en-us/library/aa544154.aspx
Hi Sam,
Thank you for comment, you’re right. ListForm and SurveyForm should be avoided and not overridden.
grtz,
Octavie
Hey,
I saw you added a webpart to your rendering template. I did try but did not work for me. I dont see any details how you did it. Would you mind sharing those details and how you get the parent id in the webpart and how do you save the comments entered in the web part.
Thanks In advance
Hey Sam,
Can you tell me at what point you’re stuck? Remember that you will have to set up the User Profiles Service Application for your web application otherwise the SocialCommentWebPart won’t work.
Did you try to add the webpart first to the DefaultTemplates.ascx, so it will show up in EVERY list? If that works, then you can move on to create your own copy of DefaultTemplates.ascx like I mentioned in the post.
Good luck,
Octavie