Programmatically Creating SharePoint 2010 Content Types that have Event Receivers (Wrox)

Summary: Learn how to create Microsoft SharePoint 2010 content types that have event receivers by using Microsoft Visual Studio 2010.

Wrox logo

Wrox SharePoint Books

Applies to: Business Connectivity Services | Open XML | SharePoint | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio

Author: Bryan Phillips

Editors: WROX Tech Editors for SharePoint 2010 Articles

Contents

  • Introduction to Creating Content Types that have Event Receivers

  • Creating Content Types

  • Adding Event Receivers to Content Types

  • Adding Content Types to Lists and Libraries

  • Conclusion

  • About the Author

  • Additional Resources

Introduction to Creating Content Types that have Event Receivers

At its most basic level, a content type is a set of related columns that you can add to a list or library. You usually create content types around a type of data or document that typically has the same columns, such as a project task or an expense report. For example, the Task content type has columns to store the priority, status, completion percentage, assignee, and due date of the task. Content types also let you do the following:

  • Configure a custom template to use when the content type is associated with a document library.

  • Add one or more information management policies to enable document retention and auditing.

  • Add one or more workflows that execute when an item or document that has that content type assigned is added or modified.

When you add a content type to a list or library, the columns in the content type are also added to the list or library, and you can add information to the columns when you create a new item or document of that content type. A content type can run code when you create, modify, or delete an item or document that is associated with that content type. This code is contained in a class that you create, called an event receiver. You can create event receivers for lists, libraries, content types, sites, site collections, and even emails. For example, you can use content types to apply retention policies to documents that are stored in SharePoint. Those retention policies, for example, might specify the deletion of documents when they expire and are no longer needed. Content types also enable you to classify documents regardless of the file name extensions associated with the documents, which simplifies the code that you write to interact with those documents.

Creating Content Types

To create a content type programmatically, you must first create a SharePoint 2010 project by using Visual Studio 2010. To create a new SharePoint project, select File, New, Project from the menu bar in Visual Studio 2010. In the New Project dialog box, shown in Figure 1, make sure that .NET Framework 3.5 is selected from the drop-down list at the top of the dialog box. Now select SharePoint, 2010 from the pane on the left side, and then select Content Type from the list on the right side. Type ContentTypes in the Name text box, and then click OK.

Figure 1. New Project dialog box

New Project dialog box

In the SharePoint Customization Wizard dialog box, shown in Figure 2, specify the URL to your SharePoint site, select the Deploy as a farm solution radio button, and then click Next.

Figure 2. SharePoint Customization Wizard dialog box

SharePoint Customization Wizard dialog box

Visual Studio connects to your SharePoint site and obtains a list of the content types. In the SharePoint Customization Wizard dialog box select Document from the drop-down list and then click Finish, as shown in Figure 3. Selecting Document adds the content type to a document library instead of a list, and configures the content type to inherit columns and other settings from the Document content type. Any changes that you make to the Document content type apply to this content type too.

Warning

Solutions that contain event receivers must be deployed as farm solutions in order to execute outside of an IIS worker process. Deploying a farm solution places .NET assemblies in the global assembly cache. Assemblies in the global assembly cache run as fully trusted code and can execute code in SharePoint that otherwise is not allowed. You must make sure that any code deployed in a farm solution is safe to run and free from defects because it can potentially harm your SharePoint farm.

Figure 3. SharePoint Customization Wizard dialog box

SharePoint Customization Wizard dialog box

When Visual Studio creates the project, a content type named ContentType1 is added to the project and its Elements.xml definition file opens in the text editor. Replace the contents of the Elements.xml file with the following code.

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="https://schemas.microsoft.com/sharepoint/">
  <!-- Parent ContentType: Document (0x0101) -->
  <ContentType ID="0x01010024b0c43f2179474e84cd1a8a0ec44c54"
               Name="Project Document"
               Group="Custom Content Types"
               Description="My Content Type"
               Inherits="TRUE"
               Version="0">
    <FieldRefs>
    </FieldRefs>
  </ContentType>
