Events are built into various places in
SharePoint; for example, events are available on a list. Suppose that
when an item is added into a list, you want to execute some code. Or
maybe you want to execute some code right before the item is added so
you can validate that item, and maybe even cancel the addition if it
fails the validation. Could you also show a proper error message to the
end user? Say, your item could not be added because "insert reason
here"? Also, there could be events on objects other SharePoint list
items. For instance, maybe you want to perform some action when a new
subsite gets added into a site. All of these are good candidates for
the implementation of events. And to satisfy this need, the SharePoint
object model provides you with a rich event model infrastructure.
Events inside of .NET generally follow the "ed",
"ing" design pattern. SharePoint does the same! Thus for instance,
there is an ItemAdding event, and there is an ItemAdded event. As the
name suggests, the ItemAdding event occurs right before the item has
been added. This event is called synchronously, so you can set a
property (e.Cancel = true) on a passed-in parameter in the event
handler to cancel the addition. ItemAdded, however, occurs
asynchronously right after an item has been added. Thus the user may
see the data right before the event processing is complete. But if the
computer's mood was better that day and it was quick in processing your
post "ed" ItemAdded event, then the user may see half-cooked data. This
is a problem, as you can tell, but it also has a new solution in
SharePoint 2010, as you will see shortly. But before we go much
further, let's write a very simple example of an event receiver and
actions so we understand the basic concepts first.
I genuinely care about what everybody thinks about
me (NOT!), so I intend to set up a SharePoint survey in which I will
ask everyone what they think about me. To keep things simple, the
survey will have only one question: "Is Sahil a good boy?". There are
only two values as answers: "Yes" and "No". Now obviously the right
answer is "Yes", so if users choose to answer "No", in the item added
event receiver, I will change their answer to "Yes". Thus, first go
ahead and create a new survey list, and call it "Sahil Feedback". If
you created this list based on the survey list definition, immediately
after creating the list, SharePoint will ask you to provide questions
for the list. I'm going to add only one question: "Is Sahil a good
boy?" of type "Choice", with possible values of "Yes" and "No". Yes is
the default, and the user must provide a value (see Figure 1).
Click Finish to complete the definition of your
survey. Now don't take the survey just yet because I'm going to use an
event receiver to rig the survey so all answers are "Yes".
Pop open Visual Studio 2010 and create a project based on the empty SharePoint project template. Call this project EventReceivers
and choose it to be a sandbox solution. In this project, add a new
SharePoint item and choose to add an event receiver. When adding an
event receiver, Visual Studio provides you with a dialog box asking
what kind of event you want to create. They are many choices and some
of them are new in SharePoint 2010, as shown in Figure 2.
Go ahead and look around the kinds of events you can add as an event
receiver. When you're done looking, choose to add a "List Item Events"
event receiver added to the survey item and choose to handle the "an
item was added" event.
Choose to call this newly added event receiver Surveyrigger.
Adding this new event receiver adds a class that contains the actual
implementation of the EventReceiver. This class inherits from the
SPItemEventReceiver base class. Similarly, a ListEventReceiver inherits
from SPListeventReceiver, an Email Event Receiver inherits from
SPEmailEventReceiver, a Web event receiver inherits from
SPWebEventReceiver, and a List Workflow Events event receiver inherits
from SPWorkflowEventReceiver base class. Each one of these base classes
provides you with suitable methods targeted to the target object you
can attach this EventReceiver to. For instance, the Surveyrigger that
you just created has suitable overrides for ItemAdding/ItemAdded,
ItemDeleting/ItemDeleted, and many others. When I created this
EventReceiver, I chose the "An item was added" check box for an item
was added. Thus a default implementation for ItemAdded has been created
for me in surveyrigger.cs. Go ahead and change this implementation, as
shown in Listing 1.
Example 1. The ItemAdded Implementation to Rig the Survey
public override void ItemAdded(SPItemEventProperties properties) { base.ItemAdded(properties); SPListItem item = properties.ListItem; string fieldName = "Is_x0020_Sahil_x0020_a_x0020_goo"; if (item[fieldName].ToString() == "No") { item[fieldName] = "Yes"; item.Update(); } }
|
As you can tell from Listing 1,
I'm getting a handle to the item that was just added to the list, and
I'm checking to see whether the answer they provided was "No". If the
answer they provided was "No", I'm changing the answer to "Yes".
So the logic of my survey EventReceiver is now
complete. The next question over here is this: what list does this
EventReceiver get attached to? Let's open the element.xml file. The
current code for elements.xml should look like Listing 2.
Example 2. Unedited Code for elements.xml
<Receivers ListTemplateId="102"> <Receiver> <Name>SurveyriggerItemAdded</Name> <Type>ItemAdded</Type> <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly> <Class>EventReceivers.Surveyrigger.Surveyrigger</Class> <SequenceNumber>10000</SequenceNumber> </Receiver> </Receivers>
|
Listing 2
shows the CAML syntax for attaching an EventReceiver. But as it stands
right now, the EventReceiver is getting attached to every list with
TemplateID 102, which means that every survey list in the entire site
collection will get this EventReceiver attached with it. That is
certainly not what I intend to do! In fact, with every individual
event, I may want to tie it to a specific site or even a specific web.
Why not even a specific list? In the Receivers element, you can choose
to specify three additional attributes:
Scope = Web, or Site: Allows you to restrict the event receiver to the whole site collection or just an individual SPWeb.
RootWebOnly:
Allows you to specify that this event receiver is attached to lists
with matching template IDs only on the root web of the site collection.
ListUrl:
Allows you to attach this event receiver to a specific list, which is
what we would like to do. Also, because you are being so specific about
the specific list you wish to attach this event receiver to, you will
also need to delete the ListTemplateID attribute. Thus, go ahead and
modify the Receivers element, as shown following:
<Receivers ListUrl="/Lists/Sahil%20Feedback/">
With your project complete, now go ahead and build
and deploy this solution to your SharePoint site. Then visit the "Sahil
Feedback" survey and respond to it. When asked "Is Sahil a good boy?",
answer "No". Note that even though you answered "No", your answer was
changed to "Yes" by the EventReceiver. You can choose to run the code
in debug mode to verify that it was indeed the EventReceiver that is
changing your answer for you.
So apparently my rigged survey is now
working. This is good, but there's one big problem. The ItemAdded event
handler executes after the item has been added. In other words, between
the item being added and the event handler changing the user's
response, the user is given control back on the page. Thus there is a
finite probability that the EventReceiver will not execute fast enough,
and the users might see the original response (although hitting Refresh
will show them that changed response). This is obviously less than
ideal. Is it possible that I can execute the item added EventReceiver
synchronously instead of asynchronously? In other words, my post('ed)
EventReceiver finishes execution, before the user's page is refreshed.
This was not possible in SharePoint 2007 with post events (events that
end in 'ed). But it is possible with SharePoint 2010 using
post-synchronous event receivers. Let's enhance this example to see it
in action, but first let's look at the various other SharePoint 2010
improvements in the event model.