Adding a custom company menu tab with dynamic menu on the ribbon
Posted by Patrick Boom on May 25, 2010
The move to include the ribbon in SharePoint provides additional challenges to site designers. They have to include it somehow in their designs, as the ribbon is here to stay. One of our clients had an own navigation menu that he wanted to integrate with the ribbon. The problem is that this menu is dynamic and can change at any moment in time. And the SharePoint 2010 ribbon is static by nature. It is defined in XML files on startup.
That made me wonder whether it would be possible to dynamically add menu items to controls, tabs and groups. I then stumbled upon a blog post by a company called ICC here that described a method of dynamic flyout anchors. Another post described here by Tom Wilson described how we could change the look and feel of the ribbon to fit the internal company design.
I basically used the method described by ICC to dynamically construct my menu items on a separate tab. The source of the menu is a web service that provides the menu items in XML. An XSL transformation then creates the necessary xml definition for SharePoint, disconnecting the web service from technology specific implementations. This resulted in a combination of custom defined tabs, server side command handlers, page components and the works, meaning quite a comprehensive piece of code and a lot for this article. Although I have tried to be as complete as possible, I might have described some parts in less detail then I should. If so, feel free to leave a comment. Some stuff that will be covered in this one example:
- Creating Tabs and Groups on the ribbon
- Populating FlyoutAnchor dynamically
- Registering commands on the server
- Calling server side code using ICallbackEventHandler interface
Quite some stuff to cover here. Let us first take a look at an architectural picture on what we are trying to accomplish here:

