Wait, There’s No Code-Behind File for a SharePoint Master Page?

How Hard Can It Be?I recently had a situation where our SharePoint 2010 site’s master page was emitting an empty DIV element that ruined the layout of several pages in the site. Although largely extraneous on most pages, the DIV and it’s child ContentPlaceholder control was required by a handful of pages on the site, so we couldn’t just delete it.

As an ASP.NET developer still relatively new to SharePoint, I thought that a simple fix would be to add code to the master page to suppress the errant DIV on pages where the placeholder wasn’t used. How hard can it be?

Server-side

I created a standard ASP.NET test site with a master page where I could prototype a solution. Had I bothered to Google, I’d have had working code in seconds as I was hardly the first person to encounter this situation, but it was still only a matter of minutes to put write it myself.

protected void Page_PreRender(object sender, EventArgs e)
{
    divToHide.Visible = HasContent(placeHolderInDiv);
}

protected bool HasContent(ContentPlaceHolder placeholder)
{
    bool hasContent = true;

    //assume no content if placeholder is entirely empty
    if (placeholder.Controls.Count == 0)
    {
        hasContent = false;
    }
    //or if it contains a single literal control that only contains whitespace
    if (placeholder.Controls.Count == 1 && placeholder.Controls[0] is LiteralControl)
    {
        bool whitespaceOnly = true;
        LiteralControl singleLiteral = (LiteralControl)placeholder.Controls[0];
        foreach (Char c in singleLiteral.Text)
        {
            if (!Char.IsWhiteSpace(c))
            {
                whitespaceOnly = false;
                break;
            }
        }
        if (whitespaceOnly)
        {
            hasContent = false;
        }
    }
    return hasContent;
}

With the above code working in the prototype ASP.NET site, this SharePoint naif then set out to add this HasContent() method to the code-behind file for the SharePoint master page. This, of course, is where I went wrong, as SharePoint master pages lack a code-behind file. One possibility would be to create a standalone master page base class in a strongly-named assembly, package it, deploy it as a farm solution and reference it from the master page file. (See SharePoint 2010 custom masterpage with code behind file – Part 2 or Adding Code-Behind Files to Master Pages and Page Layouts in SharePoint Server 2007) This struck me as cumbersome, though I’ve come to expect this sort of thing with SharePoint.

The other solution would be to add a script block to the master page. On the face of it, I’d prefer not to do this simply because I’ve gotten used to keeping my presentation markup and event-handling code in separate files. Even if I wanted to do that, SharePoint won’t let you; you’d get an error letting you know that “code blocks are not allowed in this file.” There is a workaround to edit the web.config file to disable that security feature (See SharePoint Page Types), but between loosening security restrictions and mixing code in with markup, the script block approach seemed the wrong way to go.

Client-side

At this point, the path of least resistance was to handle this on the client side, particularly since the jQuery library was already available on the site in question. With jQuery and a regex, this became almost a one-liner:

<script type="text/javascript">
$(document).ready(function() {
    $("#divToHide").toggle(/\S/.test($("#divToHide").html()));
})
</script>

I’d have preferred to avoid a solution that involves downloading unnecessary markup to the browser as well as additional code to hide it. I know it’s only a few bytes—a drop in the bucket compared to the typical bloated SharePoint page—but it still bothers me, especially since the code is visible to the end user and I’m “airing my dirty laundry,” as it were. However, it’s working, so I’m not going to sweat it.

Server-side, take two

The brevity of the jQuery version got me wondering how terse and cryptic I could make a server-side version if I used Linq. Just for fun, here’s what I’ve come up with. It’s not as short as the jQuery code, but much more impenetrable than the earlier ASP.NET version.

protected void Page_PreRender(object sender, EventArgs e)
{
    divToHide.Visible = HasContent(placeHolderInDiv);
}

protected bool HasContent(ContentPlaceHolder ph)
{
    //There is no content if the placeholder has no controls 
    //or if it contains a single literal control that only whitespace
    return !(ph.Controls.Count == 0
        || (
            ph.Controls.Count == 1
            && ph.Controls[0] is LiteralControl
            && ((LiteralControl)ph.Controls[0]).Text.All(c => Char.IsWhiteSpace(c))
        ));
}

I wasn’t sure that this would work the same way as my original code and terminate the loop as soon as it hit a non-whitespace character, but the documentation assures me that the All() method will stop “as soon as the result can be determined.”

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: