Microsoft SharePoint Developer Documentation Team Blog
The Official Blog of the SharePoint Developer Documentation Team

How to Create a Web Part with a Contextual Tab

How to Create a Web Part with a Contextual Tab

  • Comments 13

This post will cover how to create a Web Part that opens a contextual tab on the Server ribbon in Microsoft SharePoint Foundation 2010. You will be working with Server ribbon XML, EcmaScript (JavaScript, JScript), and C# code. This solution will be a farm solution and will be built using Visual Studio 2010 Beta 2. If you have any questions, please feel free to leave a comment.

 The following will appear in a future version of the SDK. It is preliminary documentation and subject to change.

First, you will create a new project. Click on File, New, and then Project.

1. Under Visual C#, choose Empty SharePoint Project and enter ContextualTabWebPart for the Name. Click OK.

2.       In the SharePoint Customization Wizard, choose Deploy as a farm solution. Click Finish.

Now, you will implement the Web Part.

3.  In the Solution Explorer, right click References and choose Add Reference.

4.  On the Add Reference dialog, choose the .NET Tab. Select Microsoft.Web.CommandUI.dll. Click OK.

5.  Right click on the ContextualTabWebPart project in the Solution Explorer and choose Add, New Item...

6.   In the Add New Item dialog, choose Web Part. Enter ContextualTabWebPart as the name.

7.  When the Web Part has been added and ContextualTabWebPart.cs file is shown, add the following using statements.

using System.Xml;

using Microsoft.Web.CommandUI;

 

8.  Now, you need to implement the IWebPartPageComponentProvider interface as follows. You will work with this later.

public class ContextualTabWebPart : WebPart, IWebPartPageComponentProvider

 

9.  Next, you will create two global string variables for the ribbon XML. These two variables define the contextual tab and the group template. For a more in-depth discussion on defining a tab and group template, see How to Add a Tab to the Server Ribbon in SharePoint Foundation. In the contextualTab string, you will notice a ContextualGroup element. This element defines the following Tab element as a contextual tab. The Color attribute defines the color of the tab when it renders. The Id and ContextualGroupId are simply unique identifiers for the group. The Sequence attribute defines where the contextual tab will render. The following code implements the two global string variables.

        private string contextualTab = @"

   <ContextualGroup Color=""Magenta""

     Command=""CustomContextualTab.EnableContextualGroup""

     Id=""Ribbon.CustomContextualTabGroup""

     Title=""Custom Contextual Tab Group""

     Sequence=""502""

     ContextualGroupId=""CustomContextualTabGroup"">

          <Tab

              Id=""Ribbon.CustomTabExample""

              Title=""My Custom Tab""

              Description=""This holds my custom commands!""

              Command=""CustomContextualTab.EnableCustomTab""

              Sequence=""501"">

            <Scaling

              Id=""Ribbon.CustomTabExample.Scaling"">

              <MaxSize

                Id=""Ribbon.CustomTabExample.MaxSize""

                GroupId=""Ribbon.CustomTabExample.CustomGroupExample""

                Size=""OneLargeTwoMedium""/>

              <Scale

                Id=""Ribbon.CustomTabExample.Scaling.CustomTabScaling""

                GroupId=""Ribbon.CustomTabExample.CustomGroupExample""

                Size=""OneLargeTwoMedium"" />

            </Scaling>

            <Groups Id=""Ribbon.CustomTabExample.Groups"">

              <Group

                Id=""Ribbon.CustomTabExample.CustomGroupExample""

                Description=""This is a custom group!""

                Title=""Custom Group""

                Command=""CustomContextualTab.EnableCustomGroup""

                Sequence=""52""

                Template=""Ribbon.Templates.CustomTemplateExample"">

                <Controls

                  Id=""Ribbon.CustomTabExample.CustomGroupExample.Controls"">

                  <Button

                    Id=""Ribbon.CustomTabExample.CustomGroupExample.HelloWorld""

                    Command=""CustomContextualTab.HelloWorldCommand""

                    Sequence=""15""

                    Description=""Says hello to the World!""

                    LabelText=""Hello, World!""

                    TemplateAlias=""cust1""/>

                  <Button

                    Id=""Ribbon.CustomTabExample.CustomGroupExample.GoodbyeWorld""

                    Command=""CustomContextualTab.GoodbyeWorldCommand""

                    Sequence=""17""

                    Description=""Says good-bye to the World!""

                    LabelText=""Good-bye, World!""

                    TemplateAlias=""cust2""/>

                </Controls>

              </Group>

            </Groups>

          </Tab>

   </ContextualGroup>";

 

        private string contextualTabTemplate = @"

          <GroupTemplate Id=""Ribbon.Templates.CustomTemplateExample"">

            <Layout

              Title=""OneLargeTwoMedium"" LayoutTitle=""OneLargeTwoMedium"">

              <Section Alignment=""Top"" Type=""OneRow"">

                <Row>

                  <ControlRef DisplayMode=""Large"" TemplateAlias=""cust1"" />

                </Row>

              </Section>

              <Section Alignment=""Top"" Type=""TwoRow"">

                <Row>

                  <ControlRef DisplayMode=""Medium"" TemplateAlias=""cust2"" />

                </Row>

                <Row>

                  <ControlRef DisplayMode=""Medium"" TemplateAlias=""cust3"" />

                </Row>

              </Section>

            </Layout>

          </GroupTemplate>";

 

