Friday, March 29, 2013

Developing an Event Receiver for a Content Type


If you want that a certain event receiver only fires on certain lists or document libraries, you can develop a content type and bind an event receiver to this content type.
In this walkthrough I will explain how you can write a simple event receiver for a content type. This walkthrough builds on the first one. The content type will be applied to the Planets list and will contain fields for:
  • the name of the planet
  • the number of moons
  • distance from the sun
  • orbital period (number of days the planet needs to turn around the sun)
  • minimum temperature
  • maximum temperature

Step 1 – Create the site columns

Content types are based on site columns, so you first have to define the necessary site columns. You can do this in CAML. Best practice is to create a separate XML file for each different step. Create a sitecolumns.xml file and add the following CAML. Each sitem column is defined in a <Field> element and has an own unique ID. 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:
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <!--Site Columns-->
  <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"
    />
  <Field
    ID="{85F781F8-0AB3-433e-B828-DC0DD39AE695}"
    Name="Distance"
    DisplayName="Distance (in km)"
    Description="The distance from the sun."
    Type="Number"
    Group="Universe Site Columns"
    />
  <Field
    ID="{0E273391-2749-4c17-BCFD-009E2DC98F2F}"
    Name="OrbitalPeriod"
    DisplayName="Orbital Period (in earth days)"
    Description="The time necessary to turn around the sun calculated in earth days."
    Type="Number"
    Group="Universe Site Columns"
    />
  <Field
    ID="{7A86784F-65B3-4b48-8EAF-C77FFED7BB61}"
    Name="MinimumTemperature"
    DisplayName="Minimum temperature (in °C)"
    Description="Minimum temperature in °C at the surface of the planet."
    Type="Number"
    Group="Universe Site Columns"
    />
  <Field
    ID="{9677F5F2-4CA6-4d88-9DE1-3518DE4E2A01}"
    Name="MaximumTemperature"
    DisplayName="Maximum temperature (in °C)"
    Description="Maximum temperature in °C at the surface of the planet."
    Type="Number"
    Group="Universe Site Columns"
    />
</Elements>

Step 2 – Create the content type

Content types can also be created using CAML. The site columns that make up the content type are specified in separate <FieldRef> elements within a <FieldRefs> element. The definition of this content type looks as follows:
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <!--Content Types-->
  <ContentType
    ID="0x0100A3897215697C447eB6612DF4CF4390FE"
    Group="Universe Content Types"
    Name="Planet">
    <FieldRefs>
      <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" DisplayName="Name" />
      <FieldRef ID="{C2A8588A-F8A2-4324-BE39-4962E2E75895}" />
      <FieldRef ID="{85F781F8-0AB3-433e-B828-DC0DD39AE695}" />
      <FieldRef ID="{0E273391-2749-4c17-BCFD-009E2DC98F2F}" />
      <FieldRef ID="{7A86784F-65B3-4b48-8EAF-C77FFED7BB61}" />
      <FieldRef ID="{9677F5F2-4CA6-4d88-9DE1-3518DE4E2A01}" />
    </FieldRefs>
  </ContentType>
</Elements>
Notice that the content type inherits from the Item content type, which has content type ID 0×01. A new content type ID of an inheriting content type is calculated as follows:
parent content type ID + 00 + GUID without minus signes and accolades.
The first <FieldRef> element is based on the Title column. This column has the Guid {fa564e0f-0c70-4ab9-b863-0177e6ddd247}. When deployed, the list items will behave as normal list items and have a Edit Control Block assigned to the Title column.
Planet all items
PS. Notice that the header of the view shows Title but when creating, editing or viewing list items, you will notice that the first field is labeled as Name.

Step 3 – Add the event receiver to the content type

