Saturday, January 23, 2010

SharePoint 2010 Ribbon Customization Part V: How to dynamically populate ribbon controls?

In this article I will show you how we can populate ribbon controls on fly. Ribbon controls like combobox, dropdown, flyoutanchor, splitbutton, mrusplitbutton contains menuitems or they can host other controls like button with individual command and act like a menu. So their structure can be populates at design time in XML declaration as well as at runtime using javascript and code behind.

I will cover how we can populate the FlyoutAnchor control with its child elements on fly.

I am going to use a custom PageComponent, Delegate Control and JavaScript file. To create PageComponent and Delegate control please refer my previous articles PartIII and PartIV.

Here is XML declaration for FlyoutAnchor, I have dropped the other xml declaration to keep it simple. In this declaration you will notice that we have three new properties PopulateDynamically="true", PopulateOnlyOnce="true", PopulateQueryCommand="GetDynamicNewMenuXml". The important one is PopulateQueryCommand, this command gets executed when ribbon starts building this control. Within this event we will retrieve the required items which we want to show in FlyoutAnchor. Let's see how?

Let's look into our PageComponent, When PageComponent gets called as a result of PopulateQueryCommand in action, in handleCommand function one of its input parameter is "properties". This has one method PopulationXML which takes the items declaration in the form of XML. When Ribbon found that this control has been set for dynamic population then it uses XML assigned to its properties.
On PopulateQueryCommand command we are calling GetDynamicNewMenuXmlFunction JavaScript function which will generates and returns the XML for menu.

Here is GetDynamicNewMenuXmlFunction() code,
The typical format of XML is ,
For this example I have create menu string in JavaScript. You can generate it on fly in code behind and then inject it into page. Use Delegate Control for this purpose.

I hope this will help you.

SharePoint 2010 Ribbon Customization Part VI: How to enable/disable buttons in ribbon conditionally?


In this article I will cover how we can enable and disable the controls from ribbon based on any specific column value. For an instance I have a list column "State", if selected row is having status "active" then and then button should be enable in Ribbon, otherwise ribbon button should remain disable.

For how to write Ribbon declaration please refer my previous articles.

In above image you can see we have one button "New" in ribbon within group "Console". Please refer the source code for Ribbon's declaration. We are going to use delegate control for injecting ECMA script on fly for desired list.
Below are the events causes ribbons to refresh so button can be turned enable/disable.
  • Checkbox from every row
  • SelectAll/DeselectAll checkbox from header row
  • On every row select
On each of above events after executing their respective functionality they instruct the ribbon to refresh. On received of refresh event ribbon calls Enable script for each of its control. Enable Script is the attribute which points to JavaScript function which can contain the logic for deciding button to be enabled or disable. You can mention the JavaScript function name to Enable Script attribute while declaring the Ribbon XML element.
Now let's see how sample is going to work.
  • On page loads and when no row is selected button will be disable
  • When user clicks checkbox of row and that row is having "Active" as status value then button will get enable
  • When user clicks row and that row is having "Active" as status value then button will get enable
  • If row is selected and button is enable and user click select all checkbox then ribbon will get refresh and button will be disable
So to achieve required functionality what we need is,
  • A function which will return a Boolean flag for ribbon to enable/disable button
  • We need to attach our own events on row click, row checkbox click and header checkbox click
  • So in our own functions we will retrieve the row and it's all cell values
  • Based on status cell value we will set the global flag true/false.
  • Then we will call Ribbon refresh, which eventually will enable/disable the button
Let's see how we can attach our own functions to various click events,

This is one of the core functions from this implementation. The final HTML of any ListViewWebPart is table and its id is always in the form of {ListGuid}-{WebPartGuid}. So this function retrieves all Html tables from the page and hooks the exact table based on ListGuid. To compare Table id for list GUID we know in advance the list GUID.

How we can do that?

Buy looking into current context. Whenever page gets rendered current SPContext gets injected into page. That can be desterilize into JavaScript object by calling GetCurrentCtx() function. You can find this function in Core.js file. The context is having all information like current list, web application information, selected row items and so on. You can inspect contains of context by doing client side debugging. [Debugging through IE and Visual Studio is the handiest approach for this]

Finally once you get the exact table then iterate through all its row and cell and attach the click events of Checkbox on each row, row and Checkbox of header row. So now we have function rowClicked for every row select, function rowCbxClicked for every checkbox of each row and selectAllClicked for header row's checkbox.

In selectAllClicked() function we are setting _isButtonEnable to false because whenever Select/Deselect all checkbox gets clicked we want our button should be disable. One we done with this we will call RefreshCommandUI(), this function again an intrinsic function from Microsoft's Js file suite. You can find this function in Core.Js file. This function causes Ribbon to refresh herself, so eventually all EnableScript functions of ribbon buttons get called and hence our isButtonEnablefn gets called. Which just return the _isButtonEnable. Since we have set this variables value to false our button gets disable.

