Monday, 15 February 2010

Customize the ribbon programmatically from web parts and field controls

In this article series:

  1. Customizing the ribbon – creating tabs, groups and controls
  2. Adding ribbon items into existing tabs/groups
  3. Ribbon customizations - dropdown controls, Client Object Model and JavaScript Page Components
  4. Customize the ribbon programmatically from web parts and field controls (this post)

In contrast to my earlier posts, this article isn’t a detailed walkthrough – rather, it is a compendium of techniques and information which should help you implement ribbon customizations for web parts, custom field controls or indeed, any other kind of page control. At the time of writing I haven’t really seen a great deal written on this, aside from a couple of resources I’ll mention. I would have loved to have written detailed walkthroughs here, but alas I can’t because a) Microsoft have recently published some good info for one of the scenarios, so I’d prefer to point you to that, b) To run through all the scenarios here in depth would take weeks and c) Because if I don’t stop writing about the ribbon soon I’ll still be on this topic this time next year, and frankly I have a  book chapter to write on a completely different area of SharePoint 2010 which I need to crack on with! So we’ll look at each requirement and I’ll discuss what I think are the key techniques you’d use along with some observations from me.

Note that due to the general lack of documentation so far on this topic (and the fact I haven’t bottomed everything out in all the scenarios), a couple of things here are speculation rather than hard fact. I’ll make these items clear, and will endeavour to come back to this article and add updates as new information emerges. 

Before we dive in, remember that if you’re abiding by ribbon design principles you should most likely be working with a ContextualGroup (discussed towards the end of Adding ribbon items into existing tabs/groups) – this is the container to use for ribbon elements which are only relevant depending on what the user is doing (shown visible but not active here):

ContextualGroup

If this isn’t what you want, note that things are probably easier if you just need to add some controls on an existing tab or group which get activated under certain circumstances. In this case you can just supply some JavaScript to the ‘EnabledScript’ attribute of your controls – I showed this in my Notifications/Status demo in Customizing the ribbon (part 1) – creating tabs, groups and controls. The rest of this article focuses on how you might get a contextual group (see image above) to show in different scenarios.

 Adding ribbon items from a web part

In SharePoint 2010 the web part framework now has special provision for ribbon customizations, which means a couple of things are taken care of for you. Microsoft have now published some guidance on this scenario in the form of an article on the SharePoint Developer Documentation blog - How to Create a Web Part with a Contextual Tab. There’s a lot of golden info in there, but I’ll distil the main points here:

  • Using server-side code, the ‘RegisterDataExtension’ method of SPRibbon can be used to pass XML to the ribbon framework.
    • This is an alternative to the fully declarative approach using the CustomAction element. As far as I can tell, either technique can be used for ribbon customizations specific to a certain control only (as opposed to ribbon customizations for say, all lists of a specific type where CustomAction is the way to go).
  • Your web part needs to implement the new IWebPartPageComponentProvider interface and it’s WebPartContextualInfo property
    • This allows you to specify information about the associated ribbon customization(s) and the ID of the associated page component (which we discussed last time) for your custom bits. This allows the ribbon framework to ‘link’ your web part with your ribbon changes – meaning that certain things are taken care of for you e.g. firing commands only when your web part has focus on the page (if you specified this behaviour by use of ‘getFocusedCommands’ in your page component). Without this you would have to write JavaScript code to manually handle page events for controls emitted by your web part e.g. the click event for a textbox.

Adding ribbon items from a field control

Like web parts, I think (speculating here) there is special provision in the framework for ribbon customizations from a field control. Consider that, like web parts, field controls should typically only show their contextual ribbon options when they have focus - since this would be the case for all field controls, it makes sense to me that Microsoft might abstract some handling around this for us. If not, you would be responsible for writing JavaScript to detect when the user clicked into your field so that you could show your ContextualGroup.

Digging around, I notice that all SharePoint field controls have 4 new properties (since they are implemented on one of the base classes, Microsoft.SharePoint.WebControls.FormComponent):

  • RibbonTabCommand
  • RibbonContextualGroupCommand
  • RibbonGroupCommand
  • RibbonCommand

I’m surmising that these control properties would be set declaratively in the hosting page and are designed to match up with commands specified in an accompanying page component - this would enable you to run client-side code similar to my sample last time, perhaps to initialize data for your ribbon controls and so on. However, when I try to implement these commands, my page component’s ‘handleCommand’ method never receives these commands. So either I’m doing something wrong or this theory is incorrect. In which case, not to worry ribbon customizations for field controls should still be entirely possible, there will just be more work to do. Read on.

Using server-side code to show ribbon items

Thinking outside of any ‘framework support’ for where our ribbon customizations are targeted at, we can always write server-side or client-side code to show a contextual group. In fact, I already showed the server-side code for this in Adding ribbon items into existing tabs/groups (ribbon customization part 2):

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);
}

