Inconvenient conditional Web Parts in SharePoint 2010


Using conditional content you can target pieces of your website to specific audiences optimizing the User Experience. Unfortunately, if you try to conditionally display Web Parts, your pages will break – at least by default because there is a way you can get this done. So how do you conditionally display Web Parts in SharePoint 2010?

Conditional content

The concept of conditionally showing pieces of web pages is not new. Through the years it has been used for many purposes: from showing data that should be visible to authenticated users only, to sophisticated content targeting mechanisms on marketing websites. One of the most recent applications is conditionally hiding content for mobile devices to optimize the user experience and lower the page footprint.

Every Content Management System or web platform has its own approach to conditionally showing content and SharePoint 2010 is not different. From the technical point of view we could distinct between three types of conditional content in SharePoint 2010: blocks of content within the Rich Text Editor, controls and Web Parts. So far I haven’t seen any solutions for conditional content in Rich Text Editor, although it doesn’t mean it wouldn’t be possible. As for controls: I have showed you a working approach in my previous article. What really is challenging is dealing with Web Parts.

Inconvenient Conditional Web Parts

There are quite a few ways in which you could achieve the result of conditionally displaying Web Parts, but probably the easiest approach, from the theoretical and effort point of view, would be to simply wrap the whole Web Part Zone in a conditional control, like the one I have showed you previously, and have all the Web Parts be conditionally displayed based on the logic of the control.

As soon as you try to wrap a Web Part Zone, with Web Parts in it, in a conditional control and then navigate to a page in a scenario that causes that Web Part Zone to be hidden, that page will break!.

Before we start

Before we proceed with wrapping Web Part Zones in conditional control there are a few things that we have to change about it.

If you would try to use the conditional control just the way I showed it previously, you would see the following exception:

ASP.NET exception displayed in Internet Explorer

Because of their lifecycle, Web Part Zones have to be instantiated very early in the page lifecycle. To solve this problem, we have to extend the base conditional control class with an additional call to the EnsureChildControls method:

using System;
using System.Web.UI;

namespace Mavention.SharePoint.MobilePanel.Controls {
    [ParseChildren(true)]
    public abstract class ConditionallyVisibleControl : Control {
        public ITemplate ContentTemplate { get; set; }

        public abstract bool ShouldBeVisible { get; }

        protected override void OnInit(EventArgs e) {
            base.OnInit(e);
            EnsureChildControls();
        }

        protected override void CreateChildControls() {
            base.CreateChildControls();

            if (ShouldBeVisible && ContentTemplate != null) {
                Control container = new Control();
                ContentTemplate.InstantiateIn(container);
                Controls.Add(container);
            }
        }
    }
}

Although adding the call to the EnsureChildControls method prevents the exception from being thrown, it leads us directly to another challenge. In order to be able to add Web Parts to both Web Part Zones, they both have to be visible in the Edit Mode.

Only one Web Part Zone visible in the Edit Mode

We can solve this issues by extending the ConditionallyVisibleControl further:

using System;
using System.Web.UI;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace Mavention.SharePoint.MobilePanel.Controls {
    [ParseChildren(true)]
    public abstract class ConditionallyVisibleControl : Control {
        public ITemplate ContentTemplate { get; set; }

        public abstract bool ShouldBeVisible { get; }

        private bool IsInEditMode {
            get {
                return SPContext.Current != null &&
                    SPContext.Current.FormContext != null &&
                    (SPContext.Current.FormContext.FormMode == SPControlMode.Edit ||
                     SPContext.Current.FormContext.FormMode == SPControlMode.New);
            }
        }

        protected override void OnInit(EventArgs e) {
            base.OnInit(e);
            EnsureChildControls();
        }

        protected override void CreateChildControls() {
            base.CreateChildControls();

            if ((ShouldBeVisible || IsInEditMode) && ContentTemplate != null) {
                Control container = new Control();
                ContentTemplate.InstantiateIn(container);
                Controls.Add(container);
            }
        }
    }
}

With this, if we switch to the Edit Mode, we will see both Web Part Zones and we are ready to perform our test.

Two Web Part Zones visible in Edit Mode

Inconvenient conditional Web Parts continued: the proof

To prove how inconvenient conditional Web Parts are, let’s take a look at the following code snippet that defines two Web Part Zones displayed conditionally:

<Mavention:MobileTrimmedControl runat="server">
    <ContentTemplate>
        <WebPartPages:WebPartZone runat="server" ID="MobileWPZ" Title="Mobile Web Part Zone" />
    </ContentTemplate>
</Mavention:MobileTrimmedControl>
<Mavention:MobileTrimmedControl Not="true" runat="server">
    <ContentTemplate>
        <WebPartPages:WebPartZone runat="server" ID="DesktopWPZ" Title="Desktop Web Part Zone" />
    </ContentTemplate>
</Mavention:MobileTrimmedControl>

Now let’s try to add a Content Editor Web Part: one to each Web Part Zone.

Two Content Editor Web Parts each in a separate Web Part Zone

As soon as we save the Publishing Page, and with that switch to the Display Mode, the logic for conditionally hiding content will be executed and the Mobile Web Part Zone will be hidden. Surprisingly both Content Editor Web Parts will be visible!

Two Content Editor Web Parts visible on a Publishing Page

When we switch back to the Edit Mode, we will see what has just happened.

Two Content Editor Web Parts in the same Web Part Zone

So it’s not our logic that failed! Somehow the Content Editor Web Part from the Mobile Web Part Zone got moved to the Desktop Web Part Zone!

Inconvenient conditional Web Parts – the truth

It turns out, that whenever there are Web Parts associated with a page and they are located in a Web Part Zone that isn’t available while deserializing Web Parts from the Content Database, those Web Parts will either be moved to the first available Web Part Zone or closed, if no Web Part Zone is available on the page. No matter at what stage you hide the Web Part Zone from rendering, if you hide it, it will break the layout of Web Parts on your page.

As we have seen, wrapping a Web Part Zone with Web Parts in it in a conditional control will break Web Parts associations the first time that Web Part Zone is hidden. There is however another way for us to have Web Parts displayed conditionally.

Conditional Web Parts – the workaround

When SharePoint 2010 came out one of the improvements it introduced was the ability to insert Web Part in content – directly in the Rich Text Editor. It turns out that this is not only great for content editors to create rich pages, but it also allows us to have Web Parts displayed conditionally!

To prove that it’s working let’s change our previous code snippet as follows:

<Mavention:MobileTrimmedControl runat="server">
    <ContentTemplate>
        <PublishingWebControls:RichHtmlField FieldName="MobileWebParts" runat="server"/>
    </ContentTemplate>
</Mavention:MobileTrimmedControl>
<Mavention:MobileTrimmedControl Not="true" runat="server">
    <ContentTemplate>
        <PublishingWebControls:RichHtmlField FieldName="DesktopWebParts" runat="server"/>
    </ContentTemplate>
</Mavention:MobileTrimmedControl>

As you can see we have replaced Web Part Zones with two Publishing HTML Fields that I’ve added to the Article Page Content Type.

Once again, if we switch to the Edit Mode we can add two Content Editor Web Parts:

Two Content Editor Web Parts added to a page each in its own Publishing HTML Field

If we now save the Publishing Page and switch over to the Display Mode we will see only the Desktop CEWP just as we wanted!

Desktop Content Editor Web Part displayed on a Publishing Page

To prove that it’s working, we can hack the User Agent string to simulate using a mobile device:

Mobile Content Editor Web Part displayed on a Publishing Page

And to really prove that we have kept all content management capabilities and are still able to manage Web Parts on the page, let’s switch back to the Editing Mode:

Two Content Editor Web Parts added to a page each in its own Publishing HTML Field

As you can see everything works correctly and looks just as we left it!

Why does it work?

The reason why conditionally displaying Web Parts using the Rich Text Editor works has to do with how Web Parts in content are rendered. Behind the scenes, all Web Parts placed in content, no matter in which Publishing HTML Field they are located, are placed in a hidden Web Part Zone called wpz. On every page load, Web Parts are deserialized from the database, put in the wpz Web Part Zone and the RichHtmlField control renders them in the right place in content. Because it’s the RichHtmlField control, and not the Web Part Zone, that is wrapped in a conditional control, the Web Part deserialization process remains untouched and Web Parts are rendered correctly.

Summary

Using conditional content you can target pieces of your website to specific audiences optimizing the User Experience. Unfortunately, if you try to conditionally display Web Parts located in Web Part Zones, your pages will break. By creating separate Publishing HTML fields and adding Web Parts in the content of those fields you can have Web Parts conditionally displayed based on the logic of the conditional control of your choice.

Others found also helpful: