Event Receivers


Event Receivers represent a developer-extensibility mechanism that can be used to add behaviors to various elements such as lists and list items. An event receiver is a class that contains one or more methods known as event handlers that are executed automatically by WSS in response to events such as a user adding a column to a list or a user deleting a list item. The event handlers are typically written to perform data validation, to ensure data integrity, or to kick off custom business processing.

Events supported by WSS can be separated into two categories: before events and after events. A before event fires before an action has been completed. For example, WSS fires a FieldAdding event when a user attempts to add a new column to a list. It is referred to as a “before” event because it fires before the field is added to the list, and the event gives the developer an opportunity to supply an event handler, which cancels the action. Before events are executed synchronously in a blocking manner on the same thread that handles the request. For this reason, before events are sometimes referred to as synchronous events.

An after event is different because it fires after an action has been completed, and it does not provide the developer with an opportunity to cancel the action. For example, WSS fires an ItemAdded event after an item has been added to a list. An after event such as this provides the developer with the opportunity to write an event handler to kick off custom business processing such as reformatting a field value inside the item that has just been added or sending out a notification e-mail message. Note that after events are nonblocking, because they are executed asynchronously on a secondary thread. For this reason, after events are sometimes referred to asynchronous events.

Tip 

Event Receivers are equivalent to the Event Sink concept from WSS 2.0, although event receivers can be applied to any list type, whereas Event Sinks in WSS 2.0 can only be applied to Document Library–based lists.

To create an event receiver, you must create a class that inherits from one of the special event receiver base classes provided by the WSS object model. It is also important to note that event receiver classes must be compiled into strong-named assemblies and deployed in the GAC before they can be used or tested.

The LitwareTypes project contains three different examples of event receiver classes. The first one we will examine is the event receiver class named VendorListEventReceiver from the source file VendorListEventReceiver.cs, which is shown in Listing 6-14. This class has been written to inherit from the SPListEventReceiver class so that it can handle list-based events. As in the case of all event receiver classes, you create event handler method implementations by overriding methods defined in the base class.

Listing 6-14: An example list event receiver

image from book
  The VendorListEventReceiver Class using System; using Microsoft.SharePoint; namespace LitwareTypes {   public class VendorListEventReceiver : SPListEventReceiver {     public override void FieldAdding(SPListEventProperties properties) {       properties.ErrorMessage = "You cannot change this list schema!";       properties.Cancel = true;     }     public override void FieldUpdating(SPListEventProperties properties) {       properties.ErrorMessage = "You cannot change this list schema!";       properties.Cancel = true;     }     public override void FieldDeleting(SPListEventProperties properties) {       properties.ErrorMessage = "You cannot change this list schema!";       properties.Cancel = true;     }   } } 
image from book

The VendorListEventReceiver class provides three event handlers by overriding the methods named FieldAdding, FieldUpdating, and FieldDeleting. Because all three of the events being handled are before events, the event handlers can cancel whatever action caused the event to fire. In this case, the three event handlers use the SPListEventProperties parameter to assign an error message and cancel the event. After these event handlers have been properly bound to a list, they will prevent the users (even those users with administrator privileges) from being able to add, rename, or delete any of its fields.

Now it’s time to discuss how to bind the event handlers within this event receiver class to a list type. You can accomplish this by adding a Receivers element within a feature like the one shown in Listing 6-15. The Receivers element in this example has a ListTemplateId attribute with a value of 10001 that will bind its inner receivers to all instances of our custom VendorList type. Note that inside the Receivers element there is an individual Receivers element for each event handler. You should observe that each event handler method must be bound by its own Receiver element because there is no way to bind multiple event handlers at once.

Listing 6-15: You can use a feature to bind event handlers to a specific list type.

image from book
  <Elements xmlns="http://schemas.microsoft.com/sharepoint/">   <!-- Receivers element can only be used in feature where Scope=Web -->   <Receivers ListTemplate >     <Receiver>       <Name>Field Adding Event</Name>       <Type>FieldAdding</Type>       <Assembly>LitwareTypes, [full 4-part assembly name goes here] </Assembly>       <Class>LitwareTypes.VendorListEventReceiver</Class>       <SequenceNumber>1000</SequenceNumber>     </Receiver>     <Receiver>       <Name>Field Updating Event</Name>       <Type>FieldUpdating</Type>       <Assembly>LitwareTypes, [full 4-part assembly name goes here] </Assembly>       <Class>LitwareTypes.VendorListEventReceiver</Class>       <SequenceNumber>1000</SequenceNumber>     </Receiver>     <Receiver>       <Name>Field Deleting Event</Name>       <Type>FieldDeleting</Type>       <Assembly>LitwareTypes, [full 4-part assembly name goes here] </Assembly>       <Class>LitwareTypes.VendorListEventReceiver</Class>       <SequenceNumber>1000</SequenceNumber>     </Receiver>   </Receivers> </Elements> 
