Add a Counter to a SharePoint List Using an Event Receiver

Home » SharePoint Development » Add a Counter to a SharePoint List Using an Event Receiver

Add a Counter to a SharePoint List Using an Event Receiver

Posted on

I have often had the need to add a sequential counter to a list in lieu of the built-in list ID field. Why? Well, to begin with, the ID field isn’t very user friendly. Let’s say you have a list with 1000 items in it, and your view breaks those items down into chunks of 100 per page. Assuming that items are added and removed from the list on a regular basis, those ID’s will be all over the map. When SharePoint creates a new list item it never duplicates a previous ID but rather creates a new unique ID value. So if you delete item #24 and create a new entry it could end up having an ID of 326, or 1001, or whatever the upper bound of the list is at the time. So if you’re looking at results 100 – 150 how would you know which item is number 136? There’s just no way to tell using the ID field alone.

In addition, I find that I’m partial to retrieving list items into a datatable for binding to a data grid, drop-down list, multi-select list, and so on. Without a sequential list of ID’s it’s difficult to apply any intelligent sorting and filtering without having to resort to a bunch of inefficient workarounds. Paging also becomes an issue that is easily overcome with sequential ID’s.

So the question is, how best to achieve this? One option is a custom field type, which is the most flexible and easiest to use repeatedly but they’re also wickedly hard to code, deploy and test. I have yet to find a solution to the problem using calculated fields that involves anything less than a dozen steps and two separate lists, so that’s not really an effective option. My favorite method is to create a simple event receiver that overrides the ItemAdded event and applies a sequential ID to each item after it is inserted into the list.

Below is a C# code sample of a very basic Event Receiver to handle this task:

public class ItemCounter : SPItemEventReceiver

{

//Override the ItemAdded event intead of ItemAdding to prevent concurrency issues

public override void ItemAdded(SPItemEventProperties properties)

{

base.ItemAdded(properties);

try

{

//Stop other events from firing while this method executes

this.DisableEventFiring();

//Get the list item from the event properties

SPListItem item = properties.ListItem;

//Get the parent list from the list item

SPList list = item.ParentList;

 

//Create a variable to hold the new ID value

int sId = 0;

//Get a count of all list items

int iCount = list.ItemCount;

//The ID of the last item in the list is the count minus one

int iLast = iCount – 1;

 

if (iCount > 0)

{

//Set the sId variable to the value of the last item in the list plus one

sId = Convert.ToInt32(list.Items[iLast]["SequentialId"].ToString());

item["SequentialId"] = sId + 1;

//Update the list item to apply the new value

item.Update();

}

}

catch

{ }

finally

{

//Re-enable event firing

this.EnableEventFiring();

}

}

}

 

Once applied to the list, the Event Receiver code will set the SequentialId field of the new item to a value equal to the previous item’s SequentialId field plus one. This insures an uninterrupted list of sequential ID’s.

With regards to applying the Event Receiver to a list, one of the most common frustrations I hear from developers is the inability to scope the receiver to a specific list using a feature. This is a noted limitation in the feature framework and there is no alternative but to do it programmatically. If you’re not feeling up to the task, the Event Receiver Manager on CodePlex will solve the problem for you. But if you’d like to take a stab at doing it yourself, here’s some code you can add as codebehind for an ASPX page and deploy to the /_layouts directory (obviously, you’ll need to create the matching fields in the .aspx page and format it as necessary):

 

public partial class EventReceiverActivation : System.Web.UI.Page

{

//Declare the visual elements

protected Label lblError;

protected TextBox tbBlogUrl;

protected TextBox tbListName;

protected Button btnActivate;

protected Button btnDeactivate;

protected string sAssemblyName = "BinaryWave.ListEvents, Version=1.0.0.0, Culture=neutral, PublicKeyToken=32c475cd525bcb35";

protected string sClassName = "BinaryWave.ListEvents.ItemCounter";

 

protected override void OnInit(EventArgs e)

{

base.OnInit(e);

//Wire up the event handlers for the Activate and Deactivate buttons

this.btnActivate.Click += new EventHandler(btnActivate_Click);

this.btnDeactivate.Click += new EventHandler(btnDeactivate_Click);

}

 

protected void btnActivate_Click(object sender, EventArgs e)

{

try

{

using (SPSite site = new SPSite(tbBlogUrl.Text))

{

using (SPWeb web = site.OpenWeb())

{

//This is a bit of a cheat – it would be far better to enumerate all the lists in the web and provide a drop-down for the user

//instead of forcing them to enter a list name manually.

SPList list = web.Lists[tbListName.Text];

//Add the new Event Receiver to the collection

list.EventReceivers.Add(SPEventReceiverType.ItemAdded, sAssemblyName, sClassName);

string sClasses = "";

sClasses += "Class " + sClassName + " has been successfuly added to " + tbListName.Text + ".<br /><br />";

sClasses += "The following Events are now associated with this list: <br />";

 

//Enumerate all the Event Receivers and list them at the top of the page

SPEventReceiverDefinitionCollection eventdefs = list.EventReceivers;

foreach (SPEventReceiverDefinition eventdef in eventdefs)

{

sClasses += "<li>" + eventdef.Class + "</li>";

}

lblError.Text = sClasses;

}

}

}

catch (System.Exception ex)

{

lblError.Text = ex.Message;

}

}

 

protected void btnDeactivate_Click(object sender, EventArgs e)

{

try

{

using (SPSite site = new SPSite(tbBlogUrl.Text))

{

using (SPWeb web = site.OpenWeb())

{

SPList list = web.Lists[tbListName.Text];

 

//Find the receiver in the collection and remove it

SPEventReceiverDefinitionCollection eventReceivers = list.EventReceivers;

foreach (SPEventReceiverDefinition def in eventReceivers)

{

if (def.Class == sClassName)

{

def.Delete();

break;

}

}

string sClasses = "";

sClasses += "Class " + sClassName + " has been successfuly removed from " + tbListName.Text + ".<br /><br />";

sClasses += "The following Events are now associated with this list: <br />";

SPEventReceiverDefinitionCollection eventdefs = list.EventReceivers;

foreach (SPEventReceiverDefinition eventdef in eventdefs)

{

sClasses += "<li>" + eventdef.Class + "</li>";

}

lblError.Text = sClasses;

}

}

}

catch (System.Exception ex)

{

lblError.Text = ex.Message;

}

}

}

 

I didn’t have time to comment the code thoroughly but it’s pretty straightforward; nothing more than getting the list of receivers for the list and adding/deleting your receiver class to/from the collection, along with some success/fail messages for the user. It’s worth noting that this probably isn’t the way you would deploy this in production but it is much easier to test and debug if you are new to working with Event Receivers. A Feature Receiver (which essentially uses the same code without all the visual interface elements) deployed as part of the solution package would be more efficient; however, it has the downside of not providing the capability to remove the receiver after it has been applied.

Happy SharePointing!