Skip to main content Home Help (new window) Eric Shupps
Go Search
Home
Blogs
Company
Products
Services
Current News
Idera Acquires Sonar from BinaryWave
BinaryWave Announces SharePoint Performance Optimization Service
Upcoming Events
Home > Blogs > Eric Shupps > Posts > Add a Counter to a SharePoint List Using an Event Receiver
Add a Counter to a SharePoint List Using an Event Receiver

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!

Comments

Do you need to call the base.ItemAdded(properties) method?

Hi!

Great tip. I've coded something very similar myself. But I noticed in your override ItemAdded method you call the base.ItemAdded(properties) method before doing any of your custom code. Is this really necessary? I've written a couple of Event Receivers and I've always deleted the call to the parent, and now I'm wondering if I am actually messing up the list by doing so, although I have not noticed any problems with my approach...
at 4/9/2008 5:36 AM

Not sure about this solution

Consider this: I have list of 100 items, I add a new item and my sequential ID is now 101. I then delete an item, so now again I am back to 100 items, now when I add a new itme I again am adding a sequential ID of 101. Now consider when I delete two more items so now I am back to 99, and then I add another which will now have a sequential ID of 100.

Maybe I am missing the point of this solution?
at 4/13/2008 4:36 AM

Thanks for the Great Post,

I really needed a way to do this.
at 4/20/2008 8:31 PM

Great post, but some corrections required

Hi,
I tried to use this code as it is provided copy/paste and found out that when the ItemAdded method is called, new item is already added to the list! This means logic you using is not correct.
Meaning:
1. if (iCount > 0)     -- has to be --> if (iCount > 1)
2. next line:
list.Items[iLast]["...  -- has to be --> list.Items[iLast-1]["

After these changes all works fine.
Thanks a lot!
at 10/10/2008 7:27 AM

Add Comment

Items on this list require content approval. Your submission will not appear in public views until approved by someone with proper rights. More information on content approval.

Title


Body *


Your Name


Your URL


Comment Date *

To prevent spam from automated bots please provide a valid date in the format "MM/DD/YYYY".
Attachments