</Elements>

In the preceding code snippet, the ContentType element defines the new Project Document content type. As the XML comment indicates, the content type inherits from the Document content type. That means that any columns on the Document content type are also available in the Project Document content type. Inheriting from content types enables you to create hierarchies that you can use to classify your data and documents. It also promotes reuse and standardization when you inherit your content types from a single content type with predefined, widely used columns. For example, you can create a Project Document content type that has columns for the project name and the project manager, and create additional content types that inherit from it to ensure that those new content types automatically have columns for the project name and the project manager. By default, when you update any of those content types, all lists, libraries, items and documents receive the update too.

To create a content type, follow these steps:

  1. Determine the ID of the content type from which you want to inherit.

  2. Append 00 and a GUID without dashes or curly braces to the end of the ID from step 1.

First, then, you must determine the ID of the content type from which you want to inherit. (For a list of IDs that correspond to well-known content types, see the List of Built-In Content Type IDs on MSDN.) In the preceding code snippet, 0x0101 is the ID of the Document content type.

After you determine the content type ID, you must concatenate the parent content type ID, 00, and a GUID without using dashes or curly braces. That concatenated string becomes the value that you must use for the ID attribute of the ContentType element.

In the preceding code snippet, 00 separates 0x0101 and the GUID 24b0c43f2179474e84cd1a8a0ec44c54. To learn more about how to create content type IDs, see Defining Content Type IDs on MSDN.

The FieldRefs element in the previous code snippet contains FieldRef elements representing the columns that you want to add to your content type. Any columns that you add must be site columns. Site columns are reusable columns that you can add to your content types, lists, and libraries.

Note

For a list of common site columns, see the List of Well-Known Site Columns on MSDN.

The following code snippet shows how to reference a site column in your content type declaration.

<FieldRefs>
  <FieldRef ID="{816FC252-DE99-4523-A5F4-3D40DA7454E2}"
         Name="ProjectName" DisplayName="Project Name" Required="TRUE"/>
</FieldRefs>

In the preceding code snippet, a FieldRef element is added to the content type and the ID of the ProjectName site column is specified for the ID attribute together with its name. The DisplayName attribute displays a friendly, easy-to-read name in the SharePoint user interface. Site columns have a default display name, but you can specify a different display name in your FieldRef element. The Required attribute indicates whether the column requires a value when the user saves an item or document of this content type. Although it is not in the code snippet, you can use the Hidden attribute to hide the column from the SharePoint user interface. That is useful for storing information that you need in your code or workflows and that the user should not change, such as status information or flags.

If a suitable site column is not available for your content type, you can create one in the same project as your content type. To create a new site column, right-click your project, and then select Add, New Item from the context menu. In the Add New Item dialog box, shown in Figure 4, select SharePoint, 2010 in the pane on the left side, and then select Empty Element from the list on the right side. Type SiteColumns in the text box, and then click Add.

Figure 4. Add New Item dialog box

Add New Item dialog box

Visual Studio adds the SiteColumns element file to the project and open the appropriate Elements.xml file. Modify the Elements.xml file as shown in the following code snippet.

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="https://schemas.microsoft.com/sharepoint/">
  <Field ID="{816FC252-DE99-4523-A5F4-3D40DA7454E2}"
       DisplayName="Project Name" StaticName="ProjectName" Name="ProjectName"
       Required="TRUE" Type="Text" 
      SourceID="https://schemas.microsoft.com/sharepoint/v3" />
</Elements>

In the preceding code snippet, the Field element defines the ProjectName site column. The ID attribute is a new GUID that represents the ID of the site column. The value of the ID attribute must match the ID of the FieldRef element in the content type to which you want to add this site column. The DisplayName attribute displays a friendly name in the SharePoint user interface. The StaticName and Name attributes are important because they contain the string values that you must use when you reference this column in your code. To save yourself a big headache, use only letters and numbers in the values for the Name and StaticName attribute values. Do not use spaces, punctuation, symbols, or any other special characters because they are hexadecimal-encoded. Although the user sees 'Project Name' in your SharePoint site, you must use 'Project_x0020_Name' in your code because the space in the column name is replaced with its hexadecimal-encoded equivalent.

