Monday, June 27, 2011

Adding custom tab to Ribbon Bar


This sample will show you how to create a custom ribbon tab, though there is a very good blog over MSDN which i used as reference in the beginning yet there are few tricks which i had to discover by myself

1-      Create Empty SharePoint 2010 project, in my workshop I followed Farm deployment. You can still deploy your project as Sandboxed solution but in this case you will need to add sand boxed web part in the further steps instead of regular visual web part which I used to demo this sample




2- Add Empty Feature item to your SharePoint project, this will add a new feature item in your project, if you compiled your project now you will find compilation errors so to fix that make sure you have deleted the .cs file generated with the feature




3- Right click your feature and select Add Feature Resource File. Rename your resource file “Resources”

4- Edit your resources file add following strings, you can change string values if you like



String
Value
Description
TabTitle
TGO
Title which appears on your tab
ContextGroupTitle


AboutDialogButton
About
About button label
GeneralDialogButton
General
General button label
Group1Description
NA
Group1 label
Group1Description
Assignment
Group2 label



5- At the Element.xml file erase all contents and copy the included xml. I usually work with Contextual tabs because i can simply call NormalizeContextualGroup method which gives it the same look and feel of normal tab



<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="WebPartContextualTabs"
                Location="CommandUI.Ribbon">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition Location="Ribbon.ContextualTabs._children">
          <ContextualGroup Id="Ribbon.WebPartContextualTabGroup"
                           ContextualGroupId="WebPartContextualTab"
                           Title="$Resources,ContextGroupTitle;"
                           Sequence="5000"
                           Color="LightBlue"
                           Command="WebPartContextualTab.OnEnableContextualTab">
            <Tab Id="Ribbon.SAMPLETab"
                Title="$Resources,TabTitle;"
                Sequence="501">
              <Scaling Id="Ribbon.SAMPLETab.Scaling">
                <MaxSize Id="Ribbon.SAMPLETab.MaxSize"
                          GroupId="Ribbon.SAMPLETab.PropertyGroup"
                          Size="LargeLarge"/>
                <MaxSize Id="Ribbon.SAMPLETab.MaxSize"
                          GroupId="Ribbon.SAMPLETab.PostBackGroup"
                          Size="LargeLarge" />
              </Scaling>
              <Groups Id="Ribbon.SAMPLETab.Groups">
                <Group Id="Ribbon.SAMPLETab.PropertyGroup"
                       Title= "$Resources,ServerGroupName;"
                       Description="$Resources,AssignemtnGroupDescirption;"
                       Sequence="15"
                       Template="Ribbon.Templates.Flexible2">
                  <Controls Id="Ribbon.SAMPLETab.PropertyGroup.Controls">
                    <Button Id="Ribbon.SAMPLETab.PropertyGroup.AboutDialogButton"
                            LabelText="$Resources,AboutDialogButton;"
                            TemplateAlias="o1"
                            Sequence="15"
                            Image16by16="../../SiteAssets/SAMPLEImages/about.jpg"
                            Image32by32="../../SiteAssets/SAMPLEImages/about.jpg"
                            />
                    <Button Id="Ribbon.SAMPLETab.PropertyGroup.AssignmDialogButton"
                            LabelText="$Resources,GeneralDialogButton;"
                            TemplateAlias="o2"
                            Sequence="17"
                            Image16by16="../../SiteAssets/SAMPLEImages/Generate.png"
                            Image32by32="../../SiteAssets/SAMPLEImages/Generate.png" />
                  </Controls>
                </Group>
                <Group Id="Ribbon.SAMPLETab.PostBackGroup"
                       Title="$Resources,AssignmentGroupName;"
                       Sequence="25"
                       Template="Ribbon.Templates.Flexible2">
                  <Controls Id="Ribbon.SAMPLETab.PropertyGroup.Controls">
                    <Button Id="Ribbon.SAMPLETab.PropertyGroup.GeneralDialogButton"
                            LabelText="$Resources,AsssignmentDialogButton;"
                            Command="WebPartContextualTabs.OnPostback"
                            TemplateAlias="o1"
                            Sequence="15"
                            Image16by16="../../SiteAssets/SAMPLEImages/Generate.png"
                            Image32by32="../../SiteAssets/SAMPLEImages/Generate.png" />
                    </Controls>
                </Group>
              </Groups>
            </Tab>
          </ContextualGroup>
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers>
        <CommandUIHandler Command="WebPartContextualTab.OnEnableContextualTab"
                          CommandAction=""
                          EnabledScript="return true;" />
        <CommandUIHandler Command="WebPartContextualTabs.OnPostback"
                          CommandAction="javascript:__doPostBack('RibbonizedWebPartPostback','');" />
        <CommandUIHandler Command="WebPartContextualTab.OnAssignmentGenerate"
                          CommandAction="javascript:__doPostBack('RibbonizedWebPartPostback','');" />
      </CommandUIHandlers>
    </CommandUIExtension>
  </CustomAction>
