When starting to build MOSS sites, many developers will start the page template development process by analyzing the shipped master pages and/or using the 'How to create a minimal master page' MSDN article. Whilst very useful, for simplicity the example in the article unfortunately does not include some of the functionality the SharePoint team implemented in default.master, specifically the use of the Delegate Control for the page to load controls. I think this is a shame, since many developers who don't take the time to specifically look at it may therefore be unaware of how powerful the Delegate Control can be.
So what is it?
Essentially the Delegate Control provides an alternative to adding user controls and server controls to a .aspx page in the normal way. By this, I mean adding the control to the page by dragging from the toolbox in the development environment or by adding the appropriate markup manually. Instead, with a Delegate Control all we do is add the markup to instantiate a Delegate Control instance, and use a Feature to specify separately which control should actually be loaded. What this effectively gives us is the ability to control what controls are used on a page without having to directly modify and redeploy master pages/page layouts. This extra level of abstraction can be quite powerful, since any feature scope can be used with the Delegate Control. I'll come back to this, but enough theory for now. Here's how it's used:
First off, we need a Delegate Control declaration on our page. This will look something like:
<SharePoint:DelegateControl runat="server" ControlId="PageHeader">
</SharePoint:DelegateControl>
Only thing to note here at this stage is the ControlId attribute - the Feature we create will use this to substitute the real user/server control.
Then we have the feature.xml file, where we specify the feature details (including scope):
<Feature xmlns="http://schemas.microsoft.com/sharepoint/" Id="373042ED-718D-46e2-9596-50379DA4D522"
Title="COB.Demos.DelegateControls"
Description="Specifies which user control should be used for the 'PageHeader' DelegateControl used on the site master page. The replacement user control is stored in the CONTROLTEMPLATES directory." Scope="Farm"
Hidden="FALSE"
Version="1.0.0.0">
<ElementManifests>
<ElementManifest Location="elements.xml"/>
</ElementManifests>
</Feature>
As always, the 'instructions' for the feature are in the element manifest:
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<!-- using a sequence number LOWER than default of 100 so our custom control gets loaded -->
<Control Id="PageHeader" Sequence="90" ControlSrc="~/_ControlTemplates/COBPageHeader.ascx" />
</Elements>
This is where we specify which control should actually be used. In addition to specifying the path, the key thing is that the 'Sequence' attribute contains a value lower than any other <Control> instructions for this control (i.e. ID of 'PageHeader'). This is especially important when we are overriding an existing Delegate Control created by Microsoft - here the default value is 100 so your sequence must be lower than this for your control to be loaded instead.
In addition to creating and activating this feature, the actual .ascx file must exist in the location specified. It can be copied to the CONTROLTEMPLATES directory manually, but a better idea is to wrap the feature up as a solution, since solution packages can also deploy files. To deploy the .ascx file along with the solution, your solution manifest file should look something like:
<Solution xmlns="http://schemas.microsoft.com/sharepoint/" SolutionId="E8694626-60F8-4d07-9140-8F9F634020DE">
<FeatureManifests>
<!-- note this is the location in the cab file! -->
<FeatureManifest Location="COB.Demos.DelegateControls\feature.xml" />
</FeatureManifests>
<TemplateFiles>
<TemplateFile Location="CONTROLTEMPLATES\COBPageHeader.ascx" />
</TemplateFiles>
</Solution>
These files would now all be packaged as a solution (.wsp) in the usual way with makecab.exe. When the solution is deployed, the file will be copied to the right place on all the web front-ends in your farm, and when the feature is activated SharePoint will know that it should henceforth load 'COBPageHeader.ascx' for any Delegate Control with an ID of 'PageHeader'.
So what's so great about that?
Well, a couple of things:-
- I can now override which control a page should load without having to go back, edit the template and redeploy
- By using a feature scoped at 'Web', I can effectively use a different page header for different areas of my site without requiring different templates or code. (Note I've not tested this extensively, but since the <Control> element can be used at scope Web, Site, WebApplication or Farm, this should be perfectly feasible.)
- I can use any standard .Net user control or server control, so for anybody familiar with Jan Tielen's SmartPart, this provides similar capability.
In terms of functionality, there's a couple of other things I want to highlight:
- Parameters can be passed to the control via the declaration on the page. To read these, the control's implementation should walk up the control tree to get the values. An example would be:
<SharePoint:DelegateControl runat="server"
ControlId="PageHeader" MyParam="MyValue">
</SharePoint:DelegateControl>
- By adding AllowMutiple="true" to the declaration, you can make the Delegate Control load more than one user/server control.
- As mentioned earlier, many of the controls used on default.master are loaded using the Delegate Control. These include global links such as My Site/My Links and the publishing console. So using this approach, customizing the publishing console is a simple matter of providing a replacement .ascx and creating a feature as described here!
Hopefully this has given some insight as to why you should consider using it on your templates. As a final thought, how about combining with a web part so that you can simply re-use existing ASP.Net user/server controls as web parts?