10.  Create a new string property called DelayScript. DelayScript contains EcmaScript (JavaScript, JScript) that adds and registers your custom page component. We will create the custom page component later in this topic. The _addCustomPageComponent method creates your custom page component and adds it to the page manager. Every Web Part has a page component, and you will use the Web Part's page component id when you create a custom page component object. This is necessary to associate the Web Part with the page component to enable contextually switching to your custom ribbon tab. The _registerCustomPageComponent method registers your EcmaScript file when the page loads. The following code implements DelayScript .

        public string DelayScript

        {

            get

            {

                string webPartPageComponentId = SPRibbon.GetWebPartPageComponentId(this);

                return @"

<script type=""text/javascript"">

//<![CDATA[

 

            function _addCustomPageComponent()

            {

                var _customPageComponent = new ContextualTabWebPart.CustomPageComponent('" + webPartPageComponentId + @"');

                SP.Ribbon.PageManager.get_instance().addPageComponent(_customPageComponent);

            }

 

            function _registerCustomPageComponent()

            {

                SP.SOD.registerSod(""CustomContextualTabPageComponent.js"", ""\/_layouts\/CustomContextualTabPageComponent.js"");

                SP.SOD.executeFunc(""CustomContextualTabPageComponent.js"", ""ContextualWebPart.CustomPageComponent"", _addCustomPageComponent);

            }

            SP.SOD.executeOrDelayUntilScriptLoaded(_registerCustomPageComponent, ""sp.ribbon.js"");

//]]>

</script>";

            }

        }

11.  Create a new function called AddContextualTab. Inside the AddContextualTab method, you will insert code to register the contextual tab and custom templates with the ribbon. The Microsoft.Web.CommandUI.Ribbon.RegisterDataExtension method will be used to register the ribbon extensions. RegisterDataExtension tells the ribbon where to load the XML passed into it. We will use the ids of the ContextualTabs and Templates elements in CMDUI.xml. The following code implements the AddContextualTab method.

        private void AddContextualTab()

        {

 

            //Gets the current instance of the ribbon on the page.

            Microsoft.Web.CommandUI.Ribbon ribbon = SPRibbon.GetCurrent(this.Page);

 

            //Prepares an XmlDocument object used to load the ribbon extensions.

            XmlDocument ribbonExtensions = new XmlDocument();

 

            //Load the contextual tab XML and register the ribbon extension.

            ribbonExtensions.LoadXml(this.contextualTab);

            ribbon.RegisterDataExtension(ribbonExtensions.FirstChild, "Ribbon.ContextualTabs._children");

 

            //Load the custom templates and register the ribbon extension.

            ribbonExtensions.LoadXml(this.contextualTabTemplate);

            ribbon.RegisterDataExtension(ribbonExtensions.FirstChild, "Ribbon.Templates._children");

        }

 

