Friday 14 June 2013

Content Type Event Receiver

With all this talk of workflows it's important to make note of another automating tool and that tool is event receivers. We are going to make an event receiver for a category list to ensure that the user does not enter duplicate entries accidentally.

1.) To get started, create a content type with two OOTB site columns title and description: if you have the cksDev tools, just add a content type with an event receiver. 

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <!-- Parent ContentType: Item (0x01) -->
  <ContentType ID="0x0100764cabc1856e471db2475d3c1414c779"
               Name="Ct_eventRecievers - Simple_CT"
               Group="Custom Content Types"
               Description="My Content Type"
               Inherits="TRUE"
               Version="0">
    <FieldRefs>
      <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" 
                Name="Title" 
                DisplayName="Category" />
      <FieldRef ID="{92bba27e-eef6-41aa-b728-6dd9caf2bde2}" 
                Name="Description" 
                DisplayName="Category Description" />
    </FieldRefs>
    <!--Insert xmlDocuments snippet here-->
  </ContentType>
</Elements>

2.) After that you're going to have to add the following xmlDocuments

<XmlDocuments>
  <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/events">
    <Receivers xmlns:spe="http://schemas.microsoft.com/sharepoint/events">
      <Receiver>
        <Name>Simple_EventHandler</Name>
        <Type>ItemAdding</Type>
        <SequenceNumber>10000</SequenceNumber>
        <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
        <Class>$SharePoint.Type.d46d2530-cb14-4f80-9853-fe51ecf97d9e.FullName$</Class>
        <Data></Data>
        <Filter></Filter>
      </Receiver>
    </Receivers>
  </XmlDocument>
</XmlDocuments>

if you're curious about the two highlighted lines, check out msdn Replaceable Parameters

3.) Now add a regular c# code file
name it whatever you please.

4.) open it up and make the following changes:
  • add the following libraries
    • using System;
    • using System.Runtime.InteropServices;
    • using Microsoft.SharePoint;
  • Add the guid you used before as a class attribut
    • [Guid("d46d2530-cb14-4f80-9853-fe51ecf97d9e")]
  • inherit from the SPItemEventReciever class
  • set the class to public
You should end up with:

using System;
using System.Runtime.InteropServices;
using Microsoft.SharePoint;
 
namespace Ct_eventRecievers.Simple_CT
{
    [Guid("df45d565-73d2-447f-a463-5a362d2fc50d")]
    public class Simple_EventReceiver : SPItemEventReceiver
    {
    }
}

5.) Now add you're event, the ones that end in (ing) are synchronous, the ones that end in (ed) are asynchronous, the difference is that the former waits for the result before moving on and the latter does not. I'm going to add ItemAdding, notice that I specified it in the xml in my elements file.

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

remove the base.ItemAdding(properties); and add your own logic

public override void ItemAdding(SPItemEventProperties properties)
{
    string title = properties.AfterProperties["Title"].ToString();
 
    using (SPWeb web = properties.OpenWeb())
    {
        SPList categoryList = web.Lists[properties.ListId];
 
        SPQuery query = new SPQuery();
        query.Query = string.Format(@"
            <Where>
                <Eq>
                    <FieldRef Name='Title' />
                    <Value Type='Text'>{0}</Value>
                </Eq>
            </Where>", title);
 
        SPListItemCollection items = categoryList.GetItems(query);
 
        if (items != null && items.Count > 0)
        {
            properties.ErrorMessage = string.Format(
                @"An item with the title {0} already exists.", title);
            properties.Status = SPEventReceiverStatus.CancelWithError;
            properties.Cancel = true;
        }
    }
}


I added a quick search for items that contain the same title as submitted and cancel the process if any are returned.

6.) make a list definition and instance from this content type using declarative XML, the event receiver will not fire if you simply add the content type to a generic list. you can however create the list definition and then use the UI to create your instance. anyway lets fast forward this part. (List definition & Instance) just use the Content type from this post instead.

OK,so you have your list definition and instance created, now in the UI add an item with the title test, after you've added one add a second one with the same title.

and you should get the following error, notice the message.

and that's it you've made your first content type with an even receiver, again I can't stress this enough you need to have a list template/definition (one and the same) built in your visual studio project.