We will use the event receiver created in the first walkthrough which checks if the newly entered planet already exists in the list or not. To bind an event handler to a content type, you have to add a <XmlDocuments> element into the <ContentType> element. This <XmlDocuments> element can contain 1 or more <XmlDocument> elements. The <Receivers> element is added as a child element to the <Document> element. Create a new XML file with f.e. the name contenttypes.xml and add the following CAML:
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <!--Content Types-->
  <ContentType
    ID="0x0100A3897215697C447eB6612DF4CF4390FE"
    Group="Universe Content Types"
    Name="Planet">
    <FieldRefs>
      <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" DisplayName="Name" />
      <FieldRef ID="{C2A8588A-F8A2-4324-BE39-4962E2E75895}" />
      <FieldRef ID="{85F781F8-0AB3-433e-B828-DC0DD39AE695}" />
      <FieldRef ID="{0E273391-2749-4c17-BCFD-009E2DC98F2F}" />
      <FieldRef ID="{7A86784F-65B3-4b48-8EAF-C77FFED7BB61}" />
      <FieldRef ID="{9677F5F2-4CA6-4d88-9DE1-3518DE4E2A01}" />
    </FieldRefs>
    <XmlDocuments>
      <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/events">
        <Receivers xmlns:spe="http://schemas.microsoft.com/sharepoint/events">
          <Receiver>
            <Name>PlanetEventHandler</Name>
            <Type>ItemAdded</Type>
            <SequenceNumber>10000</SequenceNumber>
            <Assembly>UniverseEventHandlers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=841f382b41c47c60</Assembly>
            <Class>UniverseEventHandlers.PlanetItemEventReceiver</Class>
            <Data></Data>
            <Filter></Filter>
          </Receiver>
        </Receivers>
      </XmlDocument>
    </XmlDocuments>
  </ContentType>
</Elements>

Step 4 – Create a list instance and bind the content type to the list instance

You can deploy the previous CAML to create the site columns and content types. In the user interface of the SharePoint site you can create a custom list and add the content type to it. BUT you can also create a list instance using the CAML element <ListInstance>. To bind a content type to a list instance you have to use the <ContentTypeBinding> element. Add a new XML file with f.e. the name listinstance.xml and add the following CAML to it:
<Elements Id="{F5610B81-A2E2-4957-ABF2-B8ECCD765AF3}"
          xmlns="http://schemas.microsoft.com/sharepoint/">
  <ListInstance
       FeatureId="00BFEA71-DE22-43B2-A848-C05709900100"
       Title="Planets"
       Description="List of planets based on Universe content type."
       Id="10002"
       TemplateType="100"
       OnQuickLaunch = "TRUE"
       Url="Lists/Planets">
  </ListInstance>
  <ContentTypeBinding ContentTypeId="0x0100A3897215697C447eB6612DF4CF4390FE" ListUrl="Lists/Planets"/>
</Elements>
You can read more about creating list instances in CAML here.

Step 5 – Create the feature.xml file

Your feature.xml will look as follows:
<Feature Id="{BA902EC0-2A2A-4ed3-BDC2-042D421F1B96}"
  Title="Event Handlers - Walkthrough 3"
  Description="This feature creates the content type for Event Handlers walkthrough 3."
  Scope="Site"
  Hidden="FALSE"
  ImageUrl="BPoint\BPoint.png"
  xmlns="http://schemas.microsoft.com/sharepoint/">
  <ElementManifests>
    <ElementManifest Location="sitecolumns.xml" />
    <ElementManifest Location="contenttypes.xml" />
    <ElementManifest Location="listinstance.xml" />
  </ElementManifests>
</Feature>

Step 6 – Deploy and test the feature

Deploy the feature and navigate to the Planets list. Click the New button to create a new item. Notice that you can choose between the standard Item and a Planet item. Choose Planet and add data for the planet Mars. Add a second item for the planet Venus. Try to add a second item for the planet Mars and you will see the error page displaying the message that the planet Mars already exists.
Notice also that the All Items view doesn’t display the fields of the content type. You have to add them manually to the standard view or create a new view.
Navigate to the List Settings page of the Planets list. Notice that the list has two content types: the default Item content type and the Planet content type.

Planet content type
You can manually remove the Item content type from the list.
If you want a customized view already attached to the list and only the custom content type available on the list, you will can define a custom list defintion in CAML that inherits from the list definition of the generic list. This topic will be explained in detail in a later post.

No comments:

Post a Comment