Add Web Parts to Masterpages and Have Ribbons Working, Too

Adding Web Parts to masterpages is verboten. While you can add Web Parts without a Web Part zone aka static Web Parts you don’t get any Web Part benefits from it–they act like any other control. That also means they won’t integrate with the ribbon infrastructure.

That doesn’t bother most people as they’ll just add a ContentPlaceHolder and use the page layout to fill in the Web Part zone. But that won’t work with Sharepoint 2010 team sites–those are wiki based and therefore you can’t apply page layouts.

Turns out there’s a much easier solution than using page layouts: just wrap everything in a user control. That means your user control will consist of only the XML to define the Web Part zone and your Web Part in it. In your masterpage you then reference your user control like you would reference a static Web Part–problem solved!

Disclaimer: while the above worked in my scenario there’s a reason why Microsoft doesn’t allow Web Parts in masterpages. Depending on your scenario you may have to invest additional work.

OK, some additional notes just so you didn’t follow the link to find only three paragraphs and a disclaimer:

  • The ribbon caching can throw you for a loop. Instead of deleting IE’s cache manually every time you start your project add this line to your post build event command line:
    RunDll32.exe InetCpl.cpl,ClearMyTracksByProcess 8
    It will take care of those two pesky commandui.ashx?… files.
  • The two commandui.ashx?… files are quite handy to check what the client actually received. The one with a qt=commandhandlers parameter should show the contents of your CommandUIHandlers element and the one with id=Ribbon.MyTabName.Tab and qt=ribbontab parameters should contain your tab definitions. Of course everything is in JASON. Searching the files in IE’s Temporary Internet Files directory is quite laborious but FireFox’s Firebug addon displays them nicely in it’s Net tab.
  • Most examples show the ribbon XML being generated as a string. Don’t do that unless you have a compelling reason. A file is much easier to validate.
  • Quite often you see examples using a page component–that’s a piece of object oriented JavaScript. But nobody knows how it’s really supposed to work and usually you are better off adding JavaScript to your ribbon XML or generating it in your Web Part. The page component is not mandatory. On the other hand I found the ribbon load times go up when there was a lot of JavaScript in the XML.
  • To save some HTTP round-trips use image maps instead of individual files for your ribbon’s icons. You can find SharePoint’s image maps in it’s 14 hive under \TEMPLATE\LAYOUTS\<YourLcid>\IMAGES–most notably formatmap16x16.png, formatmap32x32.png, and rtecluster.png.
  • If your ribbon shows up but disappears instantly chances are your EnabledScript isn’t returning true. It may seem to do so but step into the calling JavaScript where you’ll find eval() evaluating your function and probably returning ‘undefined’ because of syntactical errors.
  • The ribbon infrastructure checks the activated features to determine which ribbons might show up on a page. Two consequences of this:
    • Don’t forget to activate your web part feature like I did or your ribbon will never show :-). An error message like 'The operation could not be completed because the Web Part is not on this page.' when calling SPLimitedWebPartManager’s GetStorageKey() or GetZoneID() while WebPartManager.WebParts contains your Web part is a telltale sign of this.
    • If your ContextualGroup has an EnabledScript attribute you’ll have to provide the referenced JavaScript even if your Web part is not on the page! See example XML below.
<!-- Providing stand-alone EnabledScript -->
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 ...
  <CommandUIHandlers>
   <CommandUIHandler
    Command="Ribbon.My.ContextualGroup.Command"
    CommandAction=""
    EnabledScript="javascript:function handleMissingWebPart()
     {
      result = false;
      if (typeof isMyCommandEnabled == 'function') 
       result = isMyCommandEnabled('Ribbon.My.ContextualGroup.Command'); 
      return result;
     }
     handleMissingWebPart();"
   />
  </CommandUIHandlers>
 ...
</Elements>

Update 2010-11-07: Added note about using image maps.

Advertisements
This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s