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 :)