Karine Bosch’s Blog

On SharePoint

Walkthrough 1 – Developing an Event Receiver for a Content Type


In SharePoint 2010, you can create event receivers for different types of SharePoint artifacts: lists, document libraries, and even sites. When developing for SharePoint 2010 you usually use the Visual Studio 2010 templates for SharePoint 2010. Creating an event receiver for a list or document library using this tooling, is very straightforward, but developing an event receiver for a specific content type is not. This walkthroug explains the different steps to develop an event receiver for a specific content type.

This code sample defines a content type for list items of type Planet. The event receiver will check if the name of the planet is unique. You could argue that testing on the uniqueness of a name is not necessary anymore as this behaviour can be enforced in SharePoint 2010, but I’ll explain further on, when defining the fields of the content type, why I still need this event receiver.

Step 1 – Create the Visual Studio Project

The first step is to create a SharePoint project in Visual Studio 2010. Create a new project based on the Empty SharePoint Project template. Make sure that the .NET 3.5 Framework is selected.

Step 2 – Create the Site Columns

The next step is to define the site columns that are necessary for the content type. Add a New Item to the project and select the Empty Element template from the SharePoint 2010 templates. This will create an empty elements.xml.

Define the necessary site columns using the <Field> element. You can specify information like internal name, display name, type of the column and description. You can use the Group attribute to specify the group of site columns to which the new columns will belong. The definition of one of the fields looks like the following:

<Field
    ID="{C2A8588A-F8A2-4324-BE39-4962E2E75895}"
    Name="NumberOfMoons"
    DisplayName="Number of Moons"
    Description="The number of moons that turn around the planet."
    Type="Number"
    Group="Universe Site Columns"
    />

You can find the definition of the other fields in my SharePoint 2007 walkthrough.

Step 3 – Create the Content Type

To create a content type, you can Add a New Item to the project and select the Content Type template. Give your content type a user friendly name. When you click OK, the SharePoint Customization Wizard opens to let you choose a parent content type. For my code sample the content type is based on the Item content type, but the wizard offers you the possibility to create content types based on a Document or even on one of your own custom content types.

Create content type with VS 2010 tools for SharePoint 2010

When clicking the OK button, an elements.xml file is created with the following CAML skeleton:

<ContentType ID="0x01003c57ecdc4b724b2aaf0ec33b65fc9902"
               Name="Planet Content Type"
               Group="Universe Content Types"
               Description="Planet Content Type" Version="0" Inherits="True">
    <FieldRefs>
    </FieldRefs>
  </ContentType>
</Elements>

Joehoe, you don’t have to calculate the correct Content Type ID anymore, based on its inheritance tree! Visual Studio calculates it for you!

Remove the Version and the Inherits attribute as they cause your content type to deploy incorrectly. Modify data like Group and Description appropriately. Add all the fields that make up the content within the <FieldRefs> element. You have to specify at least the ID and the Name of the site column. If you omit the Name attribute, you will not get an error message during deployment but the field will not be added to the content type.

    <FieldRefs>
        <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}"
                  Name="Title" DisplayName="Name" Required="TRUE" />
        <FieldRef ID="{C2A8588A-F8A2-4324-BE39-4962E2E75895}" Name="NumberOfMoons" /> 
        <FieldRef ID="{85F781F8-0AB3-433e-B828-DC0DD39AE695}" Name="Distance" />
        <FieldRef ID="{0E273391-2749-4c17-BCFD-009E2DC98F2F}" Name="OrbitalPeriod" />
        <FieldRef ID="{7A86784F-65B3-4b48-8EAF-C77FFED7BB61}" Name="MinimumTemperature" />
        <FieldRef ID="{9677F5F2-4CA6-4d88-9DE1-3518DE4E2A01}" Name="MaximumTemperature" />
    </FieldRefs>

As you can see, the name of the planet is based on the default Title field, it is not a custom field. And that’s the reason why I still need that event receiver to validate the uniqueness of the name: you can only enforce this behaviour when defining a site column:

Enforce unique value in site column

and not when adding the field to the content type:

You cannot enforce uniqueness from within content type definition

Step 4 – Create the Event Receiver

Next step is to create the event receiver. If you try to use the Visual Studio 2010 templates for SharePoint 2010, you will see that you can create different types of event receivers but NOT for a content type. Although I really really appreciate the work of the Visual Studio team, I hope theywill address this issue in a next release.