Now you have got an idea how to enable and disable ribbon button. J It's really fun to work with Ribbon.

Now let's see what we will do in rowClicked & rowCbxClicked ,
  • First retrieve the element that causes the event.
  • Then check whether that element contains any link by calling another intrinsic function ElementContainsLink(). If that element contains the link it means you have select the cell value which is having Hyperlink so we shouldn't do anything just allow Hyperlink to do its own work so we will return from that point.
  • Then we will close the ECB menu if that is open on any of earlier action.
  • Then we will call again an intrinsic function GetItemRow() to get item row which causes this event. We need this row for our other calculations Why? We will see that in our next steps.
  • Now time to get current context, so we will call an intrinsic function CtxFromRow(), which will return us the current client context.
  • As I told you earlier this client context keeps all information about current spcontext. Along with that it also keeps the information about selected rows. context.dictSel is the
    array which holds all selected row item information. There are two intrinsic functions CountSelectedItems & GetSelectedItemsDict which return the selected item count and selected items respectively.
  • At this point we have row which cause the event and selected item count. So if selected count is 1, then and then we will check the row's State value if that is "Active" then we will set the _isButtonEnable flag to true else we will set it to false.
  • Now we will call RefreshCommandUI() which will fire the ribbon refresh event that causes to each ribbon button to fire their own EnableScript function. In our case that one is isButtonEnablefn.
  • As we have set the flag true or false based on our business logic. The button will get enable or disable based upon the value of _isButtonEnable flag.
  • All intrinsic functions I have used in this method you will find that into Core.JS file.
Note: Please create a custom list, add choice column with "Active" & "Cancel" values as i have shown in image above. So you can play with sample.

I hope this will help you.

Saturday, January 16, 2010

SharePoint 2010 Ribbon Customization Part IV: How to add PostBack commands to ribbon?


In this article I will cover how we can add commands to the ribbon which raises PostBack events.
I have add a new Button "New" within group "Console" on Items Tab. Below is the XML declaration for it,
If you observe above XML carefully, then you will find that I haven't declared the CommandUIHandler element. So events will not be raised for this Control. If you deploy it as it is then you will see the Button is disable.
What we want to do is, we want a command for this button which will raise the events and that will PostBack the page. So let's add the Command Handler for that. To add Custom ECMA Script we have two options,
  • Override the delegate control on the page: AdditionPageHead delegate control can be used to insert custom ECMA script on page. This is the best customizable options we have. Wherein you can choose the desired list for which you want to apply this customization.
  • Put a web part on the page: This option is good when we have new/display and edit page for list or library or a web part page.
We will go with Delegate Control.
So let's add a user control "RibbonDelegate" to Solution and insert XML declaration. Note the Control element in above xml declaration, we have instructed the "AdditionalPageHead" to host our Control.
In "RibbonDelegate" control, We have four types of command for ribbon SPRibbonPopulateQueryCommand, SPRibbonQueryCommand, SPRibbonCommand, SPRibbonPostBackCommand . We will use only SPRibbonCommand and SPRibbonPostbackCommand rest we will see in my next article where using this commands we will populate controls on fly.
  • As usual for Group we need command with no action. So I have taken SPRibbonCommand with second parameter empty. In case if you interested to fire any JavaScript function then second parameter of this object will be name of that JavaScript function.
  • For New button we need to PostBack, so we will use SPRibbonPostBackCommand. When we use this object at the same time we also need to implement IPostBackEventHandler interface. So we can handle the post back event and rout the system flow to post backed method. Note that "NewScorecard" is the Post back command which we will use while handling PostBack event.
  • SPRibbonScriptManager registers the JavaScript delegate functions for PageComponent. Second parameters of its methods are the name of JavaScript functions which we have mentioned in its Page Component. Later on we will see PageComponent in detail.
Now we are done with command declarations and Page Components. Now we need a PageComponent which will establish communication between Ribbon and Page. So let's go and declare the PageComponent.
Refer the SP.Ribbon.Custom.UI.js file in sample code.
Is PageComponent making you more confuse? So let's see how Ribbon communicates with page.
  • There is always a single instance of a PageManager for every page. It is defined in CUI.js file you can inspect the CUI.js file for CUI.Page.PageManager. Who holds the FocuManager(CUI.Page.FocusManager) , CommandDispatcher (CUI.Page.CommandDispatcher), UndoManager(CUI.Page.UndoManager) and other supporting objects.
  • CommandDispatcher registers all commands for ribbon and keeps the track of PageComponent. When command receive to PageManager, PageManager handovers the command to CommandDispatcher then CommandDispatcher routs the command to appropriate PageComponent.
  • PageComponent is the one who has actual implementation of commands. This is inherited by CUI.PageComponent. So Custom PageComponent should always follow the same class structure as CUI.PageComponent has.