This is probably only appropriate if your stuff is contextual-ish – an application page would be a good example of this. In this example all we care about is that the user is on our page, then we can show our options. It doesn’t matter which control has focus, effectively our options are OK to show by default when the page loads. However, if you need page-level ‘contextuality’ (I may have just invented that word by the way - use it in a sentence to your boss today) then most likely you’ll be wanting to use JavaScript to show your contextual group when the user is doing something specific on the page e.g. editing a certain field. You’d then be looking to some client-side code to detect this and respond accordingly.  

Using client-side code to show ribbon items

So, final scenario - what if you need to show ribbon items from something which isn’t a web part or field control (or like me you couldn’t get there with anything in the field control framework which may or may not be designed to help), and it has to be done client-side to be fully contextual? Well, disappointingly I’m drawing another blank here so far – I’d love to hear from anyone who knows the answer to this. In case you’re doing ribbon development and are interested, here’s a summary of my journey:

  • Checked SP.Ribbon namespace in Client OM
  • Spent many happy hours in the debugger and various out-of-the-box JavaScript files, looking for examples of where the core product does this (e.g. calendar, rich text editor to name a couple)
  • Found some interesting methods on the CUI.Ribbon object, such as showContextualGroup(‘foo’) and selectTabByCommand(‘bar’)
    • Noted that across the entire SharePoint root, these methods are only called by the RTE, not globally across the SharePoint codebase
    • Noted that the RTE code gets a CUI.Ribbon instance from a property (RTE.RichTextEditorComponent.$3b() – N.B. most of the JS is obfuscated or machine-generated* down here)
  • Tried to use SP.Ribbon.PageManager.get_instance().get_ribbon() (used elsewhere in the OOTB codebase) to get me a CUI.Ribbon instance, however this gave me a null
  • Tried to use the ‘_ribbon’ page level variable, however this appears not to be of type CUI.Ribbon as the debugger shows it does not have the methods I’m trying to call
  • Tried a couple of other things which I’ve forgotten now

Needless to say, I’d love to hear what I’m missing on this. If nothing else, hopefully MS will release some more information soon which will shed some light on how to handle this scenario.

Summary

This post doesn’t claim to have all the answers, but it might serve as a “leg up" if you’re trying to build any of these scenarios now. I’m hoping that the lack of deep information in this area is a reflection on the fact that RTM is still some time away, and that ribbon dev will get some love in the SDK between now and then. The key scenarios I discussed here are displaying custom ribbon elements from web parts and field controls, but also the more generic cases of displaying customizations with server or client-side code.

Feel free to leave a comment if you can plug some of the gaps in my coverage here.

8 comments:

Elizabeth Itty said...

Hi Chris,

Is there any way which I can bring my custom ribbon to the home page of a site?
Is there any registration type available? Here I will not be able to add the MakeTabAvailable cs code in home page.

Regards,
Elizabeth

Chris O'Brien said...

@Elizabeth,

Since the registration types are oriented around list types/content types, I'm 99% sure the only way to target a specific page in this way would be to add a web part or otherwise run some code which will make the tab available.

HTH,

Chris.

Anonymous said...

Hi there,
I am trying to design a ContextualGroup with two Tabs. The first Tab seems to be working fine, unfortunatelly I can't display the contents of the second tab. It gives me a JavaScript error saying: A template with name: undefined could not be loaded.
Is this referring to the GroupTemplate Layout? I tried multiple GroupTemplates, even some from CMDUI.xml, but still doesn't work :(
Can you give some advice?

Chris O'Brien said...

@Anonymous,

Not sure I've tried multiple ContextualGroups, but if you post your XML somewhere I can take a look and try to advise.

Cheers,

Chris.

Rainier van Slingerlandt said...

Unfortunatly, eventhough I can add Ribbon buttons using the Elements.xml. I noticed that the SPRibbon class is not available in Sandboxed solutions. I only need to rename a button text so think I'm going for the dirty JavaScript JQuery Solution :-)

Chris O'Brien said...

@Rainier,

Yes, quite true. In you case, could you not use XML to remove the original button, add a new one in it's place with the same action and new text?

Guaranteed safe in the case of service packs/patches :)

Cheers,

Chris.

Daniel Walker said...

Chris,

I have been trying to add a tab and button in pure javascript/jquery and can get a tab and group the right way, but not the rest. I feel like it should work but am missing something and would really appreciate if you have any time to look at this blog post for the "hack" I used to make it work so far.
blog post

I hope this only appears once as I had issues with the captcha!

Chris O'Brien said...

@Daniel,

Read your post - interesting stuff you're doing there :) Sorry, I haven't dug deep here, so don't have anything helpful to add I'm afraid.

Good luck!

C.