Stefan Goßner

Senior Escalation Engineer for SharePoint (WSS, SPS, MOSS, SP2010) and MCMS

Common Problem: NullReferenceException when using the VariationDataSource on a SharePoint master page

Common Problem: NullReferenceException when using the VariationDataSource on a SharePoint master page

  • Comments 1
  • Likes

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:

<%@ Control Language="C#" %>
<%@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> -&nbsp;
</ItemTemplate>
<SeparatorTemplate> | </SeparatorTemplate>
</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.

Object reference not set to an instance of an object.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error:

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.]
Microsoft.SharePoint.Publishing.<>c__DisplayClass2d.<GetGroupedPageUrls>b__2a() +385
Microsoft.SharePoint.SPSecurity.CodeToRunElevatedWrapper(Object state) +72
Microsoft.SharePoint.<>c__DisplayClass4.<RunWithElevatedPrivileges>b__2() +688
Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode) +308
Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param) +764
Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode) +135
Microsoft.SharePoint.Publishing.PublishingPage.GetGroupedPageUrls(String siteUrl, String pageUrl, String groupId) +330
Microsoft.SharePoint.Publishing.WebControls.VariationEnumerable.System.Collections.IEnumerable.GetEnumerator() +1301
System.Web.UI.WebControls.Repeater.CreateControlHierarchy(Boolean useDataSource) +438
System.Web.UI.WebControls.Repeater.OnDataBinding(EventArgs e) +68
System.Web.UI.WebControls.Repeater.EnsureDataBound() +65
System.Web.UI.WebControls.Repeater.OnPreRender(EventArgs e) +20
System.Web.UI.Control.PreRenderRecursiveInternal() +147
System.Web.UI.Control.PreRenderRecursiveInternal() +255
System.Web.UI.Control.PreRenderRecursiveInternal() +255
System.Web.UI.Control.PreRenderRecursiveInternal() +255
System.Web.UI.Control.PreRenderRecursiveInternal() +255
System.Web.UI.Control.PreRenderRecursiveInternal() +255
System.Web.UI.Control.PreRenderRecursiveInternal() +255
System.Web.UI.Control.PreRenderRecursiveInternal() +255
System.Web.UI.Control.PreRenderRecursiveInternal() +255
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3548

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;
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;
            try
            {
                item = SPContext.Current.Web.GetListItem(HttpContext.Current.Request.Url.AbsolutePath);
            }
            catch { }

            if ((item == null) ||
                !(item is SPListItem) ||
                !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:

<%@ Control Language="C#" %>
<%@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:

Comments
  • It helped me a lot! Thanks Stefan!

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
Raw Html Fix