SharePoint 2010 Ribbon Customization

SharePoint 2010 Ribbon Customization - controls and commands deployment

 

Introduction

One of the most valuable interface changes provided in SharePoint 2010 is the new Ribbon. This contextual interface allows users to execute any action related to ribbon controls depending upon the context the user is currently dealing with. The SharePoint 2010 API allows developers to extend and customize the ribbon using SharePoint features on site/site collection level. This article shows the way how to do that.

There are several types of controls which can be deployed to SharePoint Ribbon:

  • Button,
  • CheckBox,
  • DropDown,
  • FlyoutAnchor
  • and ToggleButton.

These controls can be collected for usability purposes to containers such as Group and Tab. Therefore it is possible to add controls with custom functionality not only to existing containers, but deploy new tabs and groups, and then and add necessary controls to them.

Any ribbon customization should be mounted within xml in feature declaration. The special xml tag exists for this purpose. Here is the example how it usually looks like:

 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">  
  3.     ...  
  4.     <CustomAction  
  5.       Id="[Your_custom_action_ID]"  
  6.       Location="CommandUI.Ribbon"  
  7.       Title="Custom Action title here">  
  8.         <CommandUIExtension>  
  9.             <CommandUIDefinitions>  
  10.             ...  
  11.             </CommandUIDefinitions>  
  12.             <CommandUIHandlers>  
  13.             ...  
  14.             </CommandUIHandlers>  
  15.         </CommandUIExtension>  
  16.     </CustomAction> 
  17. ...
  18. </Elements> 

 The CustomAction xml tag should have its Id in corresponding attribute – unique text value. The value for Location attribute should always be set to "CommandUI.Ribbon", independently on what kind of ribbon customization it is created for. The CommandUIDefinitions tag should contain CommandUIDefinition child tags declaring all controls or containers which should be created (i.e. buttons, groups, tabs, etc.).
The CommandUIHandlers tag can contain commands declarations for registered controls, so some JavaScript code can be executed. It is not necessary to register all commands in custom action – it can be done on server (webpart/page code-behind). The way how to do it will be described in next posts.
IMPORTANT. If you have deployed some ribbon customization xml and later have to provide some changes into it (change JavaScript for command handler, change FlyoutAnchor control sub-items etc.) it might be necessary to change the Feature ID for SharePoint feature your customization belongs to. For simple modifications (i.e. changing button titles, image URLs etc.) this is not necessary, but if you change the Feature ID every time you change anything in xml, it might save a lot of time and effort.

Adding custom button to existing group

Here is the example of declaring button control to be added to the ribbon which should be added to CommandUIDefinitions xml node:

 
  1. <CommandUIDefinition Location="[Existing_Group_ID].Controls._children">  
  2.   <Button  
  3.     Id="[Your_Button_ID]"  
  4.     Sequence="20"  
  5.     Command="[Your_Command_ID]"  
  6.     LabelText="Custom Button Label"  
  7.     Alt="Custom Button alt text"  
  8.     Image16by16="/_layouts/images/RibbonCustomization/images16x16.png"  
  9.     Image16by16Top="-16"  
  10.     Image16by16Left="-32"  
  11.     Image32by32="/_layouts/images/RibbonCustomization/images32x32.png"  
  12.     Image32by32Top="0"  
  13.     Image32by32Left="-64"  
  14.     TemplateAlias="o1"  
  15.     ToolTipTitle="Custom Button"  
  16.     ToolTipDescription="Executes custom action" />  
  17. </CommandUIDefinition> 

The Location attribute of CommandUIDefinition xml element should be constructed in the following way: [Existing_Group_ID].Controls._children. You can find group’s ID in its declaration xml or using IE Dev Toolbar (the Group node is rendered as <li id="[TabID]">…</li> html tag).
It is possible to add some image to the button. For this purpose you should specify image URLs in Image16by16 and Image32by32 attributes (Image16by16 image appears if group size is too small for 32x32 image). Attributes i.e. Image32by32Top and Image32by32Left are used to set top/left margins for the images.

To perform some action on deployed control the CommandUIHandler xml element should be added to CommandUIHandlers xml node, and its name should be set to the Command attribute of the control (you don’t need to do it in case if you add command handler programmatically). Here is the example:

  1. <CommandUIHandler  
  2.   Command="[Your_Command_ID]"  
  3.   CommandAction="javascript:alert('Button clicked.');"  
  4.   EnabledScript="true" /> 

Any ribbon control should have TemplateAlias attribute. Its value determines the location of the control inside the group (like web part zones in page layout) and depends upon the group’s template the control is being added to. To determine what value should be used here, you should check the group’s Template attribute, than find corresponding GroupTemplate template declaration xml tag (predefined templates can be found in c:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\GLOBAL\XML\CMDUI.xml file) and choose the TemplateAlias attribute value of necessary ControlRef xml sub-node.

Adding custom group to existing tab

Here is the example of adding new group to existing tab:

  1. <CommandUIDefinition Location="[ExistingTabID].Scaling._children">  
  2.   <MaxSize   
  3.     Id="[Your_Group_ID].MaxSize"  
  4.     GroupId="[Your_Group_ID]"  
  5.     Size="LargeLarge"   
  6.     Sequence="10" />  
  7. </CommandUIDefinition>  
  8. <CommandUIDefinition Location="[ExistingTabID].Scaling._children">  
  9.     <Scale  
  10.     Id="[Your_Group_ID].Popup"  
  11.     GroupId="[Your_Group_ID]"  
  12.     Size="Popup"  
  13.   Sequence="20" />  
  14. </CommandUIDefinition>  
  15. <CommandUIDefinition Location="[ExistingTabID].Groups._children">  
  16.   <Group  
  17.     Id="[Your_Group_ID]"  
  18.     Sequence="1"  
  19.     Title="Custom Group"  
  20.     Template="Ribbon.Templates.Flexible2"  
  21.     Image32by32Popup="/_layouts/images/RibbonCustomization/images32x32.png"  
  22.     Image32by32PopupTop="-128"  
  23.     Image32by32PopupLeft="-192">  
  24.       <Controls Id="Ribbon.WebPartPage.CustomGroup.Controls">  
  25.           ...  
  26.       </Controls>  
  27.   </Group>  
  28. </CommandUIDefinition> 

In this sample you should pay attention to the following:
  1. Three different CommandUIDefinition xml nodes should be created: for deploying MaxSize, Scale and Group nodes; MaxSize and Scale nodes should reference to the group which is currently being declared (using GroupId attribute); the Size attributes should be based on the group template. If no MaxSize and Scale declarations for new group is created, on incorrect values of Size attributes are specified, the group won’t be visible or no controls should be shown in it;
  2. The Location attributes of CommandUIDefinition xml elements should be constructed in the following way: [ExistingTabID].Scaling._children – for MaxSize and Scale nodes; [ExistingTabID].Groups._children – for Group node;
  3. All controls for this group can be declared directly in the Controls node inside the group declaration xml.

Adding custom tab to ribbon

Here is the example of declaring custom ribbon tab:

  1. <CommandUIDefinition Location="Ribbon.Tabs._children">  
  2.   <Tab  
  3.     Description="Custom Tab"  
  4.     Id="[Your_Tab_ID]"  
  5.     Sequence="1"  
  6.     Title="Custom Tab">  
  7.       <Scaling Id="[Your_Tab_ID].Scaling">  
  8.       ...  
  9.       </Scaling>  
  10.       <Groups Id="[Your_Tab_ID].Groups">  
  11.       ...  
  12.     </Groups>  
  13.   </Tab>  
  14. </CommandUIDefinition> 

In this sample you should pay attention to the following:
  1. The Location attribute of CommandUIDefinition xml element should be exactly the following: Ribbon.Tabs._children;
  2. All inner groups and scaling xml nodes for them can be declared directly in Scaling and Groups xml nodes inside the tab declaration node respectively.
    You should make your tab visible in the page you are working with. This should be done programmatically in either page of Web Part classes. Here is the code sample with such functionality:

  1. rotected override void OnLoad(EventArgs e)  
  2. {  
  3.     base.OnLoad(e);  
  4.   
  5.     var ribbon = SPRibbon.GetCurrent(Page);  
  6.     if (ribbon != null)  
  7.     {  
  8.         ribbon.Minimized = false;  
  9.         ribbon.CommandUIVisible = true;  
  10.         const string initialTabId = "RibbonCustomization.CustomTab";  
  11.         if(!ribbon.IsTabAvailable(initialTabId))  
  12.             ribbon.MakeTabAvailable(initialTabId);  
  13.         ribbon.InitialTabId = initialTabId;  
  14.     }  

Using this approach it is possible to deploy all necessary controls (buttons, checkboxes, dropdowns etc.), control containers (groups and tabs) and JavaScript command handlers. But sometimes it is necessary to generate a JavaScript command handler code dynamically or to handle ribbon actions on the server side. For this purpose the SharePoint ribbon allows user to create command handlers programmatically.

Here is the example:

  1. var commands = new List<IRibbonCommand>();  
  2. commands.Add(new SPRibbonCommand(  
  3.                 "Your_Command_ID",  
  4.                 "alert('this is server generated command')")); 

When you have created a command, you should register it with help of SPRibbonScriptManager class. It has protected method RegisterInitializeFunction, so it is necessary to use reflection to invoke it:

  1. var manager = new SPRibbonScriptManager();  
  2. var methodInfo = typeof(SPRibbonScriptManager).GetMethod(  
  3.     "RegisterInitializeFunction",  
  4.     BindingFlags.Instance | BindingFlags.NonPublic);  
  5. methodInfo.Invoke(  
  6.     manager,  
  7.     new object[]  
  8.     {  
  9.         Page,  
  10.         "InitPageComponent",  
  11.         "/_layouts/INC/RibbonCustomization/PageComponent.js",  
  12.         false,  
  13.         "RibbonCustomization.PageComponent.initialize()"  
  14.     }); 

This method is used to register a JavaScript class for managing server-created command handlers. It should implement the appropriate interface for registering ribbon commands. Here how it should look like:

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

The last thing you should do to enable server-registered commands on your page is registering three client functions:

  1. manager.RegisterGetCommandsFunction(Page, "getGlobalCommands", commands);  
  2. manager.RegisterCommandEnabledFunction(Page, "commandEnabled", commands);  
  3. manager.RegisterHandleCommandFunction(Page, "handleCommand", commands); 

If you are creating your own page, it might be necessary to link SP.Runtime.js and SP.js
JavaScript files. You can do this in the following way:

  1. ScriptLink.RegisterScriptAfterUI(Page, "SP.Runtime.js"falsetrue);  
  2. ScriptLink.RegisterScriptAfterUI(Page, "SP.js"falsetrue); 

This is enough to enable the handling of your server-registered commands for the ribbon controls.

Registering server-side ribbon command handlers

In order to handle ribbon command on the server you should create and register an instance of SPRibbonPostBackCommand command class. Here is the example:

  1. commands.Add(new SPRibbonPostBackCommand(  
  2.                 "ServerCommandID",  
  3.                 this,  
  4.                 "ServerCmd",  
  5.                 null)); 

In order to use the server command handling from the web part it has to implement IPostBackEventHandler interface. Then you can perform all necessary server actions in RaisePostBackEvent method. You can determine which command was run by eventArgument method argument.