Andrey Markeev on SharePoint

SharePoint Ribbon ToggleButton: creating multiview pages

January 06, 2012

SharePoint Ribbon is a very powerful thing. And today I will explain another very interesting use case for it and will show, how to create multiview application pages and use ribbon to switch between the views.

To understand, what I'm talking about, just remember a standard SharePoint calendar list. It has "Scope" group on its contextual ribbon, and you can switch scopes by toggling the corresponding buttons:

So now I'm going to implement something similar to these switches: create a page with several views, and use ribbon buttons to switch between these views.

Some real-world use cases for this kind of switches may be as follows:

  • Switch between tabular and graphical (i.e. chart or calendar) representations of same data
  • Switch page layouts
  • Filter data
  • Group data (by data periods, departments, regions, etc.)

Thus, as you could have noticed, there are some really valuable usages of this kind of functionality. That's why I reckon, that it would be very important for you, dear reader, to know how to deal with this!

Implementation

For the implementation, I will use SharePoint 2010 Fluent Ribbon API opensource project, which simplifies work with ribbon and could be downloaded naturally from CodePlex.

Warning: The ToggleButton functionality is available in Fluent Ribbon API starting from 1.4 release only!

To prove that all this is really very simple to implement, I will use ribbon in conjunction with ASP.Net server-side MultiView control. Also, since obviously I'd like to avoid complete postbacks, I will use an UpdatePanel to update the page partially. Thus, the solution will be very familiar to ordinary ASP.Net and SharePoint developers, except for the ribbon part, but I promise you, the ribbon part is not so complicated too.

Ok, let's start!

First of all, I will need a SharePoint project in Visual Studio, no matter new or existent one, and obviously I will create an application page there:

Very well, now I will inherit this application page from the RibbonLayoutsPage class, which is from FluentRibbon.dll, hence the FluentRibbon.dll should be referenced and added to the GAC deployment list ('Advanced' tab in Package editor).

The RibbonLayoutsPage class has single abstract method, required to implement, called GetTabDefinition. This method must return initialized TabDefinition class instance. All the definition-kind classes in Fluent Ribbon API are well-documented, so I hope it will not be hard for you guys to create your own custom TabDefinition. And here is what I've got:

return new TabDefinition()
{
    Id = "Tab1",
    Title = "Toggle",
    Groups = new GroupDefinition[]
    {
        new GroupDefinition()
        {
            Id = "Group1",
            Title = "Switch view",
            Template = GroupTemplateLibrary.SimpleTemplate,
            Controls = new ControlDefinition[]
            {
                new ToggleButtonDefinition()
                {
                    Id = "View1",
                    Title = "View 1",
                    Image = ImageLibrary.GetStandardImage(6,3)
                },
                new ToggleButtonDefinition()
                {
                    Id = "View2",
                    Title = "View 2",
                    Image = ImageLibrary.GetStandardImage(6,3)
                },
                new ToggleButtonDefinition()
                {
                    Id = "View3",
                    Title = "View 3",
                    Image = ImageLibrary.GetStandardImage(6,3)
                }
            }
        }
    }
};

As you see, all is pretty obvious (you can read about standard images here). However, this definition is not complete: some javascript code is missed. I will return to that a bit later.

The next thing I'm going to do now is creating the ASPX markup. I will need UpdatePanel and simple MultiView with 3 views for demonstration:

<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">

    <asp:UpdatePanel ID="MultiViewUpdatePanel" UpdateMode="Always" runat="server">
        <ContentTemplate>
            <asp:MultiView ID="MultiView" runat="server">
                <asp:View ID="View1" runat="server">
                    view1
                </asp:View>
                <asp:View ID="View2" runat="server">
                    view2
                </asp:View>
                <asp:View ID="View3" runat="server">
                    view3
                </asp:View>
            </asp:MultiView>
        </ContentTemplate>
    </asp:UpdatePanel>
</asp:Content>

Very well, and now I need to write a very simple method, which will switch the MultiView control to particular view:

private void SwitchView(int viewNumber)
{
    switch (viewNumber)
    {
        case 1:
            MultiView.SetActiveView(View1);
            break;
        case 2:
            MultiView.SetActiveView(View2);
            break;
        case 3:
            MultiView.SetActiveView(View3);
            break;
    }
}

By default (if not postback), let's assume that the first view must be selected:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        SwitchView(1);
    }
}

Ok, now all is ready to integrate our ribbon with the MultiView. Obviously, when a ribbon ToggleButton is pressed, we will need to perform a partial postback with argument, which will point to particular view.

To make the postback partial, it must be performed on behalf of the MultiViewUpdatePanel control. To get the corresponding client code for performing such postback, the GetPostBackEventReference method can be used.

Finally, I've got the following code:

var updatePanelScript = Page.ClientScript.GetPostBackEventReference(MultiViewUpdatePanel, "{0}") + ";";

