Wednesday 20 June 2007

Using the Delegate Control

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?

16 comments:

Simon said...

Chris

This does not work:

<SharePoint:DelegateControl runat="server" ControlId="PageHeader" MyParam="MyValue">

Sharepoint will give you an error:

An error occurred during the processing of /_catalogs/masterpage/GeneralContent.aspx. Type 'Microsoft. SharePoint.WebControls. DelegateControl' does not have a public property named 'MyParam'.

Am I missing something? I only way I have found to pass parameters to a user control is to use the DelegateControl "ID" property. I really hope this is NOT the only way!!

Chris O'Brien said...

Hi Simon,

Hmm, when I've used delegate controls I don't think I've needed to pass parameters yet. But I'm surprised at what you report - the MSDN documentation for the delegate control certainly mentions being able to do this.

Sorry I don't have time to set something up to check this out. I wonder if maybe it's only possible to pass parameters to server controls, not user controls?

Anyone else done this?

Thanks,

Chris.

Chris Donlan said...

Chris

Thanks for the post.

I have a custom web user control that I'm loading with a delegate contral that has a code behind. Do you know how I can attach a debugger to that code? Thanks.

Chris O'Brien said...

Hi Chris,

OK I've not done this but would think there are 2 possibilities here:

- You can do this by adding the .pdb file to the GAC, and then setting a breakpoint and using the debugger as normal. See my post on How to debug SharePoint feature receivers for the full rundown - it shouldbe exactly the same.
- It's not possible to debug code for delegate controls in this way because reflection is used.

Be interested to hear the results!

Cheers,

Chris.

Anonymous said...

Hi Chris, great article! I have a question: is it possible to check next delegate control with the same ID in a chain and insert it after the control with a max. sequence?

Chris O'Brien said...

Hmm, not sure I follow exactly but I think the answer is no unfortunately. As far as I'm aware there is no way to get access to other delegate controls with the same ID - the control will simply load the one with the lowest ID which matches.

Might be worth a quick look with Reflector to check though :-)

HTH,

Chris.

& took the road less travelled said...

Chris

this post is cool. do you know what control are the top left navigation controls and i can add mu own user control using the delegate control

Chris O'Brien said...

The navigation controls aren't delegate controls, they are added in the markup of the master page your site is using. Delegate controls are actually used for the DataSource of the navigation controls, but that's a more subtle thing!

In answer to the second question, you definitely can use your own user control with a delegate control. Hopefully the info above (particularly on using the 'ControlSrc' property) will get you started.

HTH,

Chris.

Anonymous said...

Hi Chris, by your own admission you haven't needed to pass parameters to a DelegateControl yet. This implies that you've never tried it. Why then do you include information in your post on how to pass parameters to a DelegateControl?? A quick test would have shown you this crashes the page, as pointed out by Simon above.

Chris O'Brien said...

Hi Darren,

Thanks for your comment. It's certainly true that I haven't tried parameter passing with DelegateControl, and I didn't see it as an 'admission' when I said as much in my earlier note. I included the information in my post because the fact that I haven't tried it doesn't mean it's not useful information for other people starting to look at the control. Clearly it's unfortunate that it doesn't work as advertised. Perhaps I should have added "I haven't tried this" to that bullet point, but I wasn't seeking to indicate that I had tried it.

I'm sorry you feel differently.

Chris.

Anonymous said...

MOSSuMS
It's not ideal, but to set control properties put this is you feature's elements file:

<Control
Id="pagesCntrlId"
controlSrc="~/_controltemplates/feature/cntrl.ascx"
Sequence="90">
<Property Name="prop">propVal
</Property>
</Control>

I hope this helps others use these controls to good effect!

Cheers, Mike MOSSuMS.

Ruchi said...

Nice post, it gives brief about delegate control + how to use properties. To know how to use properties please go through -http://msdn.microsoft.com/en-us/library/ms470880.aspx

Anonymous said...

Thanks for the article. It gave me a general understand of what a delegate control can do and why it is useful...

Saurabh said...

Chris.

I have tried jQuery in Sharepoint
i refered
http://weblogs.asp.net/jan/archive/2008/11/20/sharepoint-2007-and-jquery-1.aspx

http://weblogs.asp.net/jan/archive/2008/11/20/sharepoint-2007-and-jquery-2.aspx

i used the Delegate control Method.
i create a user control in controlTemplates folder, create feature, install feature and activate feature and put delegate control at masterpage

>SharePoint:DelegateControl runat="server" ControlId="AdditionalPageHead" AllowMultipleControls="true" />

(i checked my elements.xml i used the same controlId i.e. AdditionalPageHead)

then i create an aspx page and put one anchor tag and on click event want to show alert("Hello World");

my jQuery code is in aspx file.

<script type="text/javascript">
$((document).ready(function()
{
$('#anc').click(function()
{
alert("Hello World");
});
});
</script>

<a id="anc" href="#">Request</a>

it doesn't work. nothing is happened when m clicking on the anchor.

any ideas?
Kind Regards.
Saurabh

Chris O'Brien said...

@Saurabh,

If your control is present when the page is loaded then the Delegate Control part of your solution is working fine. I'm you're not 100% sure, suggest putting in some temporary HTML which will allow you to verify this.

Once you've proved your control is there, the problem isn't to do with the Delegate Control, so you should look at the other bits. Afraid I'm no jQuery expert so can't really help there.

HTH,

Chris.

JB said...

Hi Chris,
How do I find out the controlids ? I tried to use reflection but it did not help ? Also, on the editprofile.aspx page we added some extra fields and we need a delegate control to override that for some validation.

I am using Sharepoint 2007. Any help will be really appreciated.
Best Regards
Jisnu