The Required attribute indicates whether the site column is required by default when it is added to a content type, list, or library. You can also use the Hidden attribute to hide the site column from the SharePoint user interface. Finally, the Type attribute is used to indicate what data type is used for the site column. The value Text means that the site column stores a string and displays a single-line text box when it is displayed in a browser. To get a list of other values for the Type attribute, see the List of Built-In Field Types on MSDN.

Adding Event Receivers to Content Types

An event receiver is a special class that runs code to respond to events in SharePoint. In the context of content types, an event receiver responds to the ItemAdded, ItemUpdated, ItemsDeleted, ItemCheckedIn, ItemCheckedOut, ItemConverted, ItemMoved, ItemAttachmentAdded, and ItemAttachmentDeleted events of the list item or document.

To create an event receiver, follow these steps:

  1. Right-click your project, and then select Add, New Item from the context menu.

  2. In the Add New Item dialog box, as shown in Figure 5, select SharePoint, 2010 from the tree on the left side and select Event Receiver from the list on the right side.

  3. Type ProjectDocumentEventReceiver in the text box, and then click Add.

Figure 5. Add New Item dialog box

Add New Item dialog box

When the SharePoint Customization Wizard dialog box appears, select List Item Events from the first drop-down list and Document Library from the second drop-down list, as shown in Figure 6. Select the An item is being added and the An item is being updated check boxes in the list so that the event receiver runs when an item is added or updated, and then click Finish.

Figure 6. SharePoint Customization Wizard dialog box

SharePoint Customization Wizard dialog box

You can handle events synchronously or asynchronously. When you handle an event synchronously, your code runs before the change is final, which gives you an opportunity to cancel it or to make modifications to the item or document before you commit to the change in SharePoint. Any code that runs synchronously blocks the thread of the web page that is making the change. Make sure that synchronous code is as 'lightweight' as possible to prevent a performance bottleneck. Lightweight code runs quickly and uses little memory.

When you handle an event asynchronously, your code runs after the change is final and committed to SharePoint. Code that runs asynchronously does not block any web page threads, so you can run heavier, longer-running operations.

Note

Actions, such as sending an email or updating another system, should be added to one of the asynchronous event methods of your event receiver so that the action is taken after the change is committed to SharePoint. Otherwise, other event receivers could cancel the change event.

After the event receiver is added to the project, Visual Studio opens the ProjectDocumentEventReceiver class in the text editor with the following override methods.

  public override void ItemAdding(SPItemEventProperties properties) {
      base.ItemAdding(properties);
  }
  public override void ItemUpdating(SPItemEventProperties properties) {
      base.ItemUpdating(properties);
  }

In the preceding code snippet, the new ProjectDocumentEventReceiver class inherits from the SPItemEventReceiver class. All event receivers responding to list item events must inherit from the SPItemEventReceiver class. The SPItemEventReceiver class has a method for each type of list item event. Because you selected the first two check boxes in the list of the SharePoint Customization Wizard dialog box, Visual Studio added overrides for the ItemAdding method and the ItemUpdating method of the SPItemEventReceiver class. If you want to write code for additional list item events, simply add an override method for each event that you want to handle. To override additional methods, place the cursor inside the class and type override and a space to display a list of methods. Methods that you have already overridden do not appear in the list. For a list of the methods and their associated list item events, see SPItemEventReceiver Members.

When you add code to your event receiver methods, you must set the EventFiringEnabled property of your event receiver to false in the first line of the method, and set it back to true in the last line of the method. That prevents the event receiver from firing if you change the item in your code. If you do not set this property to false, any change that you make to the item could cause an endless loop and potentially crash your SharePoint web application. If you want to cancel the event that your code is handling, set the Cancel property to true on the properties object that is passed to the method. If you do cancel the event, you should also specify an informative message for the ErrorMessage property, which is displayed to the user. The SPItemEventProperties class has other properties that you can use to get references to the list item being changed, the list where the change is occurring, and the site where the list is located. The following using statement and methods added to the ProjectDocumentEventReceiver class show how to perform simple validation of the ProjectName column and cancel the event if the value of the column is formatted incorrectly.