</Elements>


  • The following xml creates a Contextual Tab with one tab composed of two groups
  • I am referring to titles from resources files so you can replace it by hard coded strings or add a resources file to your feature and refer it
  • Edit Element.xml file to point to your images at the button tags instead of paths i am initially using in order to display your images over your buttons controls

6- There is still one part missing which is in order to display your contextual tab you have to inform the server to display it. Now this is done via a custom web part or user control which registers the tab during page launch up, in my example i choose to add visual web part



7- Replace your web part code with the following

[ToolboxItemAttribute(false)]
public class SAMPLEWebPart : WebPart
{
    private const string POSTBACK_EVENT = "RibbonizedWebPartPostback";
    private const string POSTBACK_EVENT_NAV = "RibbonizedNavAssignment";
    protected override void CreateChildControls()
    {
            HandleRibbonPostback();
    }
    protected override void OnLoad(EventArgs e)
    {
            
            if (!Page.IsPostBack)
                 LoadAndActivateRibbonContextualTab();
            //setting chrometype will completely hide the webpart
    
    protected override void OnPreRender(EventArgs e)
    {
            this.ChromeType = PartChromeType.None;
            base.OnPreRender(e);
    }

    /// <summary>
    /// Activates the ribbon tab.
    /// </summary>
    private void LoadAndActivateRibbonContextualTab()
    {
        SPRibbon ribbon = SPRibbon.GetCurrent(this.Page);
        // make sure ribbon exists
        if (ribbon != null)
        {

            ribbon.CommandUIVisible = true;
            ribbon.Minimized = false; 
            // load & activate contextual tab
            if (!ribbon.IsTabAvailable("Ribbon.SAMPLETab"))
               ribbon.MakeTabAvailable("Ribbon.SAMPLETab");
               
           ribbon.SetInitialTabId("Ribbon.SAMPLETab", string.Empty);
                ribbon.MakeContextualGroupInitiallyVisible("Ribbon.WebPartContextualTabGroup", string.Empty);
           
            ribbon.NormalizeContextualGroup("Ribbon.WebPartContextualTabGroup", string.Empty);
            }
        }
       
   private void HandleRibbonPostback()
    {
        if (null == this.Page.Request["__EVENTTARGET"])
            return;

         if (this.Page.Request["__EVENTTARGET"] == POSTBACK_EVENT)
         {
                //do some code here
         }
     }
    }




  • The following code informs the ribbon to display your custom tab, select your custom tab as initial tab and display your contextual group
  • NormalizeContextualGroup() displays your context group tabs as flat tabs so you can comment this method calling and still enjoy your contextual tab appearance

8- Finally all you need now is to place your web part on your pages in order to display your tabs

  • You can place it anywhere at your site home page
  • You can use the designer to edit the All Items.aspx page of your custom list and places your custom web part.

o    For some reason when placing the web part under the WebPartZone tag at the AllItems.aspx page the contextual tab {List Tools} disappears so in order to avoid such non sense behaviour i found that by placing custom web part under the content place holder "PlaceHolderBodyAreaClass" everything works fine

o     Also you need to select your tab as initial tab and call Ribbon.Minimized = false as we coded in the web part, your custom tab disappears if the user selects any tab in the List Tools as first selection while if the user selects custom tab first then navigates to List Tools tabs your custom tab wont disappear, any way you don't need to worry it is all fixed in this sample and i believe such behaviour is related to the issue i am discussing at point # 9



9- One last thing to mention which is extremely vital after deploying your tab normally you are going to need to apply more changes in the future but you will be shocked that changing any xml declarations in the Elements xml file sometimes are not reflected in your custom tab.


I initially changed the tab id in my elements xml file and it was working fine with me for some time but I didn’t have clue why that was occurring; then I figured out that the problem has to do with the browser caching so make sure you clear your browser caching, I used to have my controls dimmed or old tabs displayed this behaviour occurred with IE but when I switched to chrome I found it working fine ; then it started occurring with chrome and hence I figured out it is a caching problem so with every new deployment I clear my browser cache and  refresh the page