SharePoint 2010 context menu item with custom code

List items in SharePoint lists and document libraries have a “context menu” on the Filename/FileLeafRef field. When you hover to the right of the field, you see a drop down menu with a bunch of options. You can add your own items to this list, and also run custom code when the item is clicked. This tutorial will show you how, using Visual Studio 2010 and SharePoint 2010.

Edit: 16/02/2012 – there were a few comments about the custom assembly not firing. I’ve revised the post below to include an extra bit in the XML to ensure the control loads (see #13.)

In SharePoint terminology, the context menu on a list item is called the EditControlBlock. We can create a CustomAction to add an item to the EditControlBlock. It can be added to four different types: Content Type, List, ProgID or File Type. In this example, we will attach to a Content Type. More info on the types.

1. In Visual Studio, create a new empty SharePoint project, called SPTest.CustomMenuItem. Use your local SharePoint site to debug, and deploy as a Farm Solution.

2. Right-click your new project, and go Add > New Item. Choose Empty Element, and name it ContextMenuItem.

3. Add the following XML within the Elements section:

<customaction Id="SPTest.CustomMenuItem.ButtonClicked"
                RegistrationType="ContentType"
                RegistrationId="0x0101"
                Location="EditControlBlock"
                ImageUrl="/_layouts/IMAGES/DOCLINK.GIF"
                Sequence="600"
                Title="Click Me!"
                Description="Shows an alert message for this content type."
                >
    <urlaction Url="javascript:alert('Hello World!');"></urlaction>
 </customaction>

4. This is actually all you need to create a custom menu item. This will be attached to documents of Content Type Document and any documents that inherit from document! Neat. To attach to other types, or your custom content types, change the RegistrationId attribute. Hint: to find the ID of a content type, find it in Site Settings > Site Content Types and click to view. The ID is in the URL bar.

5. Right-click your project and Deploy. Open up your SharePoint site, find a document that is using the specified Content Type, and open the context menu. You should see your new button:

6. And when clicked:

Well, Hello World is great and all, but how do you actually make it do something useful, like run some custom code? Let’s see. This will just write out a file with something in it, but obviously your code could do moreorless whatever you want.

7. Back in Visual Studio, add an empty C# (or VB!) Class to your Project. Call it CustomItemAction. Add a reference to System.Web.

8. Update your code to inherit from SPLinkButton and a few other bits:

using System.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace SPTest.CustomMenuItem
{
    public class CustomItemAction : SPLinkButton
    {
                protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (this.Page.Request["__EVENTTARGET"] == "SPTest.CustomMenuItem.ButtonClicked")
            {
                int itemId = Convert.ToInt32(this.Page.Request["__EVENTARGUMENT"]);

                System.IO.TextWriter writer = new StreamWriter(@"C:\custommenuoutput.txt", true);

                writer.WriteLine("Event Fired at:" + DateTime.Now.ToLongTimeString() + ": Item ID:" + itemId.ToString());

               writer.Close();

            }

        }
    }
}

9. Now – Deploy your project again. This won’t actually work yet, but we need to get some info in order to wire up our CustomAction to our custom class.

10. On the machine, navigate to C:Windowsassembly. In the items, find SPTest.CustomMenuItem. View the properties of it, and note/copy the Public Key Token.

11. We can now link our CustomAction to our Class. In Elements.xml file, add the following attributes:

ControlAssembly="SPTest.CustomMenuItem, Version=1.0.0.0, Culture=neutral, PublicKeyToken={PublicKeyToken}"
                ControlClass="SPTest.CustomMenuItem.CustomItemAction" 

Replace {PublicKeyToken} with the token you noted down from the assembly.

12. We also need to update the UrlAction to send a Postback, to trigger the event.

<urlaction Url="javascript:__doPostBack('SPTest.CustomMenuItem.ButtonClicked',{ItemId});"></urlaction>

13. We also need to tell SharePoint to load the control:

<control ControlAssembly="SPTest.CustomMenuItem, Version=1.0.0.0, Culture=neutral, PublicKeyToken={PublicKeyToken}"
                ControlClass="SPTest.CustomMenuItem.CustomItemAction" Sequence="50" Id="AdditionalPageHead"></control>

14. Your finished Elements.xml file should look like this:

< ?xml version="1.0" encoding="utf-8"?>
<elements xmlns="http://schemas.microsoft.com/sharepoint/">
<control ControlAssembly="SPTest.CustomMenuItem, Version=1.0.0.0, Culture=neutral, PublicKeyToken={PublicKeyToken}"
                ControlClass="SPTest.CustomMenuItem.CustomItemAction" Sequence="50" Id="AdditionalPageHead"></control>