using System.Text.RegularExpressions;

public override void ItemAdding(SPItemEventProperties properties) {
    this.EventFiringEnabled = false;
    ValidateProjectName(properties);
    this.EventFiringEnabled = true;
}
public override void ItemUpdating(SPItemEventProperties properties) {
    this.EventFiringEnabled = false;
    ValidateProjectName(properties);
    this.EventFiringEnabled = true;
}

private void ValidateProjectName(SPItemEventProperties properties) {
    string projectName = 
        properties.AfterProperties["ProjectName"] as string;

    if (projectName != null) {
        if (!Regex.IsMatch(projectName, @"[A-Z][A-Z][A-Z]-\d\d\d\d")) {
            properties.Cancel = true;
            properties.ErrorMessage = 
                "Project Name must be in the format ABC-1234.";
        }
    }
}

In the preceding code snippet, the first and last lines of the ItemAdding and ItemUpdating events disable and enable event firing. While event firing is disabled, both methods call the ValidateProjectName method to validate the format of the value in the ProjectName column. The AfterProperties property of the SPItemEventProperties class is used to get the new value for the ProjectName column. If the column has a value, the System.Text.RegularExpression.Regex class verifies that the value is formatted with letters for the first three characters, a dash for the fourth character, and numbers for the last four characters. If the value does not match that pattern, the event is canceled and the ErrorMessage property of the SPItemEventProperties class returns a message to the user.

Note

You should not use the SPContext class inside an event receiver, because its Current property returns null when the event receiver is running outside of Internet Information Services in a SharePoint job or workflow. When you need a reference to the list item, list, site, or to the user making the change, use the SPItemEventProperties class instead.

After you add your code to your event receiver, you must associate it with your content type. In the Solution Explorer tool window, double-click the Elements.xml file that is next to the ProjectDocumentEventReceiver.cs file to open it. The Elements.xml file contains instructions to SharePoint to run your SharePoint event receiver when a document is added or updated in a document library. However, you must modify the XML instructions so that the event receiver runs for your content type instead. Replace the beginning Receivers element with the following element.

<Receivers xmlns:spe="https://schemas.microsoft.com/sharepoint/events">

After that, open the Elements.xml file for the content type and add the following code immediately after the closing tag for the FieldRefs element.

<XmlDocuments>
  <XmlDocument 
    NamespaceURI="https://schemas.microsoft.com/sharepoint/events">
  </XmlDocument>
</XmlDocuments>

Finally, move the Receivers element from the Elements.xml file of the event receiver to the Elements.xml file of the content type, placing the Receivers element inside the XmlDocument element from the preceding code snippet. After that, the ContentType element in the Elements.xml file of content type should resemble the one in the following code snippet. Make sure to remove the empty Elements.xml from the event receiver because it is no longer used.

<ContentType ID="0x01010024b0c43f2179474e84cd1a8a0ec44c54"
  Name="Project Document"
  Group="Custom Content Types"
  Description="My Content Type"
  Inherits="TRUE"
  Version="0">
  <FieldRefs>
    <FieldRef ID="{816FC252-DE99-4523-A5F4-3D40DA7454E2}" 
      Name="ProjectName" DisplayName="Project Name" Required="TRUE"/>
  </FieldRefs>
  <XmlDocuments>
    <XmlDocument 
        NamespaceURI="https://schemas.microsoft.com/sharepoint/events">
      <Receivers 
        xmlns:spe="https://schemas.microsoft.com/sharepoint/events">
        <Receiver>
          <Name>ProjectDocumentEventReceiverItemAdding</Name>
          <Type>ItemAdding</Type>
          <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
         <Class>      ContentTypes.ProjectDocumentEventReceiver.ProjectDocumentEventReceiver
         </Class>
         <SequenceNumber>10000</SequenceNumber>
        </Receiver>
        <Receiver>
          <Name>ProjectDocumentEventReceiverItemUpdating</Name>
          <Type>ItemUpdating</Type>
          <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
         <Class>
ContentTypes.ProjectDocumentEventReceiver.ProjectDocumentEventReceiver
         </Class>
          <SequenceNumber>10000</SequenceNumber>
        </Receiver>
      </Receivers>
    </XmlDocument>
  </XmlDocuments>
</ContentType>

Adding Content Types to Lists and Libraries

To associate your content type with a list or library, add a ContentTypeBinding element to your project. If you added a list instance to create the list or library that is associated with your content type, add the ContentTypeBinding element to the Elements.xml file of that list instance. Alternately, add an empty Element item to your project by following the steps that you used previously for the custom site columns and add the ContentTypeBinding element to that file.

The ContentTypeBinding element has two attributes, ContentTypeId and ListUrl. The ContentTypeId attribute specifies the ID of the content type that is being bound to the list. The ListUrl attribute specifies the URL for the list. The following code snippet shows an example of the ContentTypeBinding element.

<ContentTypeBinding 
    ContentTypeId="0x01010024b0c43f2179474e84cd1a8a0ec44c54"
    ListUrl="ProjectDocLib" />

In the preceding code snippet, the ContentTypeId attribute is set to the value of the ID of the content type that you specified in the Creating Content Types section. The ListUrl attribute is set to the URL of the ProjectDocLib document library. In the next paragraphs, you add a list instance to your project, but before you do, save your ContentTypeBinding element and add it to the Elements.xml file of the list instance.

To add a document library to your project, right-click your project, and then select Add, New Item from the context menu. In the Add New Item dialog box, shown in Figure 7, select SharePoint, 2010 from the tree on the left side and List Instance from the list on the right side. Type ProjectDocLib in the text box, and then click Add.

Figure 7. Add New Item dialog box

Add New Item dialog box

When the SharePoint Customization Wizard dialog box appears, type ProjectDocLib in the first and last text boxes. Select Document Library from the drop-down list, and then click Finish. Visual Studio adds the list instance to the project and opens the new Elements.xml file, which displays the following code.

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="https://schemas.microsoft.com/sharepoint/">
  <ListInstance Title="ProjectDocLib"
                OnQuickLaunch="TRUE"
                TemplateType="101"
                FeatureId="00bfea71-e717-4e80-aa17-d0c71b360101"
                Url="ProjectDocLib"
                Description="">
  </ListInstance>
</Elements>

Figure 8. SharePoint Customization Wizard dialog box

SharePoint Customization Wizard dialog box

In the preceding code snippet, the ListInstance element creates the new document library because its TemplateType attribute is set to 101, the value for document libraries. The FeatureId attribute is set to the ID of the feature that contains the schema for document libraries. For a list of TemplateType and FeatureId attribute values, see the List of Well-Known List Template IDs and List of Built-In Feature IDs. The Title, OnQuickLaunch, Url, and Description attributes are set to the values that you set in the SharePoint Customization Wizard dialog box. With the list instance added to your project, you can add your ContentTypeBinding element immediately before the end tag of the Elements element.

Finally, before you can test your code, in the Solution Explorer tool window, double-click the Features1 node under the Features node to open the feature that deploys your content type. At a minimum, you must ensure that all items in the leftmost list are added to the rightmost list if they are not there already. Type Project Documents for the Title text box and Adds the Project Documents content type to the site. in the Description text box. Always specify values for the Title and Description text boxes to make it easy for you and your users to identify the feature on the Site Collection Features page in your SharePoint site. To allow site owners to activate your feature instead of only site collection administrators, leave the Scope drop-down list set to Web. After you make your changes, save your project and select Debug > Start Debugging from the menu bar to deploy and run your project.

Conclusion

