Saturday, January 23, 2010

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.

13 comments:

  1. excellent tutorial series.
    Keep it up

    ReplyDelete
  2. Looks nice but I dont get this part:
    'You can mention the JavaScript function name to Enable Script attribute while declaring the Ribbon XML element.'

    Where or how exactly?

    ReplyDelete
  3. I want to add a button into ViewFormat group, but it did not work correctly. what's wrong in my code?


    [CustomAction
    Id="Ribbon.MyTab"
    Title="Adds a custom button into List"
    RegistrationType="List"
    RegistrationId="104"
    Location="CommandUI.Ribbon"
    ]
    [CommandUIExtension]
    [CommandUIDefinitions]
    [CommandUIDefinition
    Location="Ribbon.ListItem.ViewFormat.Controls._children"]

    [Button
    Id="Ribbon.ListItem.Scorecard.New"
    Alt="Ribbon.MyTab.MyGroup.Button"
    Command="Ribbon.MyTab.MyGroup.Button_CMD"
    Image16by16="~site/Shared Documents/ServerRibbon1/images/Icon16x16.gif"
    Image32by32="~site/Shared Documents/ServerRibbon1/images/Icon32x32.gif"
    LabelText="Button"
    Sequence="10"
    TemplateAlias="o1"
    ToolTipTitle="Button"
    ToolTipDescription="ok" /]


    [/CommandUIDefinition]
    [CommandUIDefinition
    Location="Ribbon.Templates._children"]
    GroupTemplate Id="Ribbon.Templates.MyTab.MyGroup.CustomTemplate"]

    [Layout Title="LargeMedium"]
    OverflowSection Type="OneRow" TemplateAlias="o1" DisplayMode="Large"/]
    OverflowSection Type="ThreeRow" TemplateAlias="o2" DisplayMode="Medium"/]
    /Layout]

    [/GroupTemplate]
    [/CommandUIDefinition>
    [/CommandUIDefinitions]

    [CommandUIHandlers]

    [CommandUIHandler
    Command="Ribbon.MyTab.MyGroup.Button_CMD"
    CommandAction="javascript:alerrt('ok');" /]


    [/CommandUIHandlers]
    [/CommandUIExtension]
    [/CustomAction]


    Please help me correct them.

    ReplyDelete
  4. Thanks a million for this post. You saved my day. Great post.
    Initially I could not get the code working as I was always getting the var ctxCur = GetCurrentCtx(); as null always. However, I modified the code to my requirement and now it works like a charm..
    Thanks again..

    ReplyDelete
    Replies
    1. Hi vijay ... Im trying to work with an ExternalList and one field type:string but doesnt works ... could you help me pls

      Delete
  5. Excelent articles. To bad the source code "link" under the articles are all not functioning. I would realy like to see the complete examples.

    Also in the further articles table which you so conviently placed under ever article part V cannot be clicked

    ReplyDelete
  6. Hello makarand Kulkarni,

    Is it possible to show/hide the Ribbon.ContextualTabs or Ribbon.Tabs completely conditionally?

    Kind regards,
    m.vandeneijnde@on-l-i-n-e.com

    ReplyDelete
  7. Hi Makarand,

    I need your help on Ribbons. I have Delete button on ribbon of document library, I want to overide that delete button funtionality. can you please guide me on this.
    If you dont mind can you please give me your email id so that I will send you my issue with proper screen shot.

    Thanks & Regards,
    Sachin Jagtap

    ReplyDelete
  8. Hi Makarand,

    Great post.Thanks for sharing.

    Thanks & Regards
    Raghavendra Shanbhag

    ReplyDelete
  9. hi Makarand
    is it possible modify state (hidden or visible) of existing buttons like the Modify button?
    Thanks for your article and for the support...

    ReplyDelete
  10. hi
    I want to do some Javascript actions on Ribben Save button, how can i do this?

    ReplyDelete
  11. hi makarand i trying to do same but with an external list with a type:string field but doesnt work ... some comment abt it?
    someone cans help me! pls

    ReplyDelete
  12. Hello, I love your posts; they are very helpful t me and my team. I have a few questions. Scenerio: We have created a list with some 40 fields, then we have create 6 content types and added the appropriate list fields to the specific content type. We have customized the edit form to show a customized view of another form. On the edit form we are showing the content type field and we realize that if a user starts filling out the content on the edit form and then clicks to change the content type without saving the new data, he or she will loose the prevoiusly typed information. We are tryping to figure out how to save the new data entered in the form when the user clicks to change the content type. Any thoughts on how to do that? The only thing we have decided to do is when the user clicks the control he or she will get a pop-up warning.

    ReplyDelete