A common scenario with Variations is to customize the variation label menu which allows to switch between different labels by using (e.g.) a Repeater or DataList control which is bound to a VariationDataSource.
You can achieve this easily by either writing a custom user control or by modifying the existing VariationsLabelMenu.ascx.
Code similar to the following would do the job:
<%@Assembly Name=“Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c”%>
<%@Register TagPrefix=“CMS” Assembly=“Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” namespace=“Microsoft.SharePoint.Publishing.WebControls”%>
<cms:VariationDataSource id=“LabelMenuDataSource” LabelMenuConfiguration=“1” Filter=“^\w” runat=“server”/>
<asp:Repeater ID=“Repeater1” runat=“server” DataSourceID=“LabelMenuDataSource” EnableViewState=“False”>
<ItemTemplate> <a href=“<%# DataBinder.Eval(Container.DataItem, “NavigateUrl“) %>”>
<%# DataBinder.Eval(Container.DataItem, “DisplayText”) %></a> -
</ItemTemplate>
</asp:Repeater>
A sample for the DataList control can be found in the following MSDN article.
This method works pefectly fine – till you try to View or Edit the page properties of the page. These two actions invoke the DispForm.aspx and EditForm.aspx pages in the Forms folder of the Pages library.
As soon as the master page contains the above listed user control you will see the following error:
Server Error in ‘/’ Application.
|
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below. |
Stack Trace:
[NullReferenceException: Object reference not set to an instance of an object.] |
Version Information: Microsoft .NET Framework Version:2.0.50727.4206; ASP.NET Version:2.0.50727.4209
The root cause for this problem is that the VariationDataSource expects to be executed in context of a PublishingPage – and the DispForm.aspx and EditForm.aspx are not publishing pages.
To ensure that the problem does not occur we have to ensure that the VariationDataSource and the controls which use it are not added to the controls collection of the page if the page is not a PublishingPage. The easiest way to achieve this is to write a custom container control which removes all child controls if the container control is not rendered in context of a Publishing Page.
As I did not want to create a container control from scratch I used the ASP.NET System.Web.UI.WebControls.PlaceHolder class as a base class for my container control as it already provides the required functionality – except that it always renders all child controls.
To add the required logic to remove the child controls if the control is on a non-PublishingPage I overloaded the AddedControl method which is invoked whenever a new child control is added to the container. This also includes child controls configured directly in the ascx file.
Below is the complete source code for the container control:
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
namespace StefanG.Containers
{
public class PublishingPageContainer : PlaceHolder
{
protected override void AddedControl(Control control, int index)
{
base.AddedControl(control, index);
/* SPContext not for a publishing page – clear child control collection */
if (!(SPContext.Current.Item is SPListItem) ||
!(PublishingPage.IsPublishingPage(SPContext.Current.Item as SPListItem)))
{
this.Controls.Clear();
return;
}
/* PublishingPageContainer not on a publishing page – clear child control collection */
string listItemPath = HttpContext.Current.Request.Url.AbsolutePath;
SPListItem item = null;
{
item = SPContext.Current.Web.GetListItem(HttpContext.Current.Request.Url.AbsolutePath);
}
catch { }
if ((item == null) ||
!PublishingPage.IsPublishingPage(item))
{
this.Controls.Clear();
return;
}
}
}
}
To use this control it is required to strong name the assembly and add it to the global assembly cache (GAC).
In addiition it is required to add the control to the SafeControls list in the web.config of the web application.
The last step is to update the user control which renders the variation label menu by embedding the variation specific logic inside our new container control:
<%@Assembly Name=“Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c”%>
<%@Register TagPrefix=“CMS” Assembly=“Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” namespace=“Microsoft.SharePoint.Publishing.WebControls”%>
<%@Register TagPrefix=“SG” Assembly=“PublishingPageContainer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8581cb84480ab21f” namespace=“StefanG.Containers”%>
<SG:PublishingPageContainer runat=“server”>
<cms:VariationDataSource id=“LabelMenuDataSource” LabelMenuConfiguration=“1” Filter=“^\w” runat=“server”/>
<asp:Repeater ID=“Repeater1” runat=“server” DataSourceID=“LabelMenuDataSource” EnableViewState=“False”>
<ItemTemplate><a href=“<%# DataBinder.Eval(Container.DataItem, “NavigateUrl“) %>”>
<%# DataBinder.Eval(Container.DataItem, “DisplayText”) %></a>
</ItemTemplate>
<SeparatorTemplate> | </SeparatorTemplate>
</asp:Repeater>
</SG:PublishingPageContainer>
You can download the source code and the user control from the following location:
Permalink
It helped me a lot! Thanks Stefan!