Excerpted from Professional SharePoint 2007 Development
Programming Event Handling in Windows SharePoint Services
By Matt Ranlett
WSS v2 offered developers the ability to catch certain user actions in code and react programmatically. These user actions triggered a set of asynchronous events which happened after the user had completed the action. An example is the act of adding a document to a document library. A developer could catch the DocumentAdded event and perform some action. Unfortunately for v2 developers, all of the event handlers were "after the fact" handlers. You can't stop a user from performing an action with an event handler. Another limitation of v2 event handlers is that you can only catch events on document and forms libraries.
Fortunately for us, all of this has changed. Now, WSS v3 has a vastly increased number of events developers can take advantage of. These events include "before the fact" or synchronous events as well as "after the fact" or asynchronous events as illustrated in Figure 1. As a developer interested in catching SharePoint events, you are no longer limited to only document libraries and forms libraries. Now you have the ability to catch events on practically every list type that SharePoint offers, as well as at the site level, list or library level, and at the individual file level. Combine the increase in available functionality of event handlers with Workflow and you'll really have a flexible system.
Figure 1
Receiving SharePoint Events
To catch SharePoint events at an item level, your classes must inherit from the SPItemEventReceiver base class. From this inheritance, your class will be able to take advantage of the methods shown in the following table:
| Public Methods | |
| Name |
Description |
|
ContextEvent |
Item level event reaction to a SPContext class method call. |
|
ItemAdded |
Asynchronous After event that occurs after a new item has been added to its containing object. |
|
ItemAdding |
Synchronous before event that occurs when a new item is added to its containing object. |
|
ItemAttachmentAdded |
Asynchronous after event that occurs after a user adds an attachment to an item. |
|
ItemAttachmentAdding |
Synchronous before event that occurs when a user adds an attachment to an item. |
|
ItemAttachmentDeleted |
Asynchronous after event that occurs when after a user removes an attachment from an item. |
|
ItemAttachmentDeleting |
Synchronous before event that occurs when a user removes an attachment from an item. |
|
ItemCheckedIn |
Asynchronous after event that occurs after an item is checked in. |
|
ItemCheckedOut |
Asynchronous after event that occurs after an item is checked out. |
|
ItemCheckingIn |
Synchronous before event that occurs as a file is being checked in. |
|
ItemCheckingOut |
Synchronous before event that occurs after an item is checked out. |
|
ItemDeleted |
Asynchronous after event that occurs after an existing item is completely deleted. |
|
ItemDeleting |
Synchronous before event that occurs before an existing item is completely deleted. |
|
ItemFileConverted |
Asynchronous after event that occurs after a file has been transformed by the SPFile.Convert method. |
|
ItemFileMoved |
Occurs after a file is moved. |
|
ItemFileMoving |
Occurs when a file is being moved. |
|
ItemUncheckedOut |
Synchronous before event that occurs when an item is being unchecked out. |
|
ItemUncheckingOut |
Synchronous before event that occurs when an item is being unchecked out. |
|
ItemUpdated |
Asynchronous after event that occurs after an existing item is changed, for example, when the user changes data in one or more fields. |
|
ItemUpdating |
Synchronous before event that occurs when an existing item is changed, for example, when the user changes data in one or more fields. |
| Protected Methods |
|
| Name | Description |
|
DisableEventFiring |
Prevents events from being raised.(inherited from SPEventReceiverBase) |
|
EnableEventFiring |
Enables events to be raised.(inherited from SPEventReceiverBase) |
You can also catch events at the List (SPListEventReceiver), Web (SPWebEventReceiver), E-mail (SPEmailEventReceiver), and Feature (SPFeatureReceiver) levels.
Write Code to Cancel an Item Deletion
Let's look at a sample event handler to help understand the core concepts behind handling events. Since your previous levels of functionality left you completely unable to prevent an action on a non-document library list, in this example you'll stop someone from deleting an announcement. While you will be working with the Announcements List Type, the following table is a list of available List Template Types:
|
List Template Type |
Template ID |
|
Custom List |
100 |
|
Document Library |
101 |
|
Survey |
102 |
|
Links List |
103 |
|
Announcements List |
104 |
|
Contacts List |
105 |
|
Events List |
106 |
|
Tasks List |
107 |
|
Discussion Board |
108 |
|
Picture Library |
109 |
To find these list IDs and the IDs of additional list template types, navigate to the Features directory under the \12\TEMPLATE folder on your SharePoint server. Find the folder for the list you are looking for in the Features directory and open the ListTemplates folder under that. If you were looking for the Announcements list type, you'd navigate to C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\AnnouncementsList\ListTemplates. Open the XML file in here to find the following code. Pick out the Template ID Type value.
Code Listing 1
Announcements.xml
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<ListTemplate
Name="announce"
<b>Type="104" </b>
BaseType="0"
OnQuickLaunch="TRUE"
SecurityBits="11"
Sequence="320"
DisplayName="$Resources:core,announceList;"
Description="$Resources:core,announceList_Desc;"
Image="/_layouts/images/itann.gif"/>
</Elements>
To create and use your sample event handler, you'll need to do two things. First you'll need to create the code which will run whenever you catch your desired event. Then you'll need to create a Feature to activate your event handler. Features are described in more depth in Chapter 4, "WSS v3 Platform Services," of the book, Professional SharePoint 2007 Development (Wrox, 2007, ISBN: 978-0-470-11756-9), so you won't be focusing on this portion of the task in great detail.
Open Visual Studio and start a new C# Class Library project called EventHandlerExample1. Since event handlers are part of the Object Model, the first thing you'll need to do is to add a reference to the Microsoft.SharePoint namespace.
- Right click on the References folder and select Add. In the Add Reference dialog, .NET tab, scroll to the bottom and select the
Microsoft.SharePointnamespace. - Next you'll want to rename
Class1.cstoCancelAnnouncementDeleteHandler.cs.
You're now ready to begin writing your code.
The CancelAnnouncementDeleteHandler class needs to inherit from the SPItemEventReceiver class to function, so next to the class name put ": SPItemEventReceiver". This will give access to the methods listedin the previous table. You are trying to prevent an item from being deleted so you will be overriding the ItemDeleting method as seen in Figure 2.
Figure 2
In the ItemDeleting method, you'll cancel the deletion before it happens. Your code should look like Listing 2.
Code Listing 2
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
namespace EventHandlerExample1
{
class CancelAnnouncementDeleteHandler: SPItemEventReceiver
{
public override void ItemDeleting(SPItemEventProperties properties)
{
string HandledMessage = ("Announcements can not be deleted from this list");
properties.ErrorMessage = HandledMessage;
properties.Cancel = true;
}
}
}
The next step to getting your event handler to work is to compile your DLL and deploy it to the GAC. Deploying an assembly to the GAC requires a strongly named assembly, so in the Project Properties dialog, click the Signing tab and check the box to sign the assembly as in Figure 3. Be sure to create a new key file if you haven't already. Build the project and deploy to the GAC by entering the following command into the SDK Command Prompt as in Figure 4.
gacutil /i "C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\EventHandlerExample1\bin\Debug\EventHandlerExample1.dll"
Figure 3
Figure 4
Verify successful deployment by finding your file in the C:\Windows\assembly folder. Normally this GAC deployment would be automated with post build steps or batch files, but has been explained in manual detail here as a learning exercise.
Create a Feature to Enable the Event Handler
The next task is to create and deploy a Feature that activates your event handler.
- Back in Visual Studio, add two new XML files to the project. Call the first XML file
Feature.xmland the second fileElements.xml. - Next, navigate to an existing feature in the C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES folder. You need to create a new folder for your event handler. Create a new folder with the same name as your event handler as in Figure 5.
Figure 5
- While you're here in the FEATURES directory, an example from one of the Microsoft provided Features for your own
Feature.xmlandElements.xml. Navigate into the AnnouncementsList folder and open theFeature.xml. Copy the contents of the AnnouncementsFeature.xmlinto your own Feature.xml in Visual Studio. Now navigate down into the AnnouncementsList\ListTemplate folder and copy the contents of theAnnouncements.xmlinto yourElements.xmlin Visual Studio. Now it's time to update yourFeature.xmlandElements.xmlto match your event handler project. - Start with Feature.xml. You'll need a unique Feature Id value, so generate a new GUID with the Create GUID tool on the Visual Studio Tools menu. Next you want to give your Feature a title and description to match its purpose. Finally, set the scope of the feature to Web. Remove any additional elements such as
VersionandHidden. When you're finished, yourFeature.xmlshould look like Listing 3:
Code Listing 3
<?xml version="1.0" encoding="utf-8"?>
<Feature Id="<b>85DA483B-C3D4-4b5c-8F3A-89331E996305</b>"
Title="<b>EventHandlerExample1</b>"
Description="<b>An event handler which will cancel the deletion of an announcements list item and insert a new item announcing that a delete attempt was made</b>"
Scope="<b>Web</b>"
xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
<ElementManifest Location="Elements.xml"/>
</ElementManifests>
</Feature>
- With
Elements.xml, you need to create a Receivers node in your XML. InElements.xml, theReceiversnode has the following elements and attributes. TheListTemplateIDindicates which template type (se table 5) this event handler targets, in your case the Announcements list type is 104. The name of the receiver is the name of the project. The type of receiver is the function you've overridden in your DLL. TheSequenceNumberis a unique number that identifies this Feature. Microsoft has reserved a significant number of these sequence numbers, but any above 20,000 should be available. The Assembly name is your DLL name, version number, culture, andPublicKeyToken. - To get the
PublicKeyTokenvalue, find your DLL in the GAC (C:\Windows\Assembly), right click on it and select Properties. Copy the public key from the properties dialog as illustrated by Figure 6. Finally, the class element contains the Namespace.classname of your event handler class.
Figure 6
The Elements.xml should look like Listing 4:
Code Listing 4
<?xml version="1.0" encoding="utf-8"?> <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <Receivers ListTemplateId="104"> <Receiver> <Name>EventHandlerExample1</Name> <Type>ItemDeleting</Type> <SequenceNumber>20000</SequenceNumber> <Assembly> EventHandlerExample1, Version=1.0.0.0, culture=neutral, PublicKeyToken=db6373fbcacd33ee </Assembly> <Class>EventHandlerExample1.CancelAnnouncementDeleteHandler</Class> <Data></Data> <Filter></Filter> </Receiver> </Receivers> </Elements>
- Now it's time to save the
Feature.xmlandElements.xmland copy them from your project folder to the Features folder you created above - The next steps are to install and activate the Feature with the
stsadm.exetool and to attempt to delete an announcement. To usestsadm, open a command prompt window and navigate to the bin directory under the 12 folder. First you need to install the feature to the site collection. Use the following command:
C:\Program Files\Common Files\Microsoft Shared\Web server extensions\12\BIN\stsadm.exe -o installfeature -filename EventHandlerExample1\Feature.xml
The parameters to stsadm to install a new feature are "-o installfeature" to indicate which action you're performing and "-filename (Feature Folder Name\Feature.xml)" to indicate which feature you're installing.
- Once you've installed the feature, the final step is to activate this feature. This task can be done from inside the SharePoint UI, but for your purposes
stsadm.exeworks just as well (since you're already in the command dialog you'll just activate the feature from here). Use the following command:
C:\Program Files\Common Files\Microsoft Shared\Web server extensions\12\BIN\stsadm.exe -o activatefeature -filename EventHandlerExample1\Feature.xml -URL http://localhost
The parameters to stsadm to activate a new feature are "-o activatefeature" to indicate which action you're performing, "-filename (Feature Folder Name\Feature.xml)" to indicate which feature you're installing, and "-URL url" to indicate which Web you're activating for.
You should have a command window that looks like the one in Figure 7.
Figure 7
Now go to your site and create an Announcements list and put a few items in it. I've done so with my Sample Announcements list. Select an item and try to delete it. You should get an error message that reads "Announcements cannot be deleted from this list" (see Figures 8 and 9).
Figure 8
Figure 9
Best Practices Coding Techniques
As you've seen in this article, writing code for the SharePoint Object Model requires the instantiation of lots of objects like SPWeb, SPSite and more. Many of these objects implement the IDisposable interface and should be disposed of after they've been used to prevent these objects from remaining in memory.
Scott Harris and Mike Ammerlaan from Microsoft have written an excellent article outlining these best practices recommendations titled Best Practices: Using Disposable Windows SharePoint Services Objects. Any developer interested in writing code targeting the SharePoint platform should read this article. Scott and Mike illustrate many great practices and they warn you about the times that you shouldn't automatically dispose of every disposable object.
This article is excerpted from Chapter 5, "Programming Windows SharePoint Services," from the book Professional SharePoint 2007 Development (Wrox, 2007, ISBN: 978-0-470-11756-9) by John Holliday, John Alexander, Jeff Julian, Eli Robillard, Brendon Schwartz, Matt Ranlett, J. Dan Attis, Adam Buenz, Tom Rizzo. Matt Ranlett, a SQL Server MVP, has been a fixture of the Atlanta .NET developer community for many years. A founding member of the Atlanta Dot Net Regular Guys (www.devcow.com), Matt has formed and leads several area user groups. Matt spends dozens of hours after work on local and national community activities such as the SharePoint 1, 2, 3! series (www.sharepoint123.com), 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. Another recent wrox.com article excerpted from this book is Creating Content Type Metadata for SharePoint 2007 Document Management Solutions, by John Holliday.
