In Hour 8, “Understanding Client Object Model in SharePoint 2010,” you learned how SharePoint enables you to hook the events in the life cycle of a SharePoint site collection and a SharePoint site through various event receivers provided by SharePoint. This section looks at the event receivers for a list and for list items. The event receivers for a list and list items can be categorized in two ways. One is the event receivers that get called when an event is happening and before it is completed. For example, the FieldAdding
event is called before a new field is added to the list, and the ItemAdding
event gets called before a new list item is created. Similarly some event receivers get called after the event has happened. For example, the FieldAdded
event receiver gets called after a new field has been added to the list, and the ItemAdded
event receiver gets called after a new list item has been created. You will find that the name of Before events end with “ing” and the name of the After events end with “ed.”
The other way to categorize events is synchronous events and asynchronous events. The ItemAdding
event occurs synchronously, which allows you to set the Cancel
property of the event argument to the item creation. The ItemAdded
event on the other hand is called asynchronously after the item has been added. This means the user might sometimes see the added list item before the ItemAdded
event receiver has been processed or at times after it is processed.
Table 11.1 lists the various events for a SharePoint list. Notice that the names of most of these events are self-describing.
Note that these event handlers are defined in the SPListEventReceiver
class, which is the class that provides methods to trap events that occur for lists. This class is never instantiated, and your event receiver class inherits from this class and overrides the require methods.
Table 11.2 lists the various events for a list item.
Try It Yourself: Write a List Item Event Receiver
You now look at an example of list item events. This example handles the ItemAdded
and ItemUpdated
events of a custom list called Presentations that has the following columns:
• Title—Out of the box single line title field
• ViewerComments—Custom multiline plain text field
• History—Custom multiline plain text field
The user first creates a new item and enters a title and comment in the list item. The ItemAdded
event copies the contents of the Title and ViewerComments fields into the History field in some specific format and clears the ViewerComments field. When the user edits the list item and adds new ViewerComments to it the ItemUpdating
event appends the existing title and the newly added comment to the History field. This way the History field shows a history of the list item. Note that the multiline field has a property that allows you to append the changes to the field, thus effectively storing the history. However, for demonstration purposes this example does it through event receivers.
Follow these steps to create the event receiver:
1. Create a new custom list called Presentations and add two multiline plain text fields called ViewerComments
and History
to it. Open Visual Studio 2010 and create a new Empty SharePoint Project called PresentationsListEvents. Add a new item of type Event Receiver named PresentationsListEventsReceiver
. After clicking on Add you see the Choose Event Receiver Settings screen shown in Figure 11.4. Select List Item Events as the type of event receiver and Custom List for the event source. In the Handle the Following Events section select the An Item Was Added and An Item Was Updated events.
2. Click the Finish button. A new folder is added that has an XML and class file. Rename the newly added feature to some appropriate name. The project structure should look similar to Figure 11.5.
3. Open the PresentationsListEventReceiver.cs. You see a class that inherits from SPItemEventReceiver. This is the base class for handling list item events. Open the elements.xml file. You find the following code:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Receivers ListTemplateId="100">
<Receiver>
<Name>PresentationsListEventsReceiverItemAdded</Name>
<Type>ItemAdded</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>
PresentationsListEvents.
PresentationsListEventsReceiver.
PresentationsListEventsReceiver
</Class>
<SequenceNumber>10000</SequenceNumber>
</Receiver>
<Receiver>
<Name>PresentationsListEventsReceiverItemUpdated</Name>
<Type>ItemUpdated</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>
PresentationsListEvents.
PresentationsListEventsReceiver.
PresentationsListEventsReceiver
</Class>
<SequenceNumber>10000</SequenceNumber>
</Receiver>
</Receivers>
</Elements>
4. This XML is responsible for associating your event handler with the list. The value of the ListTemplateId
in the Receivers
tag is set to 100, which represents a custom list template id and hence your event receivers get associated to a custom list. The Name
, Type
, and Assembly
elements specify the name of the event receiver, the type of the event receiver, and the assembly containing the event receiver. The Class
element specifies the class containing the event handler code. In addition the Sequence
element specifies the sequence of the event receiver to execute in case multiple event receivers of the same type are associated with the same template id.
5. Note that you could also have programmatically associated your event receivers with the Presentations list in the FeatureActivated
event of the feature. The following code programmatically associates an event receiver in the feature activated event and removes it in the feature deactivated event:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb web = properties.Feature.Parent as SPWeb;
SPList list = web.Lists["Presentations"];
SPEventReceiverDefinition newReceiver = list.EventReceivers.Add();
newReceiver.Assembly =
"PresentationsListEvents, Version=1.0.0.0,
Culture=neutral; PublicKeyToken=e05ebc5d87c6e48c";
newReceiver.Class = "PresentationsListEventsReceiver";
newReceiver.Type = SPEventReceiverType.ItemAdded;
newReceiver.SequenceNumber = 10000;
}
public override void FeatureDeactivating(
SPFeatureReceiverProperties properties)
{
SPWeb web = properties.Feature.Parent as SPWeb;
SPList list = web.Lists["Presentations"];
int count = list.EventReceivers.Count;
while (count > 0)
{
SPEventReceiverDefinition curReceiver =
list.EventReceivers[count - 1];
if (curReceiver.Type ==
SPEventReceiverType.ItemAdded
&& curReceiver.Class ==
"PresentationsListEventsReceiver"
&& curReceiver.Assembly ==
"PresentationsListEvents, Version=1.0.0.0,
Culture=neutral; PublicKeyToken=e05ebc5d87c6e48c")
{
curReceiver.Delete();
}
count--;
}
}
6. Now look at the event receiver code. Open the PresentationsListEventsReceiver.cs file, and you find the ItemAdded
and ItemUpdated
methods overridden here. Edit the code in the ItemAdded
and ItemUpdated
event as shown in the following code:
public override void ItemAdded(SPItemEventProperties properties)
{
base.ItemAdding(properties);
if (properties.List.Title != "Presentations")
return;
SPListItem curItem = properties.ListItem;
curItem["History"] =
string.Format(
"Time:{0}
User: {1}
Title:{2}
Comments:{3}
",
curItem["Modified"],
curItem["Modified By"].ToString().Split('#')[1],
curItem["Title"],
curItem["ViewerComments"]);
curItem["ViewerComments"] = string.Empty;
// disable event firing else the item updated event will get called
this.EventFiringEnabled = false;
curItem.Update();
// enable event firing
this.EventFiringEnabled = true;
}
public override void ItemUpdated(SPItemEventProperties properties)
{
base.ItemUpdating(properties);
if (properties.List.Title != "Presentations")
return;
SPListItem curItem = properties.ListItem;
curItem["History"] =
string.Format(
"Time:{0}
User: {1}
Title:{2}
Comments:{3}
{4}",
curItem["Modified"],
curItem["Modified By"].ToString().Split('#')[1],
curItem["Title"],
curItem["ViewerComments"],
curItem["History"]);
curItem["ViewerComments"] = string.Empty;
// disable event firing else the item updated event will get called
this.EventFiringEnabled = false;
curItem.Update();
// enable event firing
this.EventFiringEnabled = true;
}
7. The preceding code updates the current list item represented by properties.ListItem
. The History
field also is updated with the data from the other fields, and the ViewerComments
field is made blank. Also before starting any processing the code checks for the title of the list on which this event was fired. If the list title does not match Presentations we do not do any processing and return from the function. An interesting line of code is where this.EventFiringEnabled
is set to false and then to true. Setting the EventFiringEnabled
property to false prevents any new events from being raised. This is important because you don’t want the event receivers to be called when you are updating the list item from your code.
8. Build the solution and deploy it. Add a new item (see Figure 11.6) and save it.
9. You can see that the ViewerComments
field got cleared and the History
field was updated as shown in Figure 11.7.
10. Edit the list item, enter something in ViewerComments
, and click Save. You see the History
field is updated.
11. At times you may need to refresh the screen to see the changes. This is because the event receivers are running asynchronously. You can make the events run synchronously through the Synchronization
element. Update the Elements.xml as shown here:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Receivers ListTemplateId="100">
<Receiver>
<Name>PresentationsListEventsReceiverItemAdded</Name>
<Type>ItemAdded</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>
PresentationsListEvents.
PresentationsListEventsReceiver.
PresentationsListEventsReceiver
</Class>
<SequenceNumber>10000</SequenceNumber>
<Synchronization>Synchronous</Synchronization>
</Receiver>
<Receiver>
<Name>PresentationsListEventsReceiverItemUpdated</Name>
<Type>ItemUpdated</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>
PresentationsListEvents.
PresentationsListEventsReceiver.
PresentationsListEventsReceiver
</Class>
<SequenceNumber>10000</SequenceNumber>
<Synchronization>Synchronous</Synchronization>
</Receiver>
</Receivers>
</Elements>
Note that the preceding code indicates that our event receivers must run synchronously by setting the Synchronization
element to Synchronous
. Deploy the solution and test the changes. You should now be able to see the changes correctly after saving, and a browser refresh will not be needed anymore.
You can also define the event receivers on content types. To bind an event receiver to a content type you can call the SPContentType.EventReceivers.Add()
method.
While the event receivers framework seems to provide the developer with a lot of flexibility and power and it does provide it, you must be careful about the code written in event receivers as it can drastically impact performance.