In this article series:
- Customizing the ribbon – creating tabs, groups and controls
- Adding ribbon items into existing tabs/groups
- Customize the ribbon programmatically from web parts and field controls
[Beta sidenote] At the time of writing (Feb 2010, still in beta), after some time on this I’m forming the opinion that ribbon development could be one of the more complex development areas in SP2010. Maybe some stellar documentation from Microsoft could change this, but right now there are many dark corners which are quite difficult to understand – currently there is practically no coverage of much of this stuff in the SDK (and very little anywhere really), so unless you have inside information it’s mainly blood, sweat and tears all the way. I’ve mentioned this to the PM in the Product Group (Elisabeth Olson), and it sounds like more MS guidance is on the way soon, so let’s hope.
My sample shows the use of a custom dropdown in the ribbon – I’ll go into more detail later, but the concepts I’m showing here can be used for many controls in the ribbon, not just a dropdown. So if what you’re wanting to do doesn’t specifically involve a dropdown, I’d suggest reading on anyway as this technique is still probably what you will use.
When clicked the first time, it uses the Client Object Model to fetch a collection of lists in the current web, then presents them as options in the dropdown:
What’s required – summary
- Declarative XML to provision the ribbon controls
- Link the external .js file
- Ensure core dependent .js files are loaded e.g. SP.js, CUI.js
- Call into the initialization function within our page component – this registers our component with the ribbon framework and ensures our component gets added to the page.
1. Declarative XML
I used the following XML – here I’m actually showing a cut-down extract which is just for the group containing my controls. Really it’s just the ‘Controls’ section which is the most interesting bit, the surroundings would depend on whether you are wanting to create a new tab or add the items into an existing tab/group, see my previous articles for those details.
- The ‘initialize’ function is typically responsible for calling ‘addPageComponent’ on the ribbon PageManager (but not before SP.Ribbon.js has loaded)
- The commands referenced in the JS are those specified in the control XML e.g. for my dropdown
- The ‘getFocusedCommands’ function returns an array of commands which should execute when my control has focus
- The ‘getGlobalCommands’ function returns an array of commands which should execute regardless of focus
- We need to list the commands which can be handled in the ‘canHandleCommand’ function, and provide the actual implementation for each of these in ‘handleCommand’
- Note the following crucial points about the various commands used:
- PopulateQueryCommand – used to build the list of items in the control. This is where I’m using the Client Object Model (ECMAScript version) to fetch the lists for the current web.
- QueryCommand – called when the parent container (e.g. tab) is activated. Remember the ribbon is all about “script on demand” (lazy loading), so I’m choosing this as a better place to do my initialization work of the actual Client OM request – more on this later.
- Command – called when the user actually selects an item
- IMPORTANT – these commands apply to lots of ribbon controls other than dropdowns e.g. FlyoutAnchor, SplitButton, ToggleButton, TextBox, Checkbox, Spinner, etc. This is why this information is relevant even if it’s not specifically a dropdown control you’re working with.
- The key to populating controls which take collections is to use the ‘properties’ object passed to handleCommand, using either: ul>
- I’m using properties.PopulationXML to supply the items which should appear in my dropdown, and the format required is:
- There are some interesting facets to combining the Client OM with the ribbon JS framework – effectively the async model used means the result of your method call may not be ready by the time the ribbon framework needs it (it happens on a different request after all). I’ll explain how I dealt with this in my example towards the end of this article.
The important things here are that we ensure required system JS files are loaded with the ScriptLink tag, do the same for our JS file, then call the .initialize() function of our page component.
So those the component pieces for complex controls in the ribbon! A wide variety of ribbon customizations should be possible by tailoring this information/sample code as needed (remember ‘handleCommand’ is the key implementation hook), and I definitely think that starting from such a template is the way to go.
Appendix - considerations for using the Client Object Model in the ribbon
Consequently, you need to do the actual processing before the ‘PopulateQueryCommand’ is called. You could choose to do as soon as the page loads, but in most cases this could be inefficient – what if the user doesn’t come near your ribbon control on this page load? In this case we would have incurred some client-side processing and an extra request to the server which was completely unnecessary – on a high-traffic page, this could be bad news. Without any documentation it’s hard to be sure at this stage, but it seems the ‘QueryCommand’ is a good place to put such Client OM code – this seems to be called when the parent container (e.g. tab) is made visible (at which point there’s now a chance the user could use our control). In my code I have the actual Client OM query run here and store the result in page-level variable - this is then picked up and iterated for the ‘PopulateQueryCommand’. By the time the script runs this command to populate the control, the query has already executed and has the data ready – happy days. I’ll be interested to see what emerges in this area to see whether this is the expected pattern or, well, if I’ve got things completely wrong.