image from book

The technique shown in Listing 6-15 for binding event handlers by using a Receivers element has a few noteworthy limitations. First, a Receivers element can be used only in features scoped at the site level. It cannot be used in features scoped at other levels including the site collection level, which means we cannot use this event binding technique in the LitwareTypes feature. Secondly, the Receivers element allows you to bind event handlers only to a list type. It does not provide the flexibility to bind event handlers to list instances or to a content type. In many cases, you will want to use the WSS object model to bind your event handlers, because this approach provides much more flexibility.

Now let’s discuss how to bind event handlers to a list instance instead of a list type. Listing 6-16 shows code using the WSS object model to bind the event handlers of the VendorListEventReceiver class to the list instance named Vendors. This code has been written inside the FeatureActivated method, which executes as the final part of the activation sequence for the LitwareTypes feature. If you examine this code, you can see it acquires a reference to the SPList object for the Vendors list and then calls the Add method on the EventReceivers property. When you add an event handler in this fashion, the event binding information is persisted in the content database just like any other customization that is recorded for the target list.

Listing 6-16: Event handlers can be bound to list events through the WSS object model.

image from book
  public override void FeatureActivated(SPFeatureReceiverProperties properties) {   SPSite siteCollection = (SPSite)properties.Feature.Parent;   SPWeb site = siteCollection.RootWeb;   SPList lstVendors = site.Lists["Vendors"];   string asmName = "LitwareTypes, [full 4-part assembly name goes here] ";   string listReceiverName = "LitwareTypes.VendorListEventReceiver";   // add event receiver to fire before new column is added   lstVendors.EventReceivers.Add(SPEventReceiverType.FieldAdding,                                 asmName, listReceiverName);   // add event receiver to fire before existing column is updated   lstVendors.EventReceivers.Add(SPEventReceiverType.FieldUpdating,                                 asmName, listReceiverName);   // add event receiver to fire before existing column is updated   lstVendors.EventReceivers.Add(SPEventReceiverType.FieldDeleting,                                 asmName, listReceiverName); } 
image from book

Writing Event Handlers

It is common to write event handlers that allow an action to occur only if a certain condition is met. For example, you can write an ItemDeleting handler that cancels the action whenever the current user is not a site administrator.

  public class VendorItemEventReceiver : SPItemEventReceiver {   public override void ItemDeleting(SPItemEventProperties properties) {     if (!properties.OpenWeb().CurrentUser.IsSiteAdmin) {       properties.Status = SPEventReceiverStatus.CancelWithError;       properties.ErrorMessage = "Vendor can only be deleted by site administrator";       properties.Cancel = true;     }   } } 

It is also common to write event handlers for before events that contain validation logic. For example, imagine a scenario in which you want to require that any item added to the Vendors list contain a phone number that is at least seven characters in length. Therefore, you must supply an ItemAdding event handler as well as an ItemUpdating event handler to perform the validation logic. Listing 6-17 shown an example of how to create such an event receiver class with a helper method named PhoneIsValid so that you can maintain all the validation logic in one place.

Listing 6-17: An example of how to structure event validation logic

image from book
  public class VendorItemEventReceiver : SPItemEventReceiver {   // provide method with validation logic   private bool PhoneIsValid(string Phone) {     if ((Phone == null) || (Phone.Length < 7))       return false;     else       return true;   }   // provide error message   const string PhoneInvalidErrorMessage =         "VALIDATION ERROR: Phone must be at least 7 digits.";   public override void ItemAdding(SPItemEventProperties properties) {     // validate Phone column for new vendor item     string Phone = properties.AfterProperties["WorkPhone"].ToString();     if (!PhoneIsValid(Phone)) {       properties.Status = SPEventReceiverStatus.CancelWithError;       properties.ErrorMessage = PhoneInvalidErrorMessage;       properties.Cancel = true;     }   }   public override void ItemUpdating(SPItemEventProperties properties) {     // validate Phone column for update to vendor item     string Phone = properties.AfterProperties["WorkPhone"].ToString();     if (!PhoneIsValid(Phone)) {       properties.Status = SPEventReceiverStatus.CancelWithError;       properties.ErrorMessage = PhoneInvalidErrorMessage;       properties.Cancel = true;     }   } } 
image from book

Note that the ItemAdding event handler and the ItemUpdating event handler shown in Listing 6-17 must each obtain the WorkPhone column value that the user is attempting to save. These event handler methods accomplish this by using the AfterProperties property of the SPItemEventProperties parameter. The AfterProperties property allows you to gain access to column values of the item being modified using the underlying Name of the field (not the DisplayName). Likewise, the SPItemEventProperties parameter also exposes a BeforeProperties property so that you can determine the initial value of a column before it was changed by the user in scenarios that require you to perform change-based validation.

Although before events are typically used for validation, after events can be used to maintain data integrity or to kick off custom processing. Imagine a scenario in which the Company names must all be maintained with uppercase characters. In this case, we will write event handlers for after events that reformat a column value any time it is changed. Listing 6-18 shows a receiver class named CompanyItemEventReceiver that provides an ItemAdded event handler and an ItemUpdated event handler. As you can see, these event handlers simply take the updated column value, modify it to the required format, and then save it back to the same column in the current list item.

Listing 6-18: Event handlers for after events can be used to maintain data integrity.

image from book
  public class CompanyItemEventReceiver : SPItemEventReceiver {   // custom logic to format a field value   private string FormatCompanyName(string value) {     return value.ToUpper();   }   public override void ItemAdded(SPItemEventProperties properties) {     DisableEventFiring();     string CompanyName = properties.ListItem["Company"].ToString();     properties.ListItem["Company"] = FormatCompanyName(CompanyName);     properties.ListItem.Update();     EnableEventFiring();   }   public override void ItemUpdated(SPItemEventProperties properties) {     DisableEventFiring();     string CompanyName = properties.ListItem["Company"].ToString();     properties.ListItem["Company"] = FormatCompanyName(CompanyName);     properties.ListItem.Update();     EnableEventFiring();   } } 
image from book

You should observe the calls to DisableEventFiring and EnableEventFiring in the event handlers in the CompanyItemEventReceiver class in Listing 6-18. By disabling event handling inside an event handler, you can update columns within a list without causing additional events to fire. This is necessary to prevent recursive behavior that could cause the code inside an event handler to fire itself repeatedly.

Binding Event Handlers to a Content Type

In the previous section, we created the CompanyItemEventReceiver class to maintain data integrity on Company names that must all be formatted using uppercase characters. As the final topic of this chapter, we now want to show how to bind the event handlers within this event receiver class to the Company content type. This is accomplished inside the content type definition by using an inner XmlDocument node, which is shown in Listing 6-19. As you can see, each event handler must be bound separately using the same information that is used to bind event handlers with the object model or with a Receivers element in afeature.

Listing 6-19: An XmlDocument element can be used to bind an event handler to a content type.

image from book
  <ContentType  Name="Company" >   <!-- FieldRefs element omitted for clarity -->   <!-- event handlers added to content type using XmlDocument element -->   <XmlDocuments>     <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/events">       <spe:Receivers xmlns:spe="http://schemas.microsoft.com/sharepoint/events">         <spe:Receiver>           <spe:Name>ItemAddedReceiver</spe:Name>           <spe:Type>ItemAdded</spe:Type>           <spe:Assembly>LitwareTypes, [full 4-part assembly name] </spe:Assembly>           <spe:Class>LitwareTypes.CompanyItemEventReceiver</spe:Class>         </spe:Receiver>         <spe:Receiver>           <spe:Name>ItemUpdatedReceiver</spe:Name>           <spe:Type>ItemUpdated</spe:Type>           <spe:Assembly>LitwareTypes, [full 4-part assembly name] </spe:Assembly>           <spe:Class>LitwareTypes.CompanyItemEventReceiver</spe:Class>         </spe:Receiver>       </spe:Receivers>     </XmlDocument>   </XmlDocuments> </ContentType> 
image from book




Inside Microsoft Windows Sharepoint Services Version 3
Inside Microsoft Windows Sharepoint Services Version 3
ISBN: 735623201
EAN: N/A
Year: 2007
Pages: 92

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net