Creating an event receiver

Cancel the wizard and manually add a class to the Visual Studio project. Let the class inherit from SPItemEventReceiver.

    public class PlanetReceiver: SPItemEventReceiver
    {
    }

In SharePoint 2010 development you only have to override the event handlers you really need. As I want to test on the uniqueness of the planet name, I have to do this in the ItemAdding event, in order to be able to cancel the creation.

    public class PlanetReceiver: SPItemEventReceiver
    {
        public override void  ItemAdding(SPItemEventProperties properties)
        {   
            // get the name of the planet that needs to be saved   
            string planet = properties.AfterProperties["Title"].ToString();   
            // get the Planet list (will be queried for existing planet name)      
                SPList list = properties.Web.Lists[properties.ListId];       
                // build the query      
                SPQuery query = new SPQuery();      
                query.Query = string.Format("<Where><Eq><FieldRef Name=\"Title\" /><Value Type=\"Text\">{0}</Value></Eq></Where>", planet);                  
                // execute the query      
                SPListItemCollection items = list.GetItems(query);      
                if (items != null && items.Count > 0)      
                {          
                    properties.ErrorMessage = string.Format(           
                        "The planet with the name {0} already exists.", planet);          
                    properties.Status = SPEventReceiverStatus.CancelWithError;          
                    properties.Cancel = true;      
                }   
        }   
     }

To be able to link the event receiver with the content type I have to add a Guid to the class.

    [Guid("EB7F7632-DA7A-4f4e-83CC-D1EFFC4A627E")]
    public class PlanetReceiver: SPItemEventReceiver
    {
        public override void  ItemAdding(SPItemEventProperties properties)
        {   
            // code omitted   
        }   
     }

Therefore I first have to add an additional using statement:

     using System.Runtime.InteropServices;

Step 5 – Modify the Content Type to add the Event Receiver

Once the event receiver is developed, you can add it to your content type using the <XmlDocuments> element, that you have to add outside the <FieldRefs> element, but inside the <ContentType> element.

    <XmlDocuments>
      <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/events">
        <Receivers xmlns:spe="http://schemas.microsoft.com/sharepoint/events">
          <Receiver>
            <Name>ItemAdding</Name>
            <Type>ItemAdding</Type>
            <SequenceNumber>10000</SequenceNumber>
            <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
            <Class>$SharePoint.Type.eb7f7632-da7a-4f4e-83cc-d1effc4a627e.FullName$</Class>
            <Data></Data>
            <Filter></Filter>
          </Receiver>
        </Receivers>
      </XmlDocument>
    </XmlDocuments>

You can use the <XmlDocuments> element to specify custom form templates, but using the <Receivers> element, you can link specific event receivers to the content type.

It is advised to use the Visual Studio tokens starting with $SharePoint to specify the assembly fullname and the class fullname. These values will be resolved during packaging of the solution. Note that the guid within the <Class> element is the guid mentioned above the class, and that its letters are in lowercase.

Step 6 – Deploy and Test

Your solution is ready to deploy. When the feature is activated, you can create a list and add your content type to it. After removing the Item content type from the list, you will be able to fill out all fields of a planet.

When you try to add a planet with a name that already exists in the list, you will get an error message:

Planet event receiver at work

You can download the code sample here.