Let's see the system flow of Button/command click,
First of all our PageComponent is registered with PageManager. PageManager has two methods addPageComponent() and removePageComponent() which takes care of registration of page component. All OOB page components you can find into SP.Ribbon.js file.

Button we used is of type "CUI.Controls.Button" and its base class is "CUI.Control". You can find declaration for all Ribbon controls into CUI.js file. This file has all classes used to construct the Ribbon.
  • User clicks on New Button.
  • OnClick event Button access its parent component, in this case it is a Group which is a type of "CUI.Component"
  • Then it calls the components raiseCommandEvent() method and then this method propagate the event to Tab and finally tab handovers this command with all required command information to PageManager.
  • Once PageManager receives the command, he passes that to CommandDispatcher along with registered PageComponents
  • CommandDispatcher then pick up appropriate PageComponent using sequence number and pass that command to PageComponent
  • PageComponent then calls method attached to command.
If everything sounds confusing then leave it. Just keep in mind that we always need a PageComponent whenever we declare commands using code behind.
So now we have defined the PageComponent for us lets register it. Click event of New Button causes page to postback, when page gets postback our delegate controls gets loaded and there we handle the post back event. Handling post back event in user control is the same as we are doing with plain vanilla asp.net pages.

Once you define a PageComponent, then you don't need to define it every time you create a new ribbon tab or control. You can use the same PageComponent across your application. All this looks very complicated at first time but once you become familiar with it you will feel very easy to have custom actions into ribbon for your application.

Few Important JS files: SP.Ribbon.js, SP.Core.js, CUI.js, Core.js

I hope this will help you all.

Saturday, January 9, 2010

SharePoint 2010 Ribbon Customization Part III: How to add custom Contextual tabs into Ribbon?

In This Article I will cover how we can have custom contextual group tabs at various locations.

In above image you can see,
  • A new custom contextual Tab “aQura” with tab “2W Series”
  • A custom tab “2W Series” has been added to default List’s contextual Tab “List Tools”
  • A single tab “aQura” with contextual tab has been added to list view
So we can move custom tab at various locations on form. Let’s see how?

Custom Contextual Tab:

Behind the scene, CUI.RibbonBuilder and CUI.RibbonBuildOptions classes are responsible for initializing Ribbon on client side. After you deploy above configuration you won’t find any custom Contextual tab for your list. The reason is CUI.RibbonBuildOptions doesn’t populate its shownTabs & initiallyVisibleContextualGroups properties with proper values. They populated with default values. You can check these properties through page’s view source. How we can instruct ribbon that, this is a new Contextual Tab and you should show whenever page gets rendered? To do so we need to access Ribbon control from the context. Once we get the Object of current ribbon then we will set the New Contextual Tab and Its all child tabs.
To do so we have two options,
  • Override a delegate control on the page.
  • Put a Web Part on the page.
Replacing a delegate should be used when working with a document library or list. The AdditionalPageHead delegate can be used to host our custom user control. Within that user control, we will access the current ribbon object. Web Parts can be placed on the page and on load of that web part we can easily access the ribbon object. Up to a great extension this is very handy solution but in case of ListView form we do not have freedom to put webpart on that page. So I prefer Delegate Control.
By adding control element into xml declaration of custom action we can instruct the OOB “AdditionalPageHead” delegate control to host our custom user control.

SPRibbon’s GetCurrent method returns the Ribbon object belonging to the page in context. Then by accessing MakTabAvailable method we are instructing ribbon to insert mention tab while rendering. Then by accessing MakeContextualGroupInitiallyVisible method we are instructing ribbon to make our custom Contextual group visible.

Adding Custom Tab to OOB ContextualTab:

Adding Custom Tab to ListView without any contextual tab:

That’s all, deploy the solution and see the results. You will see the tab has been added to list view form without any contextual tab. What I have changed is Location attribute of Custom Action. “CommandUI.Ribbon” instructs that create a tab but its scope will be global. Also I have removed RegistrationID and RegistrationType attributes. By doing this now I have a global tab which neither bind to any list nor have any predefined location.

Next I need to instruct that while rendering my list, Ribbon should have this tab. How I can achieve this? By overriding Delegate Control, I have explained how to override Delegate Control in one of the above topic. Below is the code we need into user control.

Note: While working on ribbon customization, keep deleting IE’s temporary files. Otherwise results will be unexpected.

SharePoint 2010 Ribbon Customization Part II: How to Add/Remove/Replace Tab Controls?

