Wrox Home  
Search
Professional SharePoint 2007 Development
by John Holliday, John Alexander, Jeff Julian, Eli Robillard, Brendon Schwartz, Matt Ranlett, J. Dan Attis, Adam Buenz, Thomas Rizzo
June 2007, Paperback


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
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.

  1. Right click on the References folder and select Add. In the Add Reference dialog, .NET tab, scroll to the bottom and select the Microsoft.SharePoint namespace.
  2. Next you'll want to rename Class1.cs to CancelAnnouncementDeleteHandler.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
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 3

Figure 4
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.

  1. Back in Visual Studio, add two new XML files to the project. Call the first XML file Feature.xml and the second file Elements.xml.
  2. 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
Figure 5
  1. While you're here in the FEATURES directory, an example from one of the Microsoft provided Features for your own Feature.xml and Elements.xml. Navigate into the AnnouncementsList folder and open the Feature.xml. Copy the contents of the Announcements Feature.xml into your own Feature.xml in Visual Studio. Now navigate down into the AnnouncementsList\ListTemplate folder and copy the contents of the Announcements.xml into your Elements.xml in Visual Studio. Now it's time to update your Feature.xml and Elements.xml to match your event handler project.
  2. 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 Version and Hidden. When you're finished, your Feature.xml should 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>
  1. With Elements.xml, you need to create a Receivers node in your XML. In Elements.xml, the Receivers node has the following elements and attributes. The ListTemplateID indicates 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. The SequenceNumber is 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, and PublicKeyToken.
  2. To get the PublicKeyToken value, 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
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>
  1. Now it's time to save the Feature.xml and Elements.xml and copy them from your project folder to the Features folder you created above
  2. The next steps are to install and activate the Feature with the stsadm.exe tool and to attempt to delete an announcement. To use stsadm, 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.

  1. 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.exe works 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
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 8

Figure 9
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.