<customaction Id="SPTest.CustomMenuItem.ButtonClicked"
                RegistrationType="ContentType"
                RegistrationId="0x0101"
                Location="EditControlBlock"
                ImageUrl="/_layouts/IMAGES/DOCLINK.GIF"
                Sequence="600"
                Title="Click Me!"
                Description="Writes out a file..."
ControlAssembly="SPTest.CustomMenuItem, Version=1.0.0.0, Culture=neutral, PublicKeyToken={PublicKeyToken}"
                ControlClass="SPTest.CustomMenuItem.CustomItemAction" 
                >
<urlaction Url="javascript:__doPostBack('SPTest.CustomMenuItem.ButtonClicked',{ItemId});"></urlaction>
 </customaction>
</elements>

15. One last thing…! Since we have a custom control, we need to tell SharePoint that it’s safe to load by updating the web.config. Navigate to your IIS folder for your web site, (usually C:inetpubwwwrootwssVirtualDirectories80) and open up web.config in notepad, or VS etc.

16. Search for the SafeControls element, and at the bottom of the block, add:

<safecontrol Assembly="SPTest.CustomMenuItem, Version=1.0.0.0, Culture=neutral, PublicKeyToken={PublicKeyToken}" Namespace="SPTest.CustomMenuItem" TypeName="*" Safe="True" SafeAgainstScript="False"></safecontrol>

Again, replace {PublicKeyToken} with your specific token. Save the file.

17. Deploy your SharePoint project.

18. Restart IIS and SharePoint Admin Service.

19. Now when you click your custom button, it should create a file in the root of C: called custommenuoutput.txt with the ID of the item you passed in.

And that’s it! Hope you enjoyed this – any issues, please add a comment.

Some credit…

This post was galvanised from info from here which showed how to add the button, not the custom code, and here, which showed how to use the custom code, but note that you don’t actually need a “dummy” CustomAction – you can handle it just fine from the first one.