In This Article I will cover how we can customize Ribbon Tab controls by adding/removing and replacing control.

OOB ListItem Tab,

This article will show how we can,
  • Add new Button Control to “New” group in above tab
  • Replace the “Delete Item” button with new Button
  • Hide the “WorkFlows” group
  • Add new custom Group with new button
So targeted ListItem should look like,

Add new button to “New” group of ListItem Tab
Replace the OOB “Delete” button with new Button Hide the “WorkFlows” group Add new custom Group with new button

Saturday, January 2, 2010

SharePoint 2010 Ribbon Customization Part I: Architecture of Ribbon

In This article I will cover the architecture of Ribbon control. Tabs appear across the top of the page in SharePoint 2010 sites. Top element of Ribbon is Tab. The Ribbon is defined using xml in a Feature manifest or as a Custom Action.
What Ribbon Contains?
To see ribbon’s internal you need to look into CMDUI.xml file, which you can find at location 14\template\global\xml\cmdui.xml.

Ribbon Control’s skeleton,

Ribbon Objects

CommandUI

Starting point for Ribbon which points to the namespace http://schemas.microsoft.com/sharepoint/"

Ribbon

Represents OOB ribbon control. SharePoint 2010 has only one instance of Ribbon control per page. Ribbon control by default configured with all OOB Contextual Tabs, Tabs and Templates. At runtime ribbon gets trimmed for non-required tabs and shows only relevant tabs. e.g. While rendering any List only “List Tools” contextual tab gets rendered, rest all tabs gets trimmed.

Tabs

It is a collection of all tabs. Its ID is "Ribbon.Tabs". Whenever we want to add new tab to ribbon then we have refer this collection. We have to instruct ribbon to insert custom tab into Tabs collection. To instruct ribbon to insert tab into its collection we need to use Location attribute and have to give value as “Ribbon.Tabs._children”.

Tab

This represents a single tab for a ribbon or Contextual Tabs. We can have custom tab on New/Edit/Display/ListView forms.

Tab acts like a container and contains controls which can raise the events on client side and server side. Following are the child objects of tab.

Scaling: this element decides how the controls get rendered and scale at runtime. It has two more child elements,

·       MaxSize: controls the max size of control on tab.

·       Scale: handles the scaling for controls.

Groups: It is a collection of group(s). We can add custom group to any tab. To do so we need to instruct ribbon to insert defined xml as a groups into tab. Location attribute needs to be properly configured and acceptable value for that is “Ribbon.Tabs.Groups._children”.

Group: It groups the various control into one entity. It has a child element “Controls” which contains the declaration for various controls.  Every Group is binded to Template and this template decides the layout for that group. We can customize the Group elements. We can add, remove and replace the control from a group. For that, we need to configure Location attribute properly. The standard formula is, “Ribbon.[Tab].[Group].Controls_children”. For an instance for List’s ListItem tab if you want to insert new control in its Manage group then the location value would be “Ribbon.ListItem.Manage.Controls._children”.

Controls: Allowable controls in Groups are Button, Checkbox, Color Picker, Combo Box, Drop Down, Flyout Anchor, Label, Menu, Menu Section, MRU Split Button, Spinner, Split Button, Text Box, and Toggle Button. Each of these controls has to tie with command. This command can be execute at client side or server side

ContextualTabs

This holds one or more tabs and defines the context for holding tabs. Its ID is “Ribbon.ContextualTabs”.We can add custom Contextual Tabs and for that we need to configured Location attribute and allowable value is, “Ribbon.ContextualTabs._children”. We can customize the OOB contextual tabs also for an instance if you want to add new tab into List’s Contextual tab then location attribute would be “Ribbon.ListContextualGroup._children”.

Ribbon Templates

It is a collection of all group templates. Its ID is “Ribbon.Templates”. We can add custom group template, to do so, the acceptable value for location attribute is “Ribbon.Templates._children”.

GroupTemplate

Every group from tab requires a template, this template helps ribbon to control the size of controls and scale them at rendering time.

GroupTemplate has following child elements:

 

Layout: Element has LayoutTitle attribute which is used to mention Size attribute of MaxSize and Scale element. [they are the child elements of Scaling Element and scaling element is a child element of a tab]

 

Section: Element has two child elements Alignment & Type. Alignment defines the position of control and Type decides how many rows this section will contain.

 

Row: Element has three child elements ControlRef, OverflowArea and Strip.

·       ControlRef & OverflowArea Elements has two attributes,

o   DisplayMode which controls display of controls. Acceptable values are Small, Medium, Large and Text.

o   TemplateAlias its values is used to mention TemplateAlias of control.

·       Strip: Element contains collection ControlRef elements and they gets displayed as a strip on ribbon.


Custom Tab declaration XML,

Sample Tab on New Form,