Here I'm passing "{0} " placeholder instead of particular value, because I have 3 different toggle buttons.

To call javascript when ribbon button is pressed, the CommandJavaScript property of each ToggleButtonDefinition must be initialized. To differentiate the buttons, I will replace "{0}" placeholder with values 1, 2 and 3 respectively.

So, for instance the first ToggleButtonDefinition will look like this:

new ToggleButtonDefinition()
{
    Id = "View1",
    Title = "View 1",
    Image = ImageLibrary.GetStandardImage(6,3),
    CommandJavaScript = String.Format(updatePanelScript, 1)
},

And to handle the postback, I will need to slightly improve the Page_Load method:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        SwitchView(1);
    }
    else
    {
        var eventArgument = Request.Form["__EVENTARGUMENT"];
        int viewNumber;
        if (Int32.TryParse(eventArgument, out viewNumber))
            SwitchView(viewNumber);
    }
}

Very good, challenge is almost complete, and only one last thing is left.

The point is, that SharePoint ToggleButton ribbon control is not very handy yet. And by default, it doesn't even know in which state it is. The state management has to be done manually, and the current state must be provided through the InitialValueJavaScript property of ToggleButtonDefinition class.

Obviously, for storing this state I will need some global js variable. It has to be changed when the button is pressed, and it's value has to be returned in InitialValueJavaScript.

The corresponding code for the first ToggleButton will look like this:

new ToggleButtonDefinition()
{
    Id = "View1",
    Title = "View 1",
    Image = ImageLibrary.GetStandardImage(6,3),
    InitialValueJavaScript = "return (window.my_CurrentView || 1) == 1",
    CommandJavaScript = "window.my_CurrentView = 1;" + String.Format(updatePanelScript, 1)
},

And unfortunately, this is still not all! The last piece of SharePoint magic is needed to make it work.

The point here is that SharePoint will call initial value js code only for the pressed button. But since we have the toggle group, we need also to update all other toggle buttons. For this purpose, special ApplicationStateChanged command must be executed. This can be done with the following javascript code:

SP.Ribbon.PageManager.get_instance().get_commandDispatcher().executeCommand(Commands.CommandIds.ApplicationStateChanged, null);

Thus, we have to add this call to every ToggleButton. The final version of the GetTabDefinition method will look following way:

public override TabDefinition GetTabDefinition()
{
    var updatePanelScript = Page.ClientScript.GetPostBackEventReference(MultiViewUpdatePanel, "{0}") + ";";
    var ribbonRefreshScript = "SP.Ribbon.PageManager.get_instance().get_commandDispatcher().executeCommand(Commands.CommandIds.ApplicationStateChanged, null);";
    return new TabDefinition()
    {
        Id = "Tab1",
        Title = "Toggle",
        Groups = new GroupDefinition[]
        {
            new GroupDefinition()
            {
                Id = "Group1",
                Title = "Switch view",
                Template = GroupTemplateLibrary.SimpleTemplate,
                Controls = new ControlDefinition[]
                {
                    new ToggleButtonDefinition()
                    {
                        Id = "View1",
                        Title = "View 1",
                        Image = ImageLibrary.GetStandardImage(6,3),
                        InitialValueJavaScript = "return (window.fr_CurrentView || 1) == 1",
                        CommandJavaScript = "window.fr_CurrentView = 1;" + ribbonRefreshScript + String.Format(updatePanelScript, 1)
                    },
                    new ToggleButtonDefinition()
                    {
                        Id = "View2",
                        Title = "View 2",
                        Image = ImageLibrary.GetStandardImage(6,3),
                        InitialValueJavaScript = "return (window.fr_CurrentView || 1) == 2",
                        CommandJavaScript = "window.fr_CurrentView = 2;" + ribbonRefreshScript + String.Format(updatePanelScript, 2)
                    },
                    new ToggleButtonDefinition()
                    {
                        Id = "View3",
                        Title = "View 3",
                        Image = ImageLibrary.GetStandardImage(6,3),
                        InitialValueJavaScript = "return (window.fr_CurrentView || 1) == 3",
                        CommandJavaScript = "window.fr_CurrentView = 3;" + ribbonRefreshScript + String.Format(updatePanelScript, 3)
                    }
                }
            }
        }
    };
}

Wow, at last we can deploy the project and marvel the desired result :)

Like it?

Posts of this series

  1. Add ribbon tab to all site pages using Fluent Ribbon API
  2. SharePoint Ribbon ToggleButton: creating multiview pages (this post)

About me

I'm SharePoint MVP, published author, frequent speaker, opensource projects creator and online expert.

I'm one of the top 10 experts on SharePoint StackExchange:

profile for omlin at SharePoint, Q&A for SharePoint enthusiasts

You can learn more about me on LinkedIn and Facebook.

Want to follow?

Subscribe to RSS!

Twitter

Blog archive