58 Comments on SharePoint 2010 context menu item with custom code

  1. are you missing system.web reference ?? do we need to add this reference in order to build the solution ??

  2. After clicking on Click me nothing happens ,(call back is never responded)..??
    any method to debug this

  3. Hi,

    I am unable to add the button in Displaytoolbar but I can add to EditControlBlock.
    Please let me know why I am unable to do.

    Thanks in Advance

  4. After having successfully run this from SharePoint 2010, I’m trying it out on SharePoint 2013 and it deploys fine, the ECB link is added yet the post back is never called in my class; did anybody get this to work for SharePoint 2013?

  5. It’s only a stupid question because the post already covers how to do that:

    10. On the machine, navigate to C:Windowsassembly. In the items, find SPTest.CustomMenuItem. View the properties of it, and note/copy the Public Key Token.

  6. Thanks for the code.

    In my solution i want to place the elements and c# file in a folder named CustomActions. When i do this the code no longer works allthough the namespace doesn’t change. Do you know what I should alter to make it work again?

  7. Thanks, works beautifully.
    I received a 403 error but that was just because of user permissions didn’t allow me to write in the C: (Just logging it in case somebody else runs into the same issue)

  8. I used Matt’s code like this : It is giving an index outside the bounds exception. Can anyone help in this?

    using System.IO;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebControls;
    using Microsoft.Office.DocumentManagement;

    namespace SPTest.CustomMenuItem
    {
    public class CustomItemAction : SPLinkButton
    {
    protected override void OnLoad(EventArgs e)
    {
    base.OnLoad(e);

    if (this.Page.Request[“__EVENTTARGET”] == “SPTest.CustomMenuItem.ButtonClicked”)

    {

    int itemId = Convert.ToInt32(this.Page.Request[“__EVENTARGUMENT”]);

    System.IO.TextWriter writer = new StreamWriter(@”C:\custommenuoutput.txt”, true);

    SPSite mySite = SPContext.Current.Site;
    SPWeb myWeb = mySite.OpenWeb();
    SPList myList = SPContext.Current.List;
    SPListItem myListItem = myList.GetItemById(itemId);
    string docid = Convert.ToString(myListItem[“Document ID”]);
    DocumentIdProvider provider = DocumentId.GetProvider(mySite);
    string str = docid;
    string docidpart = str.Split(‘,’).LastOrDefault();
    string[] result = provider.GetDocumentUrlsById(mySite,docidpart);
    writer.WriteLine(result[0]);
    writer.Close();

    }

    }

    }
    }

  9. HI Matt ,

    How is the item ID being captured from Event Argument field?
    ASking this because I have a requirement to capture document ID on similar lines….

  10. Hey Matt,

    Just trying to adapt your post for use with a SharePoint 2013 project I’m working on. It would appear that the SPLinkButton class is nowhere to be found in the version 15.0.0.0 Microsoft.SharePoint.dll assembly. Yet as of this writing, MSDN still says it should be under the Microsoft.SharePoint.WebControls namespace within the previously mentioned assembly.

    Would you happen to know of any other workable options?

  11. Hi Matt,

    i tried it on a list with 20 items. In the list view i activated the “ID” column and i created a new column “ID controll”.I customized my script so that the itemID from the __EVENTTARGET is written in the “ID controll” column. So if there is a problem like you suspect there should be different IDs in the “ID controll” and “ID” culomn, right? I added and deleted some items… there aren’t different IDs. so can we say that the itemID from the __EVENTTARGET is the “real” ID? What do you think. sry for my bad english 🙂

  12. Hi Philip

    That looks good with a couple of caveats. You should always wrap any SPSite and SPWeb in using statements OR dispose of them, otherwise you will get memory leak issues… eg.

    using(SPSite mySite = new SPSite..)
    using(SPWeb myWeb = mySite.OpenWeb())
    {
    // do stuff
    }

    The other thing you need to be sure of is that the itemID in the _EVENTTARGET is actually the list item ID. I had a feeling it was the INDEX of the item in the list, and therefore it may not be reliable. I can’t be certain though… try it on a list with lots of items and see what happens… !

  13. Hi Matt,

    i tried something and got this code below running. I use your urlAction dopostback, to get the itemID. Don’t know if this is nice and efficient programming / code. But it still works. What do you think?

    if (this.Page.Request[“__EVENTTARGET”] == “SPTest.CustomMenuItem.ButtonClicked”)
    {
    int itemId = Convert.ToInt32(this.Page.Request[“__EVENTARGUMENT”]);

    SPSite mySite = new SPSite(“http://myServer”);
    SPWeb myWeb = mySite.OpenWeb();
    SPList myList = myWeb.Lists[“Docs”];
    SPListItem myListItem = myList.GetItemById(itemId);

    myListItem[“Active”] = “0”;

    myListItem.Update();

    }

    }

  14. Hi Philip. Not tried but should be possible. If you look a few comments up, Mike posted a couple of links – the first 1 has some info on getting the item that you’re working on. With this in hand you should be able to set the values of any of the fields… let me know how you get on!

  15. Hi Matt,

    first, thanks for this post. It helped me a lot to get started with custom actions. I have a question. In your post you write the id in a text file. Is it possible to change the value of a column relative to the item you clicked the custom action? e.g. you have a list with a column [“Active”] when i now hit the “Click Me”-Button he shouldn create a txt file it should write some text in the [“Active”] column. Is that possible? I tried something but it didn’t work.

    Philip

  16. **less than greater than brackets were removed from above post….It should say “First link from Peter Holpar uses CustomAction to load my javascript file…..”

  17. Hi Matt,

    To solve the question I posed 4 days ago, I opted to use a combination of two approaches.

    http://pholpar.wordpress.com/2011/10/16/disabling-item-deletion-at-the-sharepoint-user-interface/

    http://weblogs.asp.net/jan/archive/2009/09/11/customizing-the-sharepoint-ecb-with-javascript-part-3.aspx

    First link from Peter Holpar uses to load my javascript file and ensure Core.js dependency is loaded prior. I moved away from the EditControlBlock approach.

    Second link from Jan Tielens shows which Core.js functions to override, and which utility functions to invoke to add context menus, and sub-menus. Jan’s code, of course, can be updated to use client-side Javascript object model available in SP2010.

    Cheers,
    Mike

  18. Hi Matt,

    Question #1
    You have the CustomAction staticly displaying the text “Click Me!”. What if I wanted to display dynamic text based on properties of the list item?

    Question #2
    From the SPLinkButton’s OnLoad event, how can I access properties of the list item? when this.Page.Request[“__EVENTTARGET”] == “SPTest.CustomMenuItem.ButtonClicked” is true?

    Thanks,
    Mike

  19. Hi Matt,

    I had a colleague that was having a bit of trouble getting the code to fire.
    I tried your solution and it worked just fine.
    Thanks 🙂

  20. Awesome !!! It just worked as is, with no change.
    Thanks Matt for out of the box example.

    As Vincent pointed out, I would also need more information as what row it has been clicked on, so that i can work on that particular document.

  21. Not sure I follow Ed. The namespace should resolve the uniqueness. The code as posted above works for me… but let me know if there is something that needs changing or expanding upon.

  22. Good lord – it’s a naming error.

    Check out the assembly name in the XML and config files: Should be SPTest.CustomMenu – NOT SPTest.CustomMenuItem

    Use unique names for your artifacts, folks (programming 101) – there’s enough confusion just dealing with the Microsoft magic!!!!

  23. Thanks at last. I have been trying to get this to work for 2 days and the extra step 13 made it work for me.

  24. Hi Matt,

    ja thats correct. It passes the ItemId, but than the code works not universal (dynamic). If i know where the ItemId is from, it is no problem. But if i have different lists, i cant use it.

  25. Hi Vincent… I think the code already does this… it passes ItemID through to the postback. I can’t remember exactly what the ItemID relates to though – i thought it was the index of the item in the list.

  26. Hey there,

    do u know if it is possible to save the ID from the list, where the ECB were used, and send it with the javascript postback? Cause i wanna find the Item by ID in the list. If i do that with a foreach, it would take a long time if there are a few 100 items in the list.

    Thank you

  27. Hello again,

    its running now. I changed the output directory to another partition. So it was a permission problem =)

    Thanks for the great HowTo

  28. Hey there,

    i just tried that too…everything works, but not the Output stream. I made a breakpoint in VS, but it seems, that this point was never reached 🙁

    Greetz

  29. My bad it works. (wrong copy paste of my token key) But it doesn’t write the file on my hard drive.
    Could it be my security rules which stop the writer?

  30. I’ve tried to make exactly like the example but i can’t make it work. After adding :

    To the web.config, my sharepoint gives me an error and stop me.
    I’ve just followed the example and tried on a new empty website.

    Any suggestion?

    Thanks

  31. Hi Christo. Thanks for the info. Yes, unfortunately the extra step will do as you suggest which is not great. I did have it working without that so it must be possible but now unsure what it was.

  32. It does work if you add Step 13. Problem it seems is that the control isn’t loaded via the CustomAction ControlAssembly and ControlClass attributes.

    If you add the code from Step 13, you can leave out the ControlAssembly and ControlClass attributes in the CustomAction. It’ll still work. Drawback is that the control will load everywhere now so you have to make sure no other code, except the code you filter using the __EVENTTARGET fires.

  33. Hi everyone. I have tested the solution. I have made a couple of changes. One is you need to add a reference to System.Web in your project. Also, I have added some extra XML that will force the page to load the Control. I have updated it above. Please let me know if this works for you…

  34. Only I have one differences…
    CustomAction Id=”SPTest.CustomMenuItem.ButtonClicked”
    RegistrationType=”FileType”
    RegistrationId=”dtsx”
    Location=”EditControlBlock”
    …..

    All the rest is the same with the exception of {PublicKeyToken}

  35. Hi Matt, Another person that doesn’t work this project. Until Hello world still works but in the custom code, I put a breakpoint in if(this.Page…..) but nothing happens. I don’t know why It doesn’t work…

  36. Hi Fahad, Zoran. It’s been a while but it did work for me. I will run through the tutorial again to double check it.

    But in the meantime have you tried debugging it? Put a breakpoint on the OnLoad method and then use VS to attach to process that’s running the code. It should break. Also check the error logs to see if there’s any messsages relating to your custom assembly. And check in the Assembly folder to make sure it’s in the GAC.

  37. Hello Matt,
    Was there any resolution to what Zoran said?
    Also on checking the event log i am constantly getting an error for “Safe mode did not start successfully. Could not find any recognizable digits.”

    Any ideas, Thanks

  38. Hi Matt,
    it doesn’t work for me either. Till ‘Hello world’ part was ok, but with custom code part, it doesn’t seem that OnLoad method is reached at all. It just does postback and nothing happens, no file created. I tried with simple Response.Redirect in OnLoad method, but nothing…

2 Trackbacks & Pingbacks

  1. Add to SharePoint 2010 Context Menu « SharePointRoot
  2. SharePoint 2010 + Document Sets + Custom Ribbon Buttons with Custom Code « Thornton Technical

Comments are closed.