16 Comments »

  1. […] Walkthrough 1 – Developing an Event Receiver for a Content Type […]

    Pingback by SharePoint 2010: Recopilatorio de enlaces interesantes (XXV)! - Blog del CIIN | November 1, 2011 | Reply

  2. […] Walkthrough 1 – Developing an Event Receiver for a Content Type […]

    Pingback by SharePoint 2010: Recopilatorio de enlaces interesantes (XXV)! « Pasión por la tecnología… | November 1, 2011 | Reply

  3. Tnx Karine,

    that was helpfull🙂

    Comment by Maarten De Bont | November 23, 2011 | Reply

  4. Karine –

    Thank you for taking the time to create this walkthrough for SharePoint 2010 – I have just started my SP education, and don’t quite get the translation from 2007 to 2010 (although I have been assured that there really were no ‘good old days’😉. I’m trying to force your example here into an existing custom document set content type in an existing project and am having problems. When I attempt to create an instance of the library I get an error: “An unexpected error has occurred. Correlation ID: {5465347d-…} (the ID of the attempted library?) Date and Time: …”. If I comment out the XmlDocument element in the document set’s elements.xml file, (leaving my event receiver class orphaned), the error goes away.

    The Elements.xml file for the document set content type does not have Inherits or Version attributes. It does have a dozen or so FieldRefs; the OOTB Title is hidden. It also has several other existing XmlDocument elements for pages and fields. My XmlDocument element is on the bottom.

    I am using the $SharePoint tokens as you suggest; I would like to try to use the actual values but don’t know how to locate them – is the ‘SPEventReceiverDefinitionCollection Editor’ that you have a screen capture of a readily available tool?

    Again, thanks for your blog; it is one of most thorough and well written that I have come across.

    – Chris

    Comment by Chris Anderson | January 13, 2012 | Reply

  5. Hi Chris,
    Thanks for reading my blog. The screenshot is taken from the SharePoint 2010 Manager, which you can download from codeplex.
    Weird that you have a problem with the XmlDocument element. If you can’t find the solution, you can send me your code at karinebosch at hotmail dot com, and I’ll try to help you out.

    Comment by Karine Bosch | January 13, 2012 | Reply

  6. Hi,

    I tried your above mentioned method but when i checked event for content type using sp manager. There is no any event and also event was not fired with content type.

    Please guide me where is issues

    Comment by vinay | February 20, 2012 | Reply

  7. If you cannot see the event handler usins SP Manager it means no event is attached to the content type. Are you sure you followed all steps explained in the walkthrough?

    Comment by Karine Bosch | February 28, 2012 | Reply

  8. Great article. However, I had to add the following to my Featre.Template.xml file to make it work:

    Comment by Nauman Ahmed | May 16, 2012 | Reply

  9. <Feature xmlns=”http://schemas.microsoft.com/sharepoint/“>  <Properties>    <Property Key=”GloballyAvailable” Value=”true” />    <Property     Key=”AssemblyQualifiedName”      Value= “$SharePoint.Type.b379a065-5bec-4d9f-8f3c-d3f58b4dbd6d.AssemblyQualifiedName$”/>    <Property     Key=”AssemblyFullName”      Value=”$SharePoint.Project.AssemblyFullName$”/>  </Properties></Feature><Feature xmlns=”http://schemas.microsoft.com/sharepoint/“>  <Properties>    <Property Key=”GloballyAvailable” Value=”true” />    <Property     Key=”AssemblyQualifiedName”      Value= “$SharePoint.Type.b379a065-5bec-4d9f-8f3c-d3f58b4dbd6d.AssemblyQualifiedName$”/>    <Property     Key=”AssemblyFullName”      Value=”$SharePoint.Project.AssemblyFullName$”/>  </Properties></Feature>

    Comment by Nauman Ahmed | May 16, 2012 | Reply

  10. Mrs. Karine Bosch thanks for guide is resolve my problem but in step 6 my error not show as pop-up but show as full screen(still in same URL path)
    Are you have any suggestion why it happen

    *i using SharePoint 2010 foundation and sorry for my bad english

    Comment by rudybelmont | January 25, 2013 | Reply

  11. Is this working for 2013. I tried to follow the step but the event receiver has not been connected to the content type. Either I am doing something wrong or things have changed in SP 2013.

    Comment by Esben Rune Hansen | April 22, 2013 | Reply

    • I haven’t retested my walkthroughs on SP2013, but I’ll put it on my agenda for the coming week.

      Comment by Karine Bosch | April 23, 2013 | Reply

  12. THe ItemAdding event receiver will only fire if the content type is the default one in the list/library. Correct?

    Comment by mihelium | May 14, 2013 | Reply

    • That’s possible but I’m not sure. Anyway, it’s a good tip.

      Comment by Karine Bosch | May 14, 2013 | Reply

  13. This is a great article. I’ve tried several times to download the sample code but it’s not working. Could you email the zip file to me?

    Comment by Wes Parker | February 27, 2014 | Reply

  14. Hi Wes, I think something went wrong when switching to OneDrive. The problem should be solved now, can you please try again?
    Kind regards,
    Karine Bosch

    Comment by Karine Bosch | February 27, 2014 | Reply


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: