Ribbon buttons with postback in SP2010

For this Ticket Desk project I’m doing, I wanted to extend the default Ribbon UI with a new button that quickly allows the application admins to quickly mark a ticket as resolved. This is just a status field on the list item itself and you could even argue to just edit the item. However, since there’s a workflow involved and I want to hide the status field on the new and edit forms from ticket submitters, I’m providing the functionality via a ribbon button.

Now, in SharePoint 2007, I was used to doing something similar, but then I’d be using a custom action with a ControlAssembly and ControlClass specified. The class specified would just inherit from WebControl and I’d implement the IPostBackEventHandler interface and be on my way. I tried something similar in SP2010, however I got no dice. So, apparently things are a bit different in SP2010, let’s find out what exactly.

First, let me outline what we need to get this to work:

  • CustomAction element that defines the ribbon button
  • User control which will load the required JavaScript and handle the postback
  • JavaScript file containing the page component

Right, I’m going to assume you know how to set-up an SP2010 project in Visual Studio 2010 and how to add the different types of items. First, create a new Empty Element which will contain the following XML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="TicketResolvedRibbonCustomAction"
                RegistrationId="28582"
                RegistrationType="List"
                Location="CommandUI.Ribbon"
                Rights="ManageLists"
                Sequence="25"
                Title="Mark as Resolved">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition Location="Ribbon.ListItem.Actions.Controls._children">
          <Button Id="TicketResolvedRibbonButton"
                  Alt="Mark this ticket as resolved."
                  Description="Mark this ticket as resolved."
                  Sequence="25"
                  Command="ResolveTicketCommand"
                  Image32by32="/_layouts/images/TicketDesk/ticketresolved.png"
                  LabelText="Mark as Resolved"                  
                  TemplateAlias="o1" />
        </CommandUIDefinition>
      </CommandUIDefinitions>
    </CommandUIExtension>
  </CustomAction>
  <Control Id="AdditionalPageHead" ControlSrc="~/_controltemplates/TicketResolvedRibbonDelegate.ascx" Sequence="25"/>
</Elements>

Basically, this XML element defines that we have a custom action, which is bound to a List with template ID “28582″. This is a custom list definition that’s deployed with my solution, but you can also bind this to a content type for example. Furthermore, the location is on the ribbon and the rights required here are ManageLists. The CommandUIDefinition element specifies that the location of the button will be the list item tab, in the Actions group. There is a command specified here as well, which we will later need in our code-behind of the user control.

Note that I’m not specifying any CommandUIHandlers here. This is not necessary since we’re handling the event on the server-side here, instead of client-side. So, instead of the handler, we define a user control that will be loaded in the AdditionalPageHead section of the masterpage. The location is linked to the default SharePoint CONTROLTEMPLATES folder, for which you can create a mapping in Visual Studio.

Next, we need the page component JavaScript file. Create a SharePoint mapped folder in Visual Studio which links to LAYOUTS, if you haven’t done that so far. In this folder, create a new JavaScript file called “PageComponent.js”. The contents of the file is as following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
function ULS_SP()
{
    if (ULS_SP.caller)
    {
        ULS_SP.caller.ULSTeamName = "Windows SharePoint Services 4";
        ULS_SP.caller.ULSFileName = "/_layouts/TicketDesk/PageComponent.js";
    }
}
 
Type.registerNamespace('RibbonCustomization');
 
// RibbonApp Page Component
RibbonCustomization.PageComponent = function ()
{
    ULS_SP();
    RibbonCustomization.PageComponent.initializeBase(this);
}
 
RibbonCustomization.PageComponent.initialize = function ()
{
    ULS_SP();
    ExecuteOrDelayUntilScriptLoaded(Function.createDelegate(null, RibbonCustomization.PageComponent.initializePageComponent), 'SP.Ribbon.js');
}
 
RibbonCustomization.PageComponent.initializePageComponent = function ()
{
    ULS_SP();
 
    var ribbonPageManager = SP.Ribbon.PageManager.get_instance();
 
    if (null !== ribbonPageManager)
    {
        ribbonPageManager.addPageComponent(RibbonCustomization.PageComponent.instance);
        ribbonPageManager.get_focusManager().requestFocusForComponent(RibbonCustomization.PageComponent.instance);
    }
}
 
RibbonCustomization.PageComponent.refreshRibbonStatus = function ()
{
    SP.Ribbon.PageManager.get_instance().get_commandDispatcher().executeCommand(Commands.CommandIds.ApplicationStateChanged, null);
}
 
RibbonCustomization.PageComponent.prototype = 
{
    getFocusedCommands: function ()
    {
        ULS_SP();
        return [];
    },
 
    getGlobalCommands: function ()
    {
        ULS_SP();
        return getGlobalCommands();
    },
 
    isFocusable: function ()
    {
        ULS_SP();
        return true;
    },
 
    receiveFocus: function ()
    {
        ULS_SP();
        return true;
    },
 
    yieldFocus: function ()
    {
        ULS_SP();
        return true;
    },
 
    canHandleCommand: function (commandId)
    {
        ULS_SP();
        return commandEnabled(commandId);
    },
 
    handleCommand: function (commandId, properties, sequence)
    {
        ULS_SP();
        return handleCommand(commandId, properties, sequence);
    }
}
 
// Register classes
RibbonCustomization.PageComponent.registerClass('RibbonCustomization.PageComponent', CUI.Page.PageComponent);
RibbonCustomization.PageComponent.instance = new RibbonCustomization.PageComponent();
 
// Notify waiting jobs
NotifyScriptLoadedAndExecuteWaitingJobs("/_layouts/TicketDesk/PageComponent.js");

Note that I’m referring to the “/_layouts/TicketDesk” location, since that’s the name of my project. You may need to change the location here to match your situation. From what I’ve gathered so far, this is object-oriented JavaScript that actually powers the ribbon button and can be considered as a template required for ribbon buttons. You can create your own implementations for the methods defined in the prototype, but in this case it’s not needed, so we’re going to leave the file as it is.

Now, on to the user control. In Visual Studio, create a mapping to the SharePoint CONTROLTEMPLATES folder. Add a new user control item in this folder. The ASCX file itself doesn’t require any contents, so we’re gonna go ahead and move to the source file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
namespace TicketDesk.CONTROLTEMPLATES
{
    public partial class TicketResolvedRibbonDelegate : UserControl, IPostBackEventHandler
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            var commands = new List<IRibbonCommand>
                           {
                               // The command name here matches the command name of the ribbon button
                               new SPRibbonPostBackCommand("ResolveTicketCommand", this,
                                                           "TicketResolvedRibbonButtonEvent",
                                                           null, "true")
                           };
 
            // Register ribbon scripts
            var spRibbonScriptManager = new SPRibbonScriptManager();
 
            spRibbonScriptManager.RegisterGetCommandsFunction(Page, "getGlobalCommands", commands);
            spRibbonScriptManager.RegisterCommandEnabledFunction(Page, "canHandleCommand", commands);
            spRibbonScriptManager.RegisterHandleCommandFunction(Page, "handleCommand", commands);
 
            InitRibbonScriptManager(commands);
        }
 
        private void InitRibbonScriptManager(IEnumerable<IRibbonCommand> commands)
        {
            // Register scripts
            ScriptLink.RegisterScriptAfterUI(Page, "SP.Runtime.js", false, true);
            ScriptLink.RegisterScriptAfterUI(Page, "SP.js", false, true);
 
            // Register initialize function
            var manager = new SPRibbonScriptManager();
            var methodInfo = typeof(SPRibbonScriptManager).GetMethod("RegisterInitializeFunction", BindingFlags.Instance | BindingFlags.NonPublic);
 
            methodInfo.Invoke(manager,
                new object[]
                    {
                        Page,
                        "InitPageComponent",
                        "/_layouts/TicketDesk/PageComponent.js",
                        false,
                        "RibbonCustomization.PageComponent.initialize()"
                    });
 
            // Register ribbon scripts
            manager.RegisterGetCommandsFunction(Page, "getGlobalCommands", commands);
            manager.RegisterCommandEnabledFunction(Page, "commandEnabled", commands);
            manager.RegisterHandleCommandFunction(Page, "handleCommand", commands);
        }
 
        public void RaisePostBackEvent(string eventArgument)
        {
            Page.Response.Write(eventArgument);
        }
    }
}

Upon loading of the user control, we’re going to add a new ribbon command, of type SPRibbonPostBackCommand. The command name here matches the command name of the button specified in the XML element earlier. We’re also specifying an event ID in the constructor, so when the postback occurs, we can make sure we’re handling the proper event. You could also specify event arguments in the constructor here.

Next, we need to instantiate a new SPRibbonScriptManager object. We’re using that to register the JavaScript methods that we’ve defined in the previous step. Note that the function names here refer to methods in the JS prototype.

The page component file now needs to be registered to SharePoint. For a reason completely beyond me, the method required to do so, is not publically accessible through the API, so we’ll need reflection to that.

For sake of demonstration purposes, the RaisePostBackEvent method isn’t doing a lot here other than writing the event arguments to the page. Deploy your project and go to the list where your ribbon button should be visible and click it. You should now see that a postback is taking place and the event arguments should be written on top of the page.

The only thing I haven’t really figured out yet is how to actually pass the ID of the item you’ve selected, hopefully I’ll be able to write something about that in a follow-up post.

Be Sociable, Share!

5 thoughts on “Ribbon buttons with postback in SP2010

  1. I am getting tab/group/control rendered but all buttons are disabled. Any idea?

    I noticed that during Page_Load event, you have registered PageComponent script function with SPRibbonScriptManager twice with the exception for RegisterCommandEnabledFunction one with “commandEnabled” and one with “canHandleCommand”.

  2. Pingback: SUPERNOVA | MetroStar Systems' Blog

  3. Pingback: Creating a Page Layout using SharePoint Designer 2010 | red1'space

  4. Hi,

    new SPRibbonPostBackCommand(“ResolveTicketCommand”, this,
    “TicketResolvedRibbonButtonEvent”,
    null, “true”)

    TicketResolvedRibbonButtonEvent what this?

    it’s method or other

    thanks!

  5. HI,

    thanks , TicketResolvedRibbonButtonEvent is ID.

    public void RaisePostBackEvent(string eventArgument)
    {
    Page.Response.Write(eventArgument);
    }
    output:{“id”:”TicketResolvedRibbonButtonEvent”}

    thanks

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">