In this article series:
- Customizing the ribbon – creating tabs, groups and controls
- Adding ribbon items into existing tabs/groups (this post)
- Ribbon customizations - dropdown controls, Client Object Model and JavaScript Page Components
- Customize the ribbon programmatically from web parts and field controls
This is the second article in my series on ribbon customization. Last time we looked in detail at creating custom tabs, and in the course of that also looked at how to create "groups" on a tab, and also how to add controls. We mentioned that the container hierarchy is ribbon > tab > group > controls, and showed how to create all of those in my example (which also used SP.UI.Status/SP.UI.Notify in the Client OM). The XML there was fairly extensive, but in this post we'll see things are somewhat simpler if you don't need to create an entire tab and instead only need to add something into an existing area of the ribbon. Here we cover the two main scenarios:
- Adding a group to an existing tab
- Adding controls to an existing group on an existing tab
Also, the last post covered a lot of ground on creating new tabs etc. but I didn't get to cover something which I wanted to, so we'll mop that up here:
- Creating contextual tabs
Summary of approach – customizing existing ribbon areas
In order to slot your customizations into existing places, the following approach is used (I've broken it down, but in reality you'll probably do some of this cross-referencing automatically once you understand the relationship between various chunks of ribbon XML and the result):
- Identify the location you wish to add your customization(s) to – Default Ribbon Customization Locations has a granular list, but I find it easier to first identify just the tab you're shooting for from the list below (taken from CMDUI.XML):
- Ribbon.Read
- Ribbon.BDCAdmin
- Ribbon.DocLibListFormEdit
- Ribbon.ListForm.Display
- Ribbon.ListForm.Edit
- Ribbon.PostListForm.Edit
- Ribbon.SvcApp
- Ribbon.Solution
- Ribbon.UsageReport
- Ribbon.WikiPageTab
- Ribbon.PublishTab
- Ribbon.WebPartPage
- Ribbon.WebApp
- Ribbon.SiteCollections
- Ribbon.CustomCommands (note that these are just 'standard' tabs – read on for tabs in contextual groups)
- Find the declaration in CMDUI.XML for this tab, by searching on the string ID.
- If you haven't already, find the ribbon location in the SharePoint UI - compare with the XML so you know what you're working with.
- Stop, do not pass go without collecting the following information:
- Full 'Location' value of where you are targeting, i.e. the Location of the tab (if you're adding a group) or group (if you're directly adding controls)
- The 'Sequence' number you want to use – determined by looking at the Sequence numbers of the surrounding elements and working out where exactly you want to put your customization. As you'd expect, Sequence is a left-to-right representation of how things come out on the page.
- If adding a group, the 'GroupTemplate' used by an existing group with similar controls.
- The 'TemplateAlias' used by a control which is the same type (e.g. button) and size (e.g. large) as the control you are adding.
So with that process in mind, let's look at the XML needed for the scenarios I listed.
Adding a group to an existing tab:
In this example I'm provisioning a single button (lifted from last week's example) into my new group, which is then being added to the wiki page editing tab ('Ribbon.WikiPageTab') in CMDUI.XML. Even if you only have a single control, groups are still very useful as they visually 'categorize' your button so it's function is separated from it's neighbors. Here I picked a Sequence of '15' to nestle in between the 'Ribbon.WikiPageTab.EditAndCheckout' group (10) and 'Ribbon.WikiPageTab.Manage' (20):
Here's the XML used to get this - you'll notice there's much less of it compared to last week's 'full ribbon' example:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction
Id="COB.SharePoint.Ribbon.NewGroupInExistingTab"
Location="CommandUI.Ribbon">
<CommandUIExtension>
<CommandUIDefinitions>
<CommandUIDefinition
Location="Ribbon.WikiPageTab.Scaling._children">
<MaxSize
Id="COB.SharePoint.Ribbon.NewGroupInExistingTab.NotificationGroup.MaxSize"
Sequence="15"
GroupId="COB.SharePoint.Ribbon.NewGroupInExistingTab.NotificationGroup"
Size="Large" />
</CommandUIDefinition>
<CommandUIDefinition
Location="Ribbon.WikiPageTab.Groups._children">
<Group
Id="COB.SharePoint.Ribbon.NewGroupInExistingTab.NotificationGroup"
Sequence="15"
Description="Used to demo adding a group"
Title="Chris's custom group"
Template="Ribbon.Templates.Flexible">
<Controls Id="COB.SharePoint.Ribbon.NewGroupInExistingTab.NotificationGroup.Controls">
<Button
Id="COB.SharePoint.Ribbon.NewGroupInExistingTab.NotificationGroup.NotifyHello"
Command="COB.NewGroupInExistingTab.Command.Notify"
Sequence="15" Image16by16="/_layouts/images/NoteBoard_16x16.png" Image32by32="/_layouts/images/NoteBoard_32x32.png"
Description="Uses the notification area to display a message."
LabelText="Notify hello"
TemplateAlias="o1" />
</Controls>
</Group>
</CommandUIDefinition>
</CommandUIDefinitions>
<CommandUIHandlers>
<CommandUIHandler
Command="COB.NewGroupInExistingTab.Command.Notify"
CommandAction="javascript:
SP.UI.Notify.addNotification('Hello from the notification area');
" />
</CommandUIHandlers>
</CommandUIExtension>
</CustomAction>
</Elements>
Points of note:
- The Location of 'Ribbon.WikiPageTab.Groups._children' on the main CommandUIDefinition tells the framework I am adding to the groups collection to the 'Ribbon.WikiPageTab' tab. This makes sense as I am adding a group.
- When adding a group, you must decide whether to use an out-of-the-box GroupTemplate (defines how controls are laid out), or whether you will supply the definition. In this sample I'm using 'Ribbon.Templates.Flexible' which is suitable for simple layouts like this (one button!), whereas in the last article I showed creating a custom GroupTemplate. It's worth spending some time in CMDUI.XML looking at the wide range of existing generic templates before creating your own, but by the same token sometimes it might be simpler just to create one, rather than find an existing one which matches the controls you're trying to lay out. It soon becomes clear how the XML works here though - the analogy I used last week is using HTML to define a table.
- Remember that the 'TemplateAlias' on your controls must match one defined somewhere in the GroupTemplate – in my example above, I'm using a TemplateAlias of 'o1' which I know is defined in 'Ribbon.Templates.Flexible'
- A 'MaxSize' definition must be supplied to the appropriate Scaling collection (i.e. 'Ribbon.WikiPageTab.Scaling._children' in this case), though it seems a 'Scale' definition is optional (e.g. if you want different controls to be used when less space is available)
Adding controls to an existing group:
If all we need to do is add a control or two to an existing group on an existing tab, things are simpler still. All we need to do is define the control/specify where it goes (CommandUIDefinition) and add the JavaScript behaviour (a CommandUIHandler element for simple cases – the approach for complex cases is discussed later in the series). For this example I'm adding to 'Ribbon.ListForm.Display.Manage.Controls._children' – this is DispForm.aspx (the list item display form), and is a good reminder that ribbons exist in application pages and can be customized there too:
..and here's the relevant XML:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction
Id="COB.SharePoint.Ribbon.NewControlInExistingGroup"
Location="CommandUI.Ribbon" Sequence="20">
<CommandUIExtension>
<CommandUIDefinitions>
<CommandUIDefinition Location="Ribbon.ListForm.Display.Manage.Controls._children">
<Button Id="COB.SharePoint.Ribbon.NewControlInExistingGroup.Notify"
Command="COB.Command.NewControlInExistingGroup.Notify"
Sequence="5" Image16by16="/_layouts/images/NoteBoard_16x16.png" Image32by32="/_layouts/images/NoteBoard_32x32.png"
Description="Uses the notification area to display a message."
LabelText="Notify hello"
TemplateAlias="o1"/>
</CommandUIDefinition>
</CommandUIDefinitions>
<CommandUIHandlers>
<CommandUIHandler
Command="COB.Command.NewControlInExistingGroup.Notify"
CommandAction="javascript:
SP.UI.Notify.addNotification('Hello from the notification area');
" />
</CommandUIHandlers>
</CommandUIExtension>
</CustomAction>
</Elements>
Points of note:
- Scaling instructions such as 'MaxSize' are not required, nor is a 'Group' or 'GroupTemplate' – since all these apply to groups, which we are not creating
- As mentioned above, ensure that the 'TemplateAlias' on your controls matches one defined somewhere in the GroupTemplate for the parent group. If you're using one of the 'Flexible' templates, 'o1' gives a large button whilst 'o2' gives a medium one. Always check the XML for the target group though, there could be differences.
The 'Custom Commands' tab
Before rushing off to create your new tab, consider that SharePoint 2010 already provides a home which may be suitable for your customization, for list pages at least – the Custom Commands tab. It only appears on list pages and contains just one lonely button under normal circumstances. This tab is actually the post-upgrade home for any SharePoint 2007 CustomActions you had – assuming you don't introduce changes during your upgrade process, any CustomActions which didn't target the ECB will end up here. In any case, it could help avoid tab proliferation so don't forget it when building your customizations:
Creating new contextual tabs
So this section doesn't quite fit with the theme of this article (adding items to existing areas of the SharePoint ribbon), but I vote we conveniently gloss over that fact. Anyway, in addition to regular tabs, you'll have noticed that the ribbon also displays many contextual tabs which only appear when relevant. In fact, these are really contextual groups which can contain any number of tabs – perhaps the most common are the 'List Tools' (blue, shown in the last screenshot) and 'Library Tools' which appear in lists and libraries respectively. If we have custom ribbon controls which we only want to present conditionally, a contextual group could be a good design choice since it would fit well with existing ribbon semantics. They also look rather sexy. As you might expect, we can amend existing contextual groups or create our own:
To do this, simply wrap your Tab element(s) in a 'ContextualGroup' element (notice the CustomAction element has no RegistrationId/RegistrationType attributes, meaning it is scoped globally) and write some code which runs when the group should be shown. First the declaration of the ContextualGroup and Tab:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction
Id="COB.SharePoint.Ribbon.ContextualTab"
Location="CommandUI.Ribbon">
<CommandUIExtension>
<CommandUIDefinitions>
<CommandUIDefinition Location="Ribbon.ContextualTabs._children">
<ContextualGroup Id="COB.SharePoint.Ribbon.ContextualGroup" Sequence="50" Color="Orange" Command="COBContextualGroupCommand" ContextualGroupId="COB.Contextual" Title="Chris's Contextual Group">
<Tab Id="COB.SharePoint.Ribbon.ContextualTab" Title="Chris's custom tab" Description="Groups and controls will go in here" Sequence="501">
<!-- Add Scaling, Groups, GroupTemplates (optional), CommandUIHandlers etc. in here as per creating a normal tab -->
</Tab>
</ContextualGroup>
</CommandUIDefinition>
</CommandUIDefinitions>
</CommandUIExtension>
</CustomAction>
</Elements>
Here I'm writing server-side code (e.g. from a web part, custom field control, or other custom control), and all you need to do is call SPRibbon.MakeTabAvailable() and SPRibbon.MakeContextualGroupInitiallyVisible(), the latter assuming you want your group to be visible immediately when the page loads:
protected override void OnPreRender(EventArgs e)
{
SPRibbon currentRibbon = SPRibbon.GetCurrent(this.Page);
currentRibbon.MakeTabAvailable("COB.SharePoint.Ribbon.ContextualTab");
currentRibbon.MakeContextualGroupInitiallyVisible("COB.SharePoint.Ribbon.ContextualGroup", string.Empty);
base.OnPreRender(e);
}
In the absence of documentation, equivalent client-side code isn't immediately obvious but I'll dig around and update this post when I find it.
In terms of updating an existing contextual group or child tab, the following are defined in CMDUI.XML (contextual groups at level 1, child tabs at level 2), and you can probably identify some of them with things you've seen on your SharePoint 2010 travels:
- Ribbon.EditingTools
- Ribbon.EditingTools.CPEditTab
- Ribbon.EditingTools.CPInsert
- Ribbon.Image.Image
- Ribbon.Document
- Ribbon.Library
- Ribbon.ListItem
- Ribbon.List
- Ribbon.Link.Link
- Ribbon.Table.Layout
- Ribbon.Table.Design
- Ribbon.WebPartInsert.Tab
- Ribbon.WebPartOption
- Ribbon.Calendar.Events
- Ribbon.Calendar.Calendar
- Ribbon.Permission
Summary
The ribbon is a key building block for SharePoint 2010 solutions, and anywhere you see a ribbon tab it can be customized. This means list pages, the page editing experience, particular application pages, Central Administration and many other locations can be targeted in the same way - identify the ID of the location from CMDUI.XML, then use this in the XML which declares your ribbon customization. In addition to adding items to existing tabs and groups, we also looked at contextual groups, and showed how to conditionally display them with code.
Next time – going beyond simple button customizations with JavaScript page components