12.  Now, you need to implement the WebPartContextualInfo property of the IWebPartPageComponentProvider interface. You can implement the interface by right clicking on IWebPartPageComponentProvider and choosing Implement Interface, Implement Interface Explicitly.

 

This interface lets the ribbon know when a Web Part has been selected and which contextual group and tab to show. When you add the contextual group, you send it the id of the contextual group, the VisibilityContext, and the Command. The id maps to the Id property of the ContextualGroup element in the ribbon XML. The VisibilityContext is used to group controls in order to show or hide them. The Command parameter maps to the Command of the ContextualGroup element in the ribbon XML. You will also add the tab that was defined in the ribbon XML. You only need to pass in the Id and the VisibilityContext parameters for the tab.

Insert the following code for the WebPartContextualInfo.

        public WebPartContextualInfo WebPartContextualInfo

        {

            get

            {

                WebPartContextualInfo info = new WebPartContextualInfo();

                WebPartRibbonContextualGroup contextualGroup = new WebPartRibbonContextualGroup();

                WebPartRibbonTab ribbonTab = new WebPartRibbonTab();

 

                //Create the contextual group object and initialize its values.

                contextualGroup.Id = "Ribbon.CustomContextualTabGroup";

                contextualGroup.Command = "CustomContextualTab.EnableContextualGroup";

                contextualGroup.VisibilityContext = "CustomContextualTab.CustomVisibilityContext";

 

                //Create the tab object and initialize its values.

                ribbonTab.Id = "Ribbon.CustomTabExample";

                ribbonTab.VisibilityContext = "CustomContextualTab.CustomVisibilityContext";

 

                //Add the contextual group and tab to the WebPartContextualInfo.

                info.ContextualGroups.Add(contextualGroup);

                info.Tabs.Add(ribbonTab);

                info.PageComponentId = SPRibbon.GetWebPartPageComponentId(this);

 

                return info;

            }

        }

 

 

13.  Next, you will implement the OnPreRender method. This allows us to add elements to the ribbon before it is rendered on the page. Inside OnPreRender you will call the AddContextualTab method and register the DelayScript with the ClientScriptManager. The following code implements OnPreRender.

        protected override void OnPreRender(EventArgs e)

        {

            base.OnPreRender(e);

 

            this.AddContextualTab();

 

            ClientScriptManager clientScript = this.Page.ClientScript;

            clientScript.RegisterClientScriptBlock(this.GetType(), "ContextualTabWebPart", this.DelayScript);

 

        }

 

The Web Part is complete. Next, you will create the page component. A page component is an EcmaScript (JavaScript, JScript) object used to interact with the Server ribbon. It enables commands on the ribbon and responds to the commands you define in the ribbon XML. The page component has required functions in order for it to operate properly. The following table lists the required functions and their purpose.

Function

Purpose

init

Initializes the page component.

getFocusedCommands

Returns a list of the focused commands. Your page component is called for the commands only if it has the focus

getGlobalCommands

Returns a list of the global commands. Your page component is called for commands regardless of focus.

isFocusable

Specifies if the page component can receive focus.

canHandleCommand

Defines if the page component can handle a command sent to it.

handleCommand

Handles the commands sent to the page component.

getId

Returns the id of the page component. This is used to associate a control with a page component.

 

There are also two optional functions. These functions will not be defined in this example.

Function

Purpose

yieldFocus

Specifies the code that is run when the page component loses focus.

receiveFocus

Specifies the code that is run when the page component receives focus.

 

In order for the page component to operate properly, you will have to deploy the file using a Module.

14.  In the Solution Explorer, right click on the ContextualTabWebPart project and select Add, New Item.

15.  In the Add New Item dialog, choose the Module template and type CustomContextualTabPageComponent as the Name. Click Add.