The content type is a powerful SharePoint feature that you can use to apply structure, default file templates, information management policies, and workflows to your data and documents. When you use them with event receivers, you can perform complex validation on changes to your items and documents, and cancel those changes when you want to. You can also respond to changes after they are committed, or when you want to send emails, update other lists and libraries, and notify external systems of those changes. By associating your event receivers with content types instead of associating them directly with lists and libraries, you can ensure that your event receivers run correctly, regardless of where the actual items and documents are stored.

About the Author

Bryan Phillips is a senior partner at Composable Systems, LLC, and a Microsoft Most Valuable Professional in SharePoint Server. He is a co-author of Professional Microsoft Office SharePoint Designer 2007 and Beginning SharePoint Designer 2010 and maintains a SharePoint-related blog. Bryan has worked with Microsoft technologies since 1997 and holds the Microsoft Certified Trainer (MCT), Microsoft Certified Solution Developer (MCSD), Microsoft Certified Database Administrator (MCDBA), and Microsoft Certified Systems Engineer (MCSE) certifications.

The following were tech editors on Microsoft SharePoint 2010 articles from Wrox:

  • Matt Ranlett is a SQL Server MVP who has been a fixture of the Atlanta .NET developer community for many years. A founding member of the Atlanta Dot Net Regular Guys, Matt has formed and leads several area user groups. Despite spending dozens of hours after work on local and national community activities, such as the SharePoint 1, 2, 3! series, organizing three Atlanta Code Camps, working on the INETA board of directors as the vice president of technology, and appearing in several podcasts such as .Net Rocks and the ASP.NET Podcast, Matt recently found the time to get married to a wonderful woman named Kim, whom he helps to raise three monstrous dogs. Matt currently works as a senior consultant with Intellinet and is part of the team committed to helping people succeed by delivering innovative solutions that create business value.

  • Jake Dan Attis. When it comes to patterns, practices, and governance with respect to SharePoint development, look no further than Jake Dan Attis. A transplant to the Atlanta area from Moncton, Canada, Dan has a degree in Applied Mathematics, but is 100% hardcore SharePoint developer. You can usually find Dan attending, speaking at, and organizing community events in the Atlanta area, including code camps, SharePoint Saturday, and the Atlanta SharePoint User Group. When he's not working in Visual Studio, Dan enjoys spending time with his daughter Lily, watching hockey and football, and sampling beers of the world.

  • Kevin Dostalek has over 15 years of experience in the IT industry and over 10 years managing large IT projects and IT personnel. He has led projects for companies of all sizes and has participated in various roles including Developer, Architect, Business Analyst, Technical Lead, Development Manager, Project Manager, Program Manager, and Mentor/Coach. In addition to these roles, Kevin also managed a Solution Delivery department as a Vice President for a mid-sized MS Gold Partner from 2005 through 2008 and later also served as a Vice President of Innovation and Education. In early 2010 Kevin formed Kick Studios as a company providing consulting, development, and training services in the specialized areas of SharePoint and Social Computing. Since then he has also appeared as a speaker at numerous user group, summit, and conference type events across the country. You can find out more about Kevin on his blog, The Kickboard.

  • Larry Riemann has over 17 years of experience architecting and creating business applications for some of the world’s largest companies. Larry is an independent consultant who owns Indigo Integrations and does SharePoint consulting exclusively through SharePoint911. He is an author, writes articles for publication and occasionally speaks at conferences. For the last several years he has been focused on SharePoint, creating and extending functionality where SharePoint leaves off. In addition to working with SharePoint, Larry is an accomplished .Net Architect and has extensive expertise in systems integration, enterprise architecture and high availability solutions. You can find him on his blog.

  • Sundararajan Narasiman is a Technical Architect with Content Management & Portals Group of Cognizant Technology Solutions, Chennai, with more than 10 years of Industry Experience. Sundararajan is primarily into the Architecture & Technology Consulting on SharePoint 2010 Server stack and Mainstream .NET 3.5 developments. He has passion for programming and also has interest for Extreme Programming & TDD.

Additional Resources

For more information, see the following resources: