When binding SharePoint event receivers to lists and libraries, the generic method involves using the ListTemplateId element. The problem with this method is that it adds the event receiver to all lists with a matching ListTemplateId. However, in many cases, we only want our event receivers attached to specific list instances.
This article explains how to dynamically add event receivers to lists with specific names across an entire site and its sub sites. The event receivers are bound to the lists in Feature events using C# code instead of the conventional XML method which uses the Elements.xml file.
NOTE: You could also bind an event receiver to a specific list instance using XML by replacing the ListTemplateID element with ListUrl. But you would need to manually activate the Web scoped feature for every site that has the list you’re targeting.
With the method described here, you only need to activate the feature on the root site and the code will recursively search for all matching list instances (using the internal name of the list) and automatically attach the event receivers we want.
Setting Up The Project In Visual Studio
Create an empty SharePoint project. Fill in any desired properties and click on OK.
Specify the site where you want the feature to be activated. Since the feature will be a web scoped feature, it will be available on the Site Features page of all websites. However, you will only need to activate it in the parent site that contains the sub webs with the list(s) we’re interested in.
When asked to specify a trust level, choose “Farm Solution”.
When you press Finish, an empty SharePoint project will be created.
Right-click on the project and press Add >> New Item. Select Event Receiver and give the event receiver an appropriate name.
You will be asked to select the type of event receiver. Choose “List Item Events”. You will also need to select the event source. You can choose any value you like here (say Custom List). The value you select here doesn’t matter because we will bypass/override it using code.
You will also need to select the events you want to handle. For this example, I will handle these events:
- An item was added (ItemAdded)
- An item was updated (ItemUpdated)
- An item is being updated (ItemUpdating)
RELATED READING: BeforeProperties and AfterProperties of the SPItemEventReceiver
When you click Finish, Visual Studio will add the event receiver using the conventional method involving ListTemplateId and the Elements.xml file. A feature will be created with a reference to the containing project. Inside this project an Elements.xml file and a code file will be created. The Elements.xml file holds the settings of the event receiver. The code file holds the code that will be executed.
If you want to add more events to be captured, select the event receiver from the solution explorer and look at the properties window. Here you will see options to enable or disable events that have to be captured.
NOTE: When you disable an already enabled event, the code inside the code file will not be removed.
Now we need to remove the existing connection between the feature and the event receiver.
Bypassing The Elements.xml File Of Our Event Receiver
To remove the binding between our feature and the Elements.xml file of the event receiver, we need to completely exclude the event receiver from the feature. We will however keep the event receiver as part of the assembly because we will use it programmatically later.
Double click on the feature file. In the “Items in the feature” list, select the event receiver and click on the back arrow (<) to remove the event receiver from the feature. This effectively gives you an empty feature.
Save the changes to the feature.
Add The Event Receiver Programmatically
Right-click the feature and add a feature event receiver.
In the newly created feature event receiver class, un-comment both the FeatureActivated and FeatureDeactivating code blocks. The code to add/remove our event receivers to specific lists will be added inside of these blocks.
At the top of the class file, create the event receiver constants that you need. Like this:
const SPEventReceiverType eventTypeAdded = SPEventReceiverType.ItemAdded; const SPEventReceiverType eventTypeUpdated = SPEventReceiverType.ItemUpdated; const SPEventReceiverType eventTypeUpdating = SPEventReceiverType.ItemUpdating;
The key lines of code that needs to be added to the FeatureActivated event to add the event receivers will look like this:
ListC.EventReceivers.Add(eventTypeAdded, Assembly.GetExecutingAssembly().FullName, "EventReceiverDemo.DemoEventReceiver.DemoEventReceiver");
That would add the ItemAdded event receiver.
Add more of such lines as appropriate for all the event receiver types you want to add. You will notice that the Assembly namespace is not recognized until you add a using statement for System.Reflection:
using System.Reflection;
The last parameter of the EventReceivers.Add method can be found in the Elements.xml file which we previously excluded from the feature. A relevant section of this file will look like:
<Receiver> <Name>DemoEventReceiverItemAdded</Name> <Type>ItemAdded</Type> <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly> <Class>EventReceiverDemo.DemoEventReceiver.DemoEventReceiver</Class> <SequenceNumber>10000</SequenceNumber> </Receiver>
Copy the value of the “Class” xml element and use it as the last parameter of the EventReceivers.Add method. $SharePoint.Project.AssemblyFullName$ is replaced by Assembly.GetExecutingAssembly().FullName in the code.
Putting It All Together
Since you might need to add and remove the event receiver multiple times during development and testing, it makes sense to first confirm that the specific event receiver you’re trying to add to a list is not already added. So you might want to first call the following method before adding the event receiver programmatically:
private static void DeleteEvents(SPList list) { SPEventReceiverDefinitionCollection erdc = list.EventReceivers; List<SPEventReceiverDefinition> eventsToDelete = new List<SPEventReceiverDefinition>(); foreach (SPEventReceiverDefinition erd in erdc) { if (erd != null) { try { eventsToDelete.Add(erd); } catch (Exception) { } } } foreach (SPEventReceiverDefinition er in eventsToDelete) { if (er.Type == eventTypeAdded || er.Type == eventTypeUpdated || er.Type == eventTypeUpdating) { er.Delete(); } } }
Here’s the full code of my feature event receiver class. Notice the code in the FeatureDeactivating section. This ensures that any events we added are removed when the feature is deactivated:
using System; using System.Runtime.InteropServices; using System.Security.Permissions; using Microsoft.SharePoint; using System.Reflection; using System.Collections.Generic; namespace EventReceiverDemo.Features.DemoFeature { /// <summary> /// This class handles events raised during feature activation, deactivation, installation, uninstallation, and upgrade. /// </summary> /// <remarks> /// The GUID attached to this class may be used during packaging and should not be modified. /// </remarks> [Guid("5995cad7-361d-4001-9b45-a3aa9553883f")] public class DemoFeatureEventReceiver : SPFeatureReceiver { const SPEventReceiverType eventTypeAdded = SPEventReceiverType.ItemAdded; const SPEventReceiverType eventTypeUpdated = SPEventReceiverType.ItemUpdated; const SPEventReceiverType eventTypeUpdating = SPEventReceiverType.ItemUpdating; #region FeatureActivated public override void FeatureActivated(SPFeatureReceiverProperties properties) { SPSecurity.RunWithElevatedPrivileges(delegate() { SPWeb rootweb = (SPWeb)properties.Feature.Parent; foreach (SPWeb web in rootweb.GetSubwebsForCurrentUser()) { // Add event receivers to Contacts List SPList ListC = null; try { ListC = web.GetList(web.ServerRelativeUrl.TrimEnd('/') + "/Lists/Contacts"); } catch (Exception) { } if (null != ListC) { DeleteEvents(ListC); ListC.EventReceivers.Add(eventTypeAdded, Assembly.GetExecutingAssembly().FullName, "EventReceiverDemo.DemoEventReceiver.DemoEventReceiver"); ListC.EventReceivers.Add(eventTypeUpdated, Assembly.GetExecutingAssembly().FullName, "EventReceiverDemo.DemoEventReceiver.DemoEventReceiver"); } // Add event receivers to Locations List SPList ListL = null; try { ListL = web.GetList(web.ServerRelativeUrl.TrimEnd('/') + "/Lists/Locations"); } catch (Exception) { } if (null != ListL) { DeleteEvents(ListL); ListL.EventReceivers.Add(eventTypeAdded, Assembly.GetExecutingAssembly().FullName, "EventReceiverDemo.DemoEventReceiver.DemoEventReceiver"); ListL.EventReceivers.Add(eventTypeUpdated, Assembly.GetExecutingAssembly().FullName, "EventReceiverDemo.DemoEventReceiver.DemoEventReceiver"); } // Add event receivers to Access List SPList ListA = null; try { ListA = web.GetList(web.ServerRelativeUrl.TrimEnd('/') + "/Lists/Access"); } catch (Exception) { } if (null != ListA) { DeleteEvents(ListA); ListA.EventReceivers.Add(eventTypeAdded, Assembly.GetExecutingAssembly().FullName, "EventReceiverDemo.DemoEventReceiver.DemoEventReceiver"); ListA.EventReceivers.Add(eventTypeUpdated, Assembly.GetExecutingAssembly().FullName, "EventReceiverDemo.DemoEventReceiver.DemoEventReceiver"); } // Add ItemUpdated event receiver to Information List SPList ListI = null; try { ListI = web.GetList(web.ServerRelativeUrl.TrimEnd('/') + "/Lists/Information"); } catch (Exception) { } if (null != ListI) { DeleteEvents(ListI); ListI.EventReceivers.Add(eventTypeUpdating, Assembly.GetExecutingAssembly().FullName, "EventReceiverDemo.DemoEventReceiver.DemoEventReceiver"); } } }); } #endregion #region FeatureDeactivating public override void FeatureDeactivating(SPFeatureReceiverProperties properties) { SPSecurity.RunWithElevatedPrivileges(delegate() { SPWeb rootweb = (SPWeb)properties.Feature.Parent; foreach (SPWeb web in rootweb.Webs) { // Remove event receivers from Contacts List SPList ListC = null; try { ListC = web.GetList(web.ServerRelativeUrl.TrimEnd('/') + "/Lists/Contacts"); } catch (Exception) { } if (null != ListC) { DeleteEvents(ListC); } // Remove event receivers from Locations List SPList ListL = null; try { ListL = web.GetList(web.ServerRelativeUrl.TrimEnd('/') + "/Lists/Locations"); } catch (Exception) { } if (null != ListL) { DeleteEvents(ListL); } // Remove event receivers from Access List SPList ListA = null; try { ListA = web.GetList(web.ServerRelativeUrl.TrimEnd('/') + "/Lists/Access"); } catch (Exception) { } if (null != ListA) { DeleteEvents(ListA); } // Remove event receivers from Information List SPList ListI = null; try { ListI = web.GetList(web.ServerRelativeUrl.TrimEnd('/') + "/Lists/Information"); } catch (Exception) { } if (null != ListI) { DeleteEvents(ListI); } } }); } #endregion #region DeleteEvents (Helper method) private static void DeleteEvents(SPList list) { SPEventReceiverDefinitionCollection erdc = list.EventReceivers; List<SPEventReceiverDefinition> eventsToDelete = new List<SPEventReceiverDefinition>(); foreach (SPEventReceiverDefinition erd in erdc) { if (erd != null) { try { eventsToDelete.Add(erd); } catch (Exception) { } } } foreach (SPEventReceiverDefinition er in eventsToDelete) { if (er.Type == eventTypeAdded || er.Type == eventTypeUpdated || er.Type == eventTypeUpdating) { er.Delete(); } } } #endregion } }
Hope this helps someone. If you have used this method before or found it helpful, please add your comments below.
thanks!
Thanks man! This helps a lot.
Question on the GUID. Right before the “public class DemoFeatureEventReceiver” line you have the GUID. Can you explain on this? Do I need to have this as well? If so, how do I obtain the GUID to use?
Thanks! This is a great article.
Disregard my last question. I see the GUID line gets insert automatically when you the new event receiver.
Thanks again for a great article.
Great to know you found it helpful. Best regards.
I’ve followed these instructions to create two event receivers: ItemAdded and ItemUpdated. Today that my ItemAdded event receiver isn’t getting removed after the feature has been activated but the ItemUpdated is.
I used Powershell to delete the rogue ItemAdded receiver. Then re-activated my feature. Both ItemAdded and ItemUpdated are there, but when I de-activate the feature again, only the ItemUpdated is removed.
Do you have any ideas on this?
Hi Greg,
You’re probably missing something in your code and you might need to share it here so I can take a quick look. Otherwise, here are a few things you might want to try:
1. Confirm that you’re not mixing up ItemAdded with ItemAdding events. Check this in the actual event you created and in the conditional check within the DeleteEvents method.
2. Maybe you have an exception occurring somewhere. If that is the case, you may not get any error messages because if you notice, the catch blocks in my code are empty. To fix this, you may need to debug your FeatureDeactivating event by stepping through the code in Visual Studio. Or you can add an extra line of code to log exceptions to some SharePoint list somewhere – and then you can inspect that list.
3. And… Are both the ItemAdded event and the ItemUpdated event on the same list?