16.  You will not need the files that are added by default. Select Elements.xml and Sample.txt in the Solution Explorer and delete them.

17.  Now, you need to add the EcmaScript (JavaScript, JScript) file. Right click on CustomContextualTabPageComponent and select Add, New Item.

18.  Under Visual C#, choose Web in the Installed Templates list. Select the Jscript File type and enter CustomContextualTabPageComponent as the name.

19.  You will need to set the deployment location to make sure the file goes into the _layouts directory. To do that, select CustomContextualTabPageComponent.js in the Solution Explorer. In the Properties window, set the Deployment Type to Root File. Open the Deployment Location property and set the Path to Template\Layouts.

The following code will go into the CustomContextualTabPageComponent.js file to implement your custom page component.

Type.registerNamespace('ContextualTabWebPart');

 

var _webPartPageComponentId;

ContextualTabWebPart.CustomPageComponent = function ContextualTabWebPart_CustomPageComponent(webPartPcId) {

    this._webPartPageComponentId = webPartPcId;

    ContextualTabWebPart.CustomPageComponent.initializeBase(this);

}

ContextualTabWebPart.CustomPageComponent.prototype = {

 

    init: function ContextualTabWebPart_CustomPageComponent$init() {  },

 

    getFocusedCommands: function ContextualTabWebPart_CustomPageComponent$getFocusedCommands() {

        return ['CustomContextualTab.EnableCustomTab', 'CustomContextualTab.EnableCustomGroup', 'CustomContextualTab.HelloWorldCommand', 'CustomContextualTab.GoodbyeWorldCommand'];

    },

 

    getGlobalCommands: function ContextualTabWebPart_CustomPageComponent$getGlobalCommands() {

        return [];

    },

 

    isFocusable: function ContextualTabWebPart_CustomPageComponent$isFocusable() {

        return true;

    },

   

    canHandleCommand: function ContextualTabWebPart_CustomPageComponent$canHandleCommand(commandId) {

        //Contextual Tab commands

        if ((commandId === 'CustomContextualTab.EnableCustomTab') || (commandId === 'CustomContextualTab.EnableCustomGroup') || (commandId === 'CustomContextualTab.HelloWorldCommand') || (commandId === 'CustomContextualTab.GoodbyeWorldCommand')) {

            return true;

        }

    },

 

    handleCommand: function ContextualTabWebPart_CustomPageComponent$handleCommand(commandId, properties, sequence) {

 

        if (commandId === 'CustomContextualTab.HelloWorldCommand') {

            alert('Hello, world!');

        }

        if (commandId === 'CustomContextualTab.GoodbyeWorldCommand') {

            alert('Good-bye, world!');

        }

    },

 

    getId: function ContextualTabWebPart_CustomPageComponent$getId() {

        return this._webPartPageComponentId;

    }

}

 

 

ContextualTabWebPart.CustomPageComponent.registerClass('ContextualTabWebPart.CustomPageComponent', CUI.Page.PageComponent);

SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("CustomContextualTabPageComponent.js");

 

Notice that getFocusedCommands returns the Command properties that you defined in the ribbon XML above. In canHandleCommand you return true to signify that your page component can respond to the specified commands. When creating this object, you use the webPartPcId parameter to set the custom page component id. This is set to the same value as the Web Part’s page component id to associate the Web Part and page component through the getId method.

20.  To test the code, press F5 to deploy the solution.

21.  Open your site and put the page into Edit mode.

22.  In the Insert tab, click the Web Part button.

23.  In the Web Part Adder, select the Custom category.

24.  In the list of Web Parts, select ContextualTabWebPart. Click Add.

25.  Once the Web Part is added to the page, click the ContextualTabWebPart.

26.  Notice the Custom Contextual Tab Group tab that shows up on selection.

Leave a Comment
  • Please add 1 and 8 and type the answer here:
  • Post
  • Very nice article!.

    I am trying to achieve somewhat similar but I am not creating my custom web part to show or activate custom tabs on the Ribbon. Instead I am trying to show OOB contextual group. I am facing an issue where I have customized the Doc Set homepage where I have removed the Document Set Contents web part and Added XLV web part because I need to establish web part connections. Adding this on the page breaks the ribbon in the way it doesn’t show OOB Library Tools Contextual Group when the page gets loaded or when XLV gets focus.

    Is there any way we can show Library Tools contextual group so that I can have the same functionality provided OOB?  

    Thanks!!

    Amit

  • If you try this sample and your buttons are disabled. Try debugging and you will find that canHandleCommand and handleCommand is never hit.

    After 5+ hours of debugging obfuscated JavaScript of Sharepoint and subsequently cursing the sample writer for missing the magical line of code (line 3).

    The solution is below.

    var ribbonPageManager = SP.Ribbon.PageManager.get_instance();  

                   ribbonPageManager.addPageComponent(_customPageComponent);

                   ribbonPageManager.get_focusManager().requestFocusForComponent(_customPageComponent);

  • For This Code mentioned in the previous comment:

    var ribbonPageManager = SP.Ribbon.PageManager.get_instance();  

                  ribbonPageManager.addPageComponent(_customPageComponent);

                  ribbonPageManager.get_focusManager().requestFocusForComponent(_customPageComponent);

    would this be placed in the CustomContextualTabPageComponent.js or the ContextualTabWebPart.cs file and in what part of the particular file would this code be placed. Thanks.

  • @anthonya

    ContextualTabWebPart.cs file

    as part of Delay Script (hard coded string)

  • @nikhilzkingdom

    There is a product bug where the buttons aren't enabled the first time you select the Web Part. Try selecting the Web Part, de-selecting the Web Part, and selecting it again. The buttons should enable at that point. This sample works without the additional call to requestFocusForComponent, which, actually, could cause timing issues. Feel free to e-mail me if you'd like to debug your solution without the call to requestFocusForComponent.

    @anthonya

    Try selecting the Web Part, de-selecting, and selecting the Web Part again. This sample should work fine without the additional line of code. If not, e-mail me and I'll help you out.

    Thanks,

    Dallas

  • @Dallas Tester

    So is the product bug fixed at the next release?

    Also in the sample above, if you try and edit the web part (causing a postback) it will blow up with

    "ArgumentException: Ribbon 'Tab' node must have a 'Scaling' subnode" exception

  • What do I need to do to support the webpart being on a page more than once? I have some javascript code and information contained within the webpart that I would like to access from the ribbon.

  • If I put two of the web parts on the page, the ContextualTabWebPart seems to be crashing the page. I am curious if I give ContextualGroup and ContextualTab unique ID for each ContextualWebPart, it will display two contextual tab on Ribbon, how does the Ribbon work when I select one web part vs. the other?

  • Sean,

    This is a known issue with this example. There is a long discussion about it (along with a fix from me) on the forums: social.msdn.microsoft.com/.../8604fda5-dad3-49e5-b820-af98ae2fb3cf

    If you have any further questions, feel free to ask!

    Thanks,

    Dallas

  • Thanks for the article. Is there any reason why I wouldn't be able to translate this to work with a visual web part?

    Thanks

  • Can we create other Ribbons by the same technique? Without having the Feature xml and using the OM only??

  • Hello,

    I tested this code and the buttons do not work. I've read the comments and saw that nikhilzkingdom provides a solution.

    But in what function in Delay Script code  i place the code??

    Thanks,

    zula

  • For anyone trying to get this working with SharePoint 2013, I had to change:

    SP.SOD.registerSod(""CustomContextualTabPageComponent.js"", ""\/_layouts\/CustomContextualTabPageComponent.js"");

    To:

    SP.SOD.registerSod(""CustomContextualTabPageComponent.js"", ""\/_layouts\/15\/CustomContextualTabPageComponent.js"");

    In DelayScript (step 10) to get it to load the .js file that gets deployed into layouts.

Page 1 of 1 (13 items)