In the previous article we looked at what rendering templates were and how rendering templates work in general. We will continue looking deeper into rendering templates and also implement a scenario which we discussed in the previous article. We will discuss the associated source code minimally so that we can devote more time into discussing the general architecture of rendering templates. That will, I hope, provide the reader with more knowledge to go ahead and build their own implementations.
The SPControlTemplateManager Class
In the end of the previous article, we discussed how rendering templates are instantiated in a container and we also briefly touched upon the SPControlTemplateManager class with a promise of looking at it a bit more deeper in this article. We will look at it now.
The SPControlTemplateManager is defined as a static class within Microsoft.SharePoint assembly under Microsoft.SharePoint.WebControls namespace. The class is not instantiated and it’s sole function is to load, cache and instantiate templates when requested by the other UI infrastructure elements within SharePoint at runtime. If you were to open up this class in Reflector, you will find a main public static method called GetTemplateByName accepting a single string parameter with the name of the template to return.
In the previous article we looked at how the default set of templates used by SharePoint infrastructure is stored within DefaultTemplates.ascx (or MobileDefaultTemplates.ascx if using mobile web browser). We also studied the structure of the file and found that each template has a specific unique ID to identify it in the grand scheme of SharePoint things. When the first request for a template with an ID comes through to SPControlTemplateManager, all the UserControl files living within CONTROLTEMPLATES folder in the SharePoint Hive are parsed and all RenderingTemplate controls are initialized and are converted to ITemplate instances and cached within an internal static Hashtable with ID being the key. We must be aware that the class doesn’t parse through the sub-folders within the CONTROLTEMPLATES folder.
After initially loading the default template files, the caching mechanism proceeds to load other .ascx files living within the folder. If it finds templates with the same name as the default one (for e.g. ListForm) then the default one is overwritten with the newly loaded one. If you were to enable “Verbose” logging for the “Web Controls” category you will see trace entries in the ULS logs that explains which template was overridden by the ones in which file. This is the technique we will be using to override a document libraries editing form with our own.
The Implementation Scenario
I think we have enough information and knowledge now to build our own little rendering template implementation. Following will be the scenario we will attempt to implement:
Metadata for documents living within any document library should not be editable when a workflow is active on the document.
The scenario is simple enough, so what options do we have to implement it? We can use:
a) Create a custom web part and add to the web part page for the library which can be built using Visual Studio and can execute code behind methods to check for workflows running for the item and disable the controls within it if one is active.
b) An ASP.NET page which does pretty much the same as described in point (a) above.
c) Use our own rendering template to override the default one and do what we described in point (a) above.
The best solution?
We will look at it one by one now to see what fits and why.
Form Web Part: SharePoint 2010 allows editing document library pages using the browser. With this option you may create a web part that provides the function of checking for workflow status and dynamically update the list form web part on the same page. This is too involved, prone to errors and is not a “clean” solution. It is not enough that the web part is created, it becomes a manual task to add this web part to all form pages for all document libraries.
ASP.NET Page: An ASP.NET page which presents the form to update the document metadata with additional functionality to check for workflow status. This is also not workable because: A lot of cumbersome code needs to be written to attach the form to all document libraries. There is also the question of how to deal with changes to the document metadata definition, such as addition, modification and deletion of fields.
Rendering Templates: This is the option we are interested in and the reason will be obvious when we implement a solution. However, let me try to list out the points that work in favour of this solution:
1) The solution is truly “plug-and-play”. The functionality is added when our solution package is deployed and removed when our solution package is uninstalled – with NO additional code to achieve it (i.e. Feature Event Receivers, PowerShell scripts and console applications etc.)
2) The solution instantly modifies all document library forms for all documents.
3) The solution is change agnostic: Adding, Modifying or Removing fields will not affect the functioning of this solution.
4) Minimal amount of coding effort required to achieve this functionality.
Building the solution
The attached demo source code contains a full working package that demonstrates what we were discussing in the rest of the article. To build the solution you will need an installation of Visual Studio 2010 with SharePoint Developer Tools installed with at least a minimal installation of SharePoint (Foundation or other version). Once built and deployed, you can see it in action by going to a document library, uploading a document and trying to edit the metadata properties of the document.
Below is a screenshot of the form when a workflow is active on the document:
While without a workflow the form will look and behave like any other document library form.
Solution Code Analysis
When you open up the solution code in Visual Studio 2010, you will see very few physical code files and no features or additional files. This shows how simply and elegantly we can create a complex modification to core SharePoint functions. The 2 files that we have there are:
1) DemoTemplates.ascx within CONTROLTEMPLATES folder
2) CheckWorkflowStatus.cs that defines the form component that actually does the grunt work.
If you open up DemoTemplates.ascx you will see 2 rendering templates defined:
If you were to compare the DocumentLibraryForm in this file to the default implementation in DefaultTemplates.ascx, you will see only one difference – addition of CheckWorkflowStatus component in the form:
<SharePoint:RenderingTemplate id="DocumentLibraryForm" runat="server">
<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbltop" RightButtonSeparator="&#160;" runat="server">
<SharePoint:FormComponent TemplateName="DocumentLibraryFormCore" runat="server"/>
The rest of the form is derived directly from another template called “DocumentLibraryFormCore” which is defined within DefaultTemplates.ascx.
The CheckWorkflowStatus is defined as an inherited control from FormComponent. You will notice how vanilla actually the code is. The few important aspects of this component are discussed below:
The component itself uses a template called “CheckWorkflowStatus” which is defined in DemoTemplate.ascx. The template simply presents some textual information that will be displayed to the user.
The first interesting thing to note is the Visible property override. It checks whether a workflow is active and the ControlMode to make sure we are in the “Edit” form for the document. If both conditions are true then the component is visible otherwise false.
The second place of interest is the OnLoad method which again checks for an active workflow running on the item. If found it walks through the control stack for the parent web part / web control and sets the ControlMode to Display. This will make all the components on the form to read-only making it look like a “View” form – including the “Save” button which becomes invisible.
The work horse of the class is IsWorkflowActive method which checks for active workflows for the item. You will be interested to see the use of this.ListItem which is inherited from the FormComponent base class. This automatically references the currently editing item.
The whole experience of implementation of a rendering template based customizations for SharePoint shows how complex modifications can be performed without a lot of effort or time. I will conclude this article with the following disclaimer: This method of customisation will not be suitable for all situations. However, surgical changes required can be implemented using this method easily and without a lot of disruptive changes to the farm.