Define the Tab, Group and Control elements.
So, let us start coding. Open Visual Studio 2010 and create a new empty SharePoint project. Add a new empty element and call it MyCompany. Also add a mapping to the Layouts folder by right clicking the project and select Add à SharePoint Mapped Layouts folder. This will host our custom JavaScript files. Open the Elements.xml file in the MyCompany empty element and populate it with the following XML:
<?xml version=“1.0” encoding=“utf-8“?>
<Elements xmlns=“http://schemas.microsoft.com/sharepoint/“>
<Control
Id=“AdditionalPageHead“
Sequence=“200“
ControlClass=“$SharePoint.Project.FileNameWithoutExtension$.CompanyTabLoader“
ControlAssembly=“$SharePoint.Project.AssemblyFullName$“>
</Control>
<CustomAction
Id=“Boom.Ribbon.EnterpriseTabExample“
Title=“Enterprise Menu“
Location=“CommandUI.Ribbon“>
<CommandUIExtension>
<CommandUIDefinitions>
<CommandUIDefinition Location=“Ribbon.Tabs._children“>
<Tab
Id=“Boom.Ribbon.EnterpriseTab“
Sequence=“250“
Title=“Enterprise Menu“>
<Scaling Id=“Boom.Ribbon.Enterprise.Scaling“>
<MaxSize Id=“Boom.Ribbon.Enterprise.Scaling.MyCompany.MaxSize” Sequence=“20” GroupId=“Boom.Ribbon.Enterprise.MyCompany” Size=“LargeMedium“ />
<MaxSize Id=“Boom.Ribbon.Enterprise.Scaling.MyJob.MaxSize” Sequence=“40” GroupId=“Boom.Ribbon.Enterprise.MyJob” Size=“LargeMedium“ />
<MaxSize Id=“Boom.Ribbon.Enterprise.Scaling.MyHR.MaxSize” Sequence=“40” GroupId=“Boom.Ribbon.Enterprise.MyHR” Size=“LargeMedium“ />
<Scale Id=“Boom.Ribbon.Enterprise.Scaling.MyCompany.MediumSmall” Sequence=“100” GroupId=“Boom.Ribbon.Enterprise.MyCompany” Size=“MediumSmall“ />
<Scale Id=“Boom.Ribbon.Enterprise.Scaling.MyJob.MediumSmall” Sequence=“120” GroupId=“Boom.Ribbon.Enterprise.MyJob” Size=“MediumSmall“ />
<Scale Id=“Boom.Ribbon.Enterprise.Scaling.MyHR.MediumSmall” Sequence=“120” GroupId=“Boom.Ribbon.Enterprise.MyHR” Size=“MediumSmall“ />
</Scaling>
<Groups Id=“Boom.Ribbon.Enterprise.Groups“>
<Group
Id=“Boom.Ribbon.Enterprise.MyCompany“
Title=“My Company Menu“
Template=“Ribbon.Templates.Flexible2“
Sequence=“100“>
<Controls Id=“Boom.Ribbon.Enterprise.MyCompany.Controls“>
<FlyoutAnchor
Id=“Boom.Ribbon.Enterprise.MyCompany.Menu“
Command=“Boom.Ribbon.Enterprise.MyCompany.Menu“
Sequence=“10“
Image16by16=“/_layouts/$Resources:core,Language;/images/formatmap16x16.png“
Image16by16Top=“-16“
Image16by16Left=“-88“
Image32by32=“/_layouts/$Resources:core,Language;/images/formatmap32x32.png“
Image32by32Top=“-128“
Image32by32Left=“-448“
LabelText=“My Company Menu“
TemplateAlias=“o1“
PopulateDynamically=“true“
PopulateOnlyOnce=“false“
PopulateQueryCommand=“PopulateDymamicMenuItemsQueryCommand“
ToolTipTitle=“FlyoutAnchor Dymamic“
ToolTipDescription=“FlyoutAnchor with dymamic menu items“ />
</Controls>
</Group>
</Groups>
</Tab>
</CommandUIDefinition>
</CommandUIDefinitions>
<CommandUIHandlers>
<CommandUIHandler
Command=“Boom.Ribbon.Enterprise.MyCompany.Menu“
CommandAction=“”
EnabledScript=“true“ />
<CommandUIHandler
Command=“DynamicButtonCommand“
CommandAction=“JavaScript:alert(‘Dynamic Button ‘ + arguments[2].MenuItemId + ‘ clicked.’);“
EnabledScript=“true“ />
</CommandUIHandlers>
</CommandUIExtension>
</CustomAction>
</Elements>
So, what did we just include? In this XML, we define a custom action that will add a Tab to our ribbon called Boom.Ribbon.EnterpriseTab. In there, we define some scaling on how the controls in the groups should be rendered and obviously, the groups itself. In this example, I only defined one group called MyCompany for simplicity, although I did added scaling for additional groups. For the purpose of this blog however, we keep it with one group. In the group, we define a single FlyoutAnchor control that will be the focus subject on this blog. For additional information about the Tab and Group definition, there are many articles out there that describe these. For instance, refer to the blogs by Chris O’Brien on this subject.
So let us take a closer look at the FlyoutAnchor node. The Id and Command attributes follow the naming convention (project-ribbon-tab-group) as best practice, although they can be anything you like. The following attributes are of more interest: PopulateDynamically, PopulateOnlyOnce and PopulateQueryCommand. These three attributes determine that the controls in this menu will be populated dynamically. PopulateOnlyOnce determines that this will only happen the first time it is loaded, which we set to false. The PopulateQueryCommand specifies the command that will be called to populate the control. The command has to set the properties.PopulationXml property with the exact declarative XML as we would use when not loading dynamically. We will get to that part later.
But, as one can see, the command mentioned in the PopulateQueryCommand is not registered in the CommandUIHandlers section of the XML. This is because we will do this registration on the server side. We do however register two commands that make the control enabled (Boom.Ribbon.Enterprise.MyCompany.Menu) and the command that each button will execute when clicked (DynamicButtonCommand). So, how do we now wire the commands to this definition? We will get to that when we solve another problem. The above declared tab will not show up in our interface if we deploy this solution now. That is because we have omitted the RegistrationId and RegistrationType attributes in our custom action, meaning it is not tied to any context yet. As we want the tab to appear always, we have to make sure that the tab is made visible when each page loads. We do this by inserting a custom control that will do this in the pre-render phase of the page, using the AdditionalPageHead control placeholder. And since we then have a control anyway, we can also use that to wire the commands of our tab. So, add a custom class to the project. I have not included it in its own directory, but you could. I called this class CompanyTabLoader.cs. How do we ensure this is loaded on each page? Well, that is accomplished by adding the Control element at the beginning of above XML (or end if you like).
Adding a Control to the AdditionalPageHead on each page
If fact, this is quite simple to accomplish. The following declaration adds our control to the AdditionalPageHead section of the page:
<Control
Id=“AdditionalPageHead“
Sequence=“200“
ControlClass=“$SharePoint.Project.FileNameWithoutExtension$.CompanyTabLoader“
ControlAssembly=“$SharePoint.Project.AssemblyFullName$“>
Some nice bonus in above declaration. We use the VS 2010 placeholders for adding our complete assembly name upon deployment, so we do not have to fill that in ourselves
. By far, this is the easiest way to get code running on each page. As shown, this declaration tells SharePoint to include our custom control in each page by adding it to the AdditionalPageHead container. Now, we can add code to our custom control to complete the wiring of all our events.
Show the custom tab each time the page is loaded
To make sure our tab is visible on each page we need to call some methods in the PreRender event of our control. Override the OnPreRender event of our base class and add the following code. (Make sure CompanyTabLoader.cs inhereits from System.Web.UI.WebControls.WebControl. Also add references to our project to the following assemblies: Microsoft.SharePoint.dll, System.Web.dll, Microsoft.Web.CommandUI.dll.
protected override void OnPreRender(EventArgs e) {
SPRibbon ribbon = Microsoft.SharePoint.WebControls.SPRibbon.GetCurrent(this.Page);
if (ribbon != null) {
const string initialTabId = “Boom.Ribbon.EnterpriseTab”;
if (!ribbon.IsTabAvailable(initialTabId))
ribbon.MakeTabAvailable(initialTabId);
}
}
We first get a reference to the SPRibbon by calling the static GetCurrent method and passing the current page. We then call the MakeTabAvailable method of the SPRibbon class and pass the ID of the Tab to make available. If you do not have this method, then add the reference to the Microsoft.Web.CommandUI assembly. This will ensure our tab is shown on each page in the site collection. We can now proceed to creating our PageComponent JavaScript to handle the custom commands that we will create.
Registering and creating a PageComponent
The page component is a JavaScript object that we will use to assign are commands to in code. This component will be the bridge between your ribbon and your commands. This component can be very confusing and complex; I know it was for me. But then again, I am no JavaScript guru. In any way, this component tends to be generic and can be extended to fit multiple needs and commands. Let me show you mine PageComponent.js in the basis. I have added this JavaScript file to the Layouts mapped folder in our solution, in its own subdirectory.
function ULS_SP() {
if (ULS_SP.caller) {
ULS_SP.caller.ULSTeamName = “Windows SharePoint Services 4″;
ULS_SP.caller.ULSFileName = “/_layouts/Boom.DynamicMenuSample/PageComponent.js”;
}
}
Type.registerNamespace(‘Boom.DynamicMenuSample’);
// RibbonApp Page Component
Boom.DynamicMenuSample.PageComponent = function () {
ULS_SP();
Boom.DynamicMenuSample.PageComponent.initializeBase(this);
}
Boom.DynamicMenuSample.PageComponent.initialize = function () {
ULS_SP();
ExecuteOrDelayUntilScriptLoaded(Function.createDelegate(null, Boom.DynamicMenuSample.PageComponent.initializePageComponent), ‘SP.Ribbon.js’);
}
Boom.DynamicMenuSample.PageComponent.initializePageComponent = function () {
ULS_SP();
var ribbonPageManager = SP.Ribbon.PageManager.get_instance();
if (null !== ribbonPageManager) {
ribbonPageManager.addPageComponent(Boom.DynamicMenuSample.PageComponent.instance);
ribbonPageManager.get_focusManager().requestFocusForComponent(Boom.DynamicMenuSample.PageComponent.instance);
}
}
Boom.DynamicMenuSample.PageComponent.refreshRibbonStatus = function () {
SP.Ribbon.PageManager.get_instance().get_commandDispatcher().executeCommand(Commands.CommandIds.ApplicationStateChanged, null);
}
Boom.DynamicMenuSample.PageComponent.prototype = {
getFocusedCommands: function () {
ULS_SP();
return [];
},
getGlobalCommands: function () {
ULS_SP();
return getGlobalCommands();
},
isFocusable: function () {
ULS_SP();
return true;
},
receiveFocus: function () {
ULS_SP();
return true;
},
yieldFocus: function () {
ULS_SP();
return true;
},
canHandleCommand: function (commandId) {
ULS_SP();
return commandEnabled(commandId);
},
handleCommand: function (commandId, properties, sequence) {
ULS_SP();
return handleCommand(commandId, properties, sequence);
}
}
// Register classes
Boom.DynamicMenuSample.PageComponent.registerClass(‘Boom.DynamicMenuSample.PageComponent’, CUI.Page.PageComponent);
Boom.DynamicMenuSample.PageComponent.instance = new Boom.DynamicMenuSample.PageComponent();
// Notify waiting jobs
NotifyScriptLoadedAndExecuteWaitingJobs(“/_layouts/Boom.DynamicMenuSample/PageComponent.js”);
No real specifics here. As you can see, I declare a namespace and a class (prototype) to handle various events on the ribbon. Also some constructors (initializers) are declared here. The actual wiring to our custom controls will be done in code. You can reuse this page component for other extensions to the ribbon if desired.
Registering our commands and the PageComponent
Now we need to register our page component and our custom commands with the page. We move back to our custom control called CompanyTabLoader.cs. In the OnPreRender event, we create a new generic list of IRibbonCommand that will hold our custom commands. We add a new SPRibbonCommand to that list that wires our PopulateDynamicMenuItemsQueryCommand to a command on the ribbon.
var commands = new List<IRibbonCommand>();
// register the command at the ribbon. Include the callback to the server to generate the xml
commands.Add(new SPRibbonCommand(“PopulateDymamicMenuItemsQueryCommand”, “CreateServerMenu(”,”); properties.PopulationXML = menuXml;”));
As you can see in above code sample, we create a new SPRibbonCommand object with two arguments. The first is the name of the command to register, in our case the command mentioned in the declarative XML above. The second argument specifies the code to execute when this command is called. We have included two statements here. First we call CreateServerMenu with two empty arguments. That will be our server side method that will be wired in a few moments. Secondly, we set the properties.PopulationXML to the result of the server call that is loaded in the menuXml variable. We now need to register this command collection (with only one command in our case) with our page component. That is done using the following code:
//Register initialize function
var manager = new SPRibbonScriptManager();
var methodInfo = typeof(SPRibbonScriptManager).GetMethod(
“RegisterInitializeFunction”,
BindingFlags.Instance | BindingFlags.NonPublic);
methodInfo.Invoke(manager, new object[] {Page, “InitPageComponent”, “/_layouts/Boom.DynamicMenuSample/PageComponent.js”, false, “Boom.DynamicMenuSample.PageComponent.initialize()”});
// Register ribbon scripts
manager.RegisterGetCommandsFunction(Page, “getGlobalCommands”, commands);
manager.RegisterCommandEnabledFunction(Page, “commandEnabled”, commands);
manager.RegisterHandleCommandFunction(Page, “handleCommand”, commands);
We create an instance of the SPRibbonScriptManager class and add the initialize function of our page component. After that, we register our commands with the three events of our page component that return an instance of the event, getGlobalCommands, commandEnabled and handleCommand. The last will be called when a command is executed on the ribbon in our page component. We should now wire our server side method mentioned in the command.
Add a callback eventhandler for server side processing
For this, we implement the ICallbackEventHandler on our class. That will add two methods called GetCallbackResult and RaiseCallbackEvent. The latter will be called by the JavaScript, the first will be processed prior to returning to the client. In our example, we just add the menu XML needed for our FlyoutAnchor in the first method. Using variables, we can pass values from one method to another. For simplicity sake, I did not. Here is the contents of our GetCallbackResult method.
/// <summary>
/// This method will return the caller with the menu. It will now return a static menu, but you can call any webservice from here or the
/// RaiseCallbackEvent event and perform transformation there.
/// </summary>
/// <returns></returns>
public string GetCallbackResult() {
string dynamicMenuXml = “<Menu Id=’Boom.Ribbon.Enterprise.MyCompany.Menu.Menu’>”
+ “<MenuSection Id=’Boom.Ribbon.Enterprise.MyCompany.Menu.Section1′ DisplayMode=’Menu16′>”
+ “<Controls Id=’Boom.Ribbon.Enterprise.MyCompany.Menu.Section1.Controls’>”;
string buttonXML = String.Format(
“<Button Id=’DynamicButton{0}’ “
+ “Command=’DynamicButtonCommand’ “
+ “MenuItemId=’{0}’ “
+ “LabelText=’My Custom menu 1′ “
+ “ToolTipTitle=’My Custom menu 1′ “
+ “ToolTipDescription=’Dynamic Button’ />”, 0);
buttonXML = buttonXML + String.Format(
“<Button Id=’DynamicButton{0}’ “
+ “Command=’DynamicButtonCommand’ “
+ “MenuItemId=’{0}’ “
+ “LabelText=’My Custom menu 2′ “
+ “ToolTipTitle=’My Custom menu 2′ “
+ “ToolTipDescription=’Dynamic Button’ />”, 1);
buttonXML = buttonXML + String.Format(
”<Button Id=’DynamicButton{0}’ “
+ “Command=’DynamicButtonCommand’ “
+ “MenuItemId=’{0}’ “
+ “LabelText=’My Custom menu 3′ “
+ “ToolTipTitle=’My Custom menu 3′ “
+ “ToolTipDescription=’Dynamic Button’ />”, 2);
buttonXML = buttonXML + String.Format(
“<Button Id=’DynamicButton{0}’ “
+ “Command=’DynamicButtonCommand’ “
+ “MenuItemId=’{0}’ “
+ “LabelText=’My Custom menu 4′ “
+ “ToolTipTitle=’My Custom menu 4′ “
+ “ToolTipDescription=’Dynamic Button’ />”, 3);
dynamicMenuXml += buttonXML;
dynamicMenuXml += “</Controls>” + “</MenuSection>” + “</Menu>”;
return dynamicMenuXml;
}
The method creates the menu XML as expected by the control. One could also call a web service in the RaiseCallbackEvent method to get the basic menu xml and execute a XSL transformation in this method to construct this XML. I kept it simple and just did it in code here. The objective (have it done on the server), was already achieved. Once here, you can do whatever you like. Now we should wire these methods to a callback reference, so that our JavaScript can call this.
Wire server side callback to JavaScript
So, we return to our OnPreRender event. In there, we add the following:
// register the client callbacks so that the JavaScript can call the server.
ClientScriptManager cm = this.Page.ClientScript;
String cbReference = cm.GetCallbackEventReference(this, “arg”, “ReceiveServerMenu”, “”);
String callbackScript = “function CreateServerMenu(arg, context) {“ + cbReference + “; }”;
cm.RegisterClientScriptBlock(this.GetType(), “CreateServerMenu”, callbackScript, true);
//Register script files
ScriptLink.RegisterScriptAfterUI(Page, “/_layouts/Boom.DynamicMenuSample/Boom.DynamicMenuSample.js”, false, true);
We instantiate the ClientScriptManager and add a callback reference to our class. The third argument in this reference is the name of the JavaScript function that will receive the response from our server, in our case ReceiveServerMenu. We then construct the client side script that wires our CreateServerMenu function to the callback and register it all as a client script block. Pffff, what a lot of wiring
But we are almost there. Just hang on for a couple of more lines
Add the JavaScript that will receive the server response
Final thing we should do is write the client side JavaScript that receives the response and assign the response to the menuXml variable, as mentioned in our custom command. For this, I have added yet another .js (Boom.DynamicMenuSample.js) file in our layouts folder, just to separate the page component from other scripts. To use this extra JavaScript file, we need to register it. This is done using the last line in the previous code sample. In the Boom.DynamicMenuSample.js file, add the following couple of (simple) lines :
// variable to hold the server menu
var menuXml;
// This function will receive the callback from the server with the menu items.
function ReceiveServerMenu(arg, context) {
menuXml = arg;
}
The menuXml variable is used to assign it to the properties.PopulationXML in our command declaration. The ReceiveServerMenu function is referenced in our callback reference declaration above. This is the callback function that will be called when the server completes its request. The return value of that function will be assigned to the ‘arg’ argument passed. In the method, we then assign the return value to the menuXml variable (which in turn will be assigned to the properties.PopulationXML in the command), which completes our circle.
Build and deploy
Now that we have completed our coding, all we have to do is click and deploy and watch the fruits of our labor. If you are lucky, you will see something similar to below. Be patient on the first load though, as the web application still needs to spin up.

Some considerations
Now that we have completed this exercise, one could argue whether this would be the approach to create a menu. Some advantages:
Although I have not tried it, the controls that are dynamically created do not have to be buttons. It could also be FlyoutAnchor’s again, which creates nested menus.
Because we can customize also the look and feel of the ribbon, it would be possible to make it look like a normal menu, as seen many times on sites.
Menu items could easily be added or removed, without the need to redeploy code.
Disadvantages can primarily be found on networks with high latency, where this approach would not be valuable. The menu would simple take too long to load.
Secondly, this approach would be rather ‘chatty’, meaning a lot of request would go back and forth. Some kind of caching mechanism on the server side is preferred.
Anyway, it does show all the possibilities you have with the ribbon. Dynamic controls, server side processing, dynamic command registration on the server, declarative commands, tabs and groups. Have fun playing around with this.
The example project with code can be downloaded here.
This entry was posted on May 25, 2010 at 19:45 and is filed under SharePoint 2010. Tagged: Callback, Dynamic Menu, FlyoutAnchor, Javascript, PageComponent, Populate Dynamically, Ribbon Extension, Server Side Commands, SharePoint 2010. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
Tweets that mention Adding a custom company menu tab with dynamic menu on the ribbon « Patrick's SharePoint Blog -- Topsy.com said
[...] This post was mentioned on Twitter by E-MailSource, E-MailSource. E-MailSource said: Adding a custom company menu tab with dynamic menu on the ribbon …: Also add references to our project to the fo… http://bit.ly/98FII9 [...]
Jawahar said
Hi,
I tried this sample on sharepoint 2010 beta and i got the tab and group with control displaying, but only for the first time when the page loads.
When i navigate to any other link and come back to the same tab, the group with control is vanishing immediately.
Appreciate your comments on this issue.
Thanks,
Jawahar.
Patrick Boom said
Strange. The code was developed on SharePoint 2010 RTM, so maybe there is a difference there. I do think that between beta 1 and 2, the messed around with the Registration(Id|Type) on the custom action node in the declaration, which makes it behave differently. The code works on the RTM version. I just switched back and forth on my development machine; the tab is always visible and the menu available.
I do not have a Beta 2 version or RC version installed at the moment, but between Beta 2 and RC1 a lot of things were changed. As the control also registers the javascripts, are they included in the page when you do not see your tab? You can use the developer tools from IE to find out.
Also, if I read your comment correctly, your tab disappears if you select another tab or control, so without a page reload. Maybe you could elaborate some more on the issue?
Jim M. said
Really decent post… I love it. Keep ‘em coming…
Patrick Boom said
Glad you like it. Exiting times.
Gopalakrishnan said
Hi,
Wonderful post. I have a little different requirement though.
I want to show the Ribbon menu in a Document Library. Users can select multiple items in the list and will click on my custom ribbon menu. Now, I need to call an aspx page with the List ID and the list of Item IDs that are selected.
Any idea how we can achieve this?
Patrick Boom said
Quite easy to achieve actually. If you take a look at my post Send Unique Link Button, most of the code you need is already included. Use the Client Object model to collect the ID’s of all the items selected. In my sample, I only got the first selected, but it is an array, so actually all the selected items are included. Then just call the aspx with the selected items in the query string OR use a callback function to handle it directly without navigating to a different page. With the examples from both posts, you can work it out. Good luck!
Navarro said
Hello Patrick! Nice post and solution!
I got a problem when use it in my ribbon. The tab appears lovely in all site content, its ok. But the button doesnt open the “dropdown” and i cant choose the DynamicButton… some idea?
Look.. i just need put a normal button (without lists, etc).. i tried to change the FlyoutAnchor to a button, looking always for the properties.. its make my ribbon locked! Can you help me? any solution can send for my email if you prefer…
Thanks again and NICE BLOG!!!!!!!
Patrick Boom said
Hi and thanks for your comment!
Could you elaborate a little bit more on what you are trying to achieve? It would then be also usefull for other visitors.
Patrick
Navarro said
Ok. I took your sample, change the Site URL to my own and deploy the solution. The “My Company Menu“ appears but when i click it, nothing happens. In your example, should rise a DynamicButton, with 4 others buttos, right? But in my personal problem, i dont need this DynamicButton, just a simple button, redirecting to other page.
I tried to change this FlyoutAnchor for a Button, but no sucess. The Ribbon make locked and i dont know what do anymore. My beg is if you can show a little sample only with a simple button taking all Home, site pages and site content.
I appreciate all help, thanks for attention :
Patrick Boom said
Navarro,
You can download my other post about the Email Unique Button to create a simple button. If combine the two projects, so the button code for the button and the tab and section code for the rest, you probably have what you want.
As to the nothing happening once installed. My only guess would be that your web application still needs to spin up, which causes a delay in the showing of the menu as that is generated server side. After the web applicatioh has warmed up, there is no noticable delay.
Chuck said
Great article. I have gotten it working for the most part but for some reason my callback is in an infinite loop…sort of. Can you think of any reason this would happen?
As mentioned, when I employ your sample it takes about 15-20 seconds for the menu to display. If I put a break point in the javascript it appears to be in an endless loop and the ReceiveServerMenu never gets called. When I run it in non-debug mode it comes back in about 20 seconds give or take and then as mentioned it is very swift. When I try to employ my own code using this method it rarely comes back at all so I chose to go the client script route and use the Client Object Model and when I setup call backs using javascript I get the same issue. The server maximiizes it’s CPU and nothing is ever returned. A few times I have seen my menu show up with the results from my custom list but most of the time nothing. I can wait for several minutes in debug mode and then put a breakpoint in the javascript and it will immediately hit it because it is continuously rerunning the same code. I was beginning to think something is messed up in my custom master but I am using callbacks from javascript in other places without issue. Any ideas what could cause this behavior? I am thinking it is something to do with the ClientScriptManger and the GetCallbackEventReference but that is just a guess.
Patrick Boom said
Hi Chuck,
Thanks for trying out my sample. So many people, so many different ways. Could you send me your version of the code? The wiring is complex and quite error prone. It took me a while to get it right. But let me take a look to see if I can help you. You can send the code to my email address, as mentioned in the contact information.
Patrick
Evgeny said
Hi Patrick,
Are you found solution?
I try you code too. And have a same issue, with server request looping when I try open dropdown menu for button:
think that a problem with GetCallbackEventReference – this method work async always, but you code want sync request for fill variable
CreateServerMenu(“”,”"); <– this method completed momentally without wait any server response
properties.PopulationXML = menuXml; <– empty menuXml
function ReceiveServerMenu(arg, context) {
(
menuXml = arg; <– this line called when all above are completed
}
Patrick Boom said
Hi Evgeny,
You could be right, but I need to test it out though. Probably, the menu gets filled the second time around, as the variable then contains the menu.
I need to boot into my VM for this, but I will try to take a look at this today.
Patrick
Patrick Boom said
Hi Evgeny,
Took a look at the code. And indeed, you are right. The menu gets populated on the second time around. The first time, due to the synchronous order, the properties.PopulationXml is assigned an empty field. The scond time around, the variable is populated and assigned.
The ways to get around it though. I did it the easy and dirty way, that is, providing a while loop checking for the menuXMl field to not be empty. You can also do it by using AJAX techniques, which would be the better way.
Chuck said
To get past the looping issue I wrote the dynamic menu options (XML) on the server site, pre_render event and then assigned them to the javascript variable. It still requires the callback event handler but it is very fast. I am still working out one javascript error I get because one of my .js files doesn’t always get downloaded but I have not been able to attribute that issue to this sample. None the less it is a good demonstration and provides a way to build a dynamic flyout menu using the ribbon bar.
Patrick Boom said
Chuck, thanks for your feedback. Given the amount of reactions on this post, I guess a lot of peope are interested and working on this. I you could sent me your easy workaround, it would be much appreciated!
Thanks for visiting my blog!
Chuck said
Hey Patrick, send me your email via email and I can send over my code. I would post in here but it will not look very good.
Patrick Boom said
Chuck, you can send my an email by clicking my picture on the homepage
Thanks!
Saikiran said
Hi Patrik
I followed the steps as said by you. But I am observing a weird issue i.e. some times PageComponent.js file is not loaded into the browser and ribbon button gets disabled if I do a browser refresh at this point I am able to see the buttons. I am able to see this issue quite frequently. I came to conclusion that PageComponent is not laoded because I included an error in the JS file and executed the code, then ribbon buttons got disabled. So I feel some times this PageComponent.js file is not being loaded.
My question is do you have any idea where in this PageComponent.js may not load. This is a generic question, but pls provide some wide guesses.
Thanks!
Patrick Boom said
Sorry, hard to say where it goes wrong. The component is generic and does not contain any specific actions.
I would take take a look at the canhandleCommand and handleCommand methods to see if there are typos in there. Same goes for the registration of the namespace at the bottom.
Anwarali Hajwane said
Hi Saikiran,
I am facing same issue for PageComponent.js, what you are. It would be great, if you can share the resolution, if you have resolved it.
FYI – This issue is not noted in FireFox/Chrome, but in IE.
Regards,
Anwar
Anwarali Hajwane said
FYI-
To resolve the PageComponent.js issue, modified the “getGlobalCommands” function.
We replaced “return getGlobalCommands();” with following (for e.g.) :
return ['PopulateDymamicMenuQueryCommand1', 'PopulateDymamicMenuQueryCommand2', 'PopulateDymamicMenuQueryCommand3'];
This means we hard-coded the command names, which we were expecting to get dynamically generated. We found that the issue is intermittent and probably some dependant JavaScript file doesn’t seem to load internally. We are not very sure of the cause though. But, this worked in our case.
Hope this help others in need.
Regards,
Anwar
Peter said
Hi,
Have you been able to implement multiple tabs?
I have been trying
…
…
This is not working, I even tried making both tabs available, still the second tab is not showing
Also tried
…
…
Peter said
CommandUIDefinition Location=“Ribbon.Tabs._children“
Tab Id=”1″
Tab Id=”2″
then tried
CommandUIDefinition Location=“Ribbon.Tabs._children“
Tabs Id=”Tabs1″
Tab Id=”1″
Tab Id=”2″
Divyesh Kotadia said
Hi Patrick,
I have used your sample as reference for creating menu for my sample site. I understood most of code except one thing i.e.
“arguments[2]“. What exactly is this and from where we are getting this? It would be great if you can elaborate more on this.
Thomas Scharrer said
Hi Patrick,
really good example but I have a problem which I cannot figure out. The tab and the group is shown on my SharePoint site but the buttons are not. There are multiple postbacks to my site when I analyze the behavior with the development tool and therefor the browser will crash.
Do you know what can be wrong. I just put the project and deployed it with success to my SharePoint 2010.
Greetings
Thomas
womens clothes sale said
[...] [...]
Dollie said
Hmm is anyone else encountering problems with the images on
this blog loading? I’m trying to determine if its a problem on my end or if it’s the blog.
Any feedback would be greatly appreciated.
Patrick Boom said
It would be a first to me. I have not heard of problems before.
Patrick
create a blog said
Asking questions are genuinely nice thing if you are not understanding anything totally, however
this article gives nice understanding yet.
Patrick Boom said
Glad it could be of use to you! Thanks for your interest.
Patrick
Ethel said
I was curious if you ever thought of changing the structure of
your blog? Its very well written; I love what youve got to say.
But maybe you could a little more in the way of content so people
could connect with it better. Youve got an awful lot of text for only having 1 or two pictures.
Maybe you could space it out better?