Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / All-Topics

Formatting values in a Publishing Page

0.00/5 (No votes)
16 Sep 2009CPOL5 min read 11.8K  
SharePoint Web Content Management is a pretty flexible beast, if you’re mad keen on writing code (and the ‘ole packaging and deployment curmudgeon), you can make it do pretty much anything.

SharePoint Web Content Management is a pretty flexible beast, if you’re mad keen on writing code (and the ‘ole packaging and deployment curmudgeon), you can make it do pretty much anything. However, some times there are simple things that you’d like to do without having to crank up Visual Studio and start hammering out some angle brackets. One of these simple things is formatting values on a publishing page, particularly dates and Boolean values. If you’re reading this, you’ve probably made the same journey as me:

Denial – There must be an easy way to do this, it’s so simple that I must be able to do it with SharePoint Designer. You’ve trawled MSDN for details, maybe you’ve even messed around with the FormattedString class.

Anger –This is madness! In what parallel universe does it make sense to develop a product without such basic functionality? Sure, we’ll spend countless man hours creating gradient fill backgrounds for every button and a stylesheet that would choke a horse but hell no, formatting dates, who’d need to do a crazy thing like that?

Bargaining – I’ll ask Google, somebody will have an answer. If only I enter the right keywords it’ll be there, simple and so obvious that I’ll feel like an idiot for missing it.

Depression – Ah Google, you failed me. I gave you the best of my keywords but you gave me nothing in return. Oh woes me, once more to the coffee font to ease my anguish.

Acceptance – Maybe you’ve come across a few posts (such as this one by Paul) with some sample code to format values. Maybe you’ll figure, if these guys are writing code to do it then there’s no other way. Alas, our fate rests with Visual Studio. “Fair enough”, you say, if I’m going to write code to format my value, let it be the code to format all values, let it be the chief of the formatting race.

Here’s a solution that I’ve some up with. I’ve taken the concept of the FormattedString class and made it work with any value. (If you couldn’t care less how this works and just want to use it on your site, feel free to download a WSP here!, scroll down for details of how to use it).

FormattedString, if you’ve messed around with it, is disappointing, it takes the rendered output of any child controls and formats them using .net composite formatting. So for example, if you had:

<SharePointWebControls:FormattedString FormatText="{0} and {1}" runat="server">

    <SharePointWebControls:TextField runat="server" FieldName="Title"/>

    <SharePointWebControls:TextField FieldName="Created_x0020_By" runat="server"/>

</SharePointWebControls:FormattedString>

You’d see:

TestPage  and BASEIMAGE\administrator

rendered on your page

Great you may think, this is the answer to our problems. We’ll put a date control in there and format it with {0:dd-MMM-yyyy) or whatever and Betty’s your auntie.

<SharePointWebControls:FormattedString FormatText="{0} and {1:dd-MMM-yyyy}" runat="server"> <SharePointWebControls:TextField runat="server" FieldName="Title"/> <PublishingWebControls:PublishingScheduleFieldControl FieldName="PublishingStartDate" runat="server"/> </SharePointWebControls:FormattedString>

However, If you try this you’ll see:

TestPage and 8/28/2009 12:15 AM
<style type="text/css"><![CDATA[
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }]]></style>

The reason for this is that the FormattedString control uses the rendered output of the child controls rather than the value, so in this case the value of the date control is rendered as a string before being passed to the formatter. Since the string formatter doesn’t understand datetime formatting strings you end up with the original value. All in all, not much use!

The Code

FormattedValue.cs : -

using System;
using System.ComponentModel;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;

namespace PublishingControls.WebControls
{

    public enum NullValueBehaviour
    {
        None,
        Any,
        All
    }

    [ParseChildren(false),
        SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true),
        AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal),
        SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true),
        AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public sealed class FormattedValue : SPControl
{
    private const string STR_EncodeMethod = "EncodeMethod";
    private string _formatText;

    // Methods
    [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
    protected override void Render(HtmlTextWriter output)
    {
        object[] objArray;
        string formatText;
        bool hasNull = false;
        bool hasValue = false;

        if (!string.IsNullOrEmpty(this.FormatText))
        {
            objArray = new object[this.Controls.Count];
            int index = 0;

            //Build an array of the values of each field control
            foreach (Control current in this.Controls)
            {
                if (current is BaseFieldControl)
                {
                    object val = (current as BaseFieldControl).ItemFieldValue;
                    if (val == null)
                    {
                        hasNull = true;
                    }
                    else
                    {
                        hasValue = true;
                    }
                    objArray[index] = val;
                    index++;
                }
            }

            formatText = null;
            switch (this.EncodeMethod)
            {
            case SPEncodeMethod.NoEncode:
                formatText = this.FormatText;
                break;
            case SPEncodeMethod.HtmlEncode:
                formatText = SPHttpUtility.HtmlEncode(this.FormatText);
                break;
            case SPEncodeMethod.HtmlEncodeAllowSimpleTextFormatting:
                formatText = SPHttpUtility.HtmlEncodeAllowSimpleTextFormatting(this.FormatText);
                break;
            case SPEncodeMethod.EcmaScriptStringLiteralEncode:
                formatText = SPHttpUtility.EcmaScriptStringLiteralEncode(this.FormatText);
                break;
            }

            string formattedString = string.Empty;

            try
            {
                formattedString = string.Format(new BoolFormatProvider(), formatText, objArray);
            }
            catch (FormatException ex)
            {
                formattedString = ex.Message;
            }

            switch (this.NullBehaviour)
            {
            case NullValueBehaviour.None:
                //Do Nothing
                break;
            case NullValueBehaviour.All:
                if (hasNull && !hasValue)
                {
                    //All the values are null
                    formattedString = NullText;
                }
                break;
            case NullValueBehaviour.Any:
                if (hasNull)
                {
                    //At least one of the values is null
                    formattedString = NullText;
                }
                break;
            }

            output.Write(formattedString);
        }
    }

    // Properties
    [DefaultValue("SPEncodeMethod.HtmlEncode"), Category("Appearance")]
    public SPEncodeMethod EncodeMethod
    {
        get
        {
            if (this.ViewState[STR_EncodeMethod] != null)
            {
                return (SPEncodeMethod)this.ViewState[STR_EncodeMethod];
            }
            return SPEncodeMethod.HtmlEncode;
        }
        set
        {
            this.ViewState[STR_EncodeMethod] = value;
        }
    }

    public string FormatText
    {
        get
        {
            return this._formatText;
        }
        set
        {
            this._formatText = value;
        }
    }

    public string NullText
    {
        get;
        set;
    }

    [DefaultValue("NullValueBehaviour.None")]
    public NullValueBehaviour NullBehaviour
    {
        get;
        set;
    }

}
}

BoolFormatProvider:-

using System;
using System.Collections.Generic;
using System.Text;

namespace PublishingControls.WebControls
{
public class BoolFormatProvider : IFormatProvider, ICustomFormatter
{
#region ICustomFormatter Members

    public string Format(string format, object arg, IFormatProvider formatProvider)
    {
        if (arg is bool)
        {
            bool value;

            value = (bool)arg;

            format = (format == null ? null : format.Trim().ToLower());

            string[] vals = format.Split(';');

            switch (vals.Length)
            {
            case 1:
                //value is for true
                return value ? vals[0] : string.Empty;
            case 2:
                //values are for true/false
                return value ? vals[0] : vals[1];
            default:
                if (arg is IFormattable)
                    return ((IFormattable)arg).ToString(format, formatProvider);
                else
                    return arg.ToString();
            }
        }
        else
        {
            if (arg == null)
            {
                return string.Empty;
            }
            else if (arg is IFormattable)
                return ((IFormattable)arg).ToString(format, formatProvider);
            else
                return arg.ToString();
        }
    }

#endregion

#region IFormatProvider Members

    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(ICustomFormatter))
            return this;
        else
            return null;
    }

#endregion
}

}

You’ll notice that I’ve added a BoolFormatProvider class. In my opinion this is sorely lacking from the .NET framework especially given how trivial it is to implement. It allows boolean values to be formatted using the following syntax:

{0:<text to display if true>;<text to display if false>}

For example: {0:Yes;No} would output Yes if the value was true and No if it was false.

or

{0:<text to display if true>}

For example: {0:Including free delivery} would output: “Including free delivery’ if the value was true and an empty string is it was false. This syntax is particularly useful with check boxes.

(For more info on composite formatting click here)

How to Use it

To make use of this control you need to either publish it to the GAC or register it as a SafeControl on your SharePoint server. (The attached WSP publishes to the GAC and registers the control on whichever site you activate the solution to.)

One the control is installed, you’ll be able to drag it onto your page template in SharePoint Designer:

FormattedValue

 

Note: Sometimes SharePoint Designer doesn’t behave quite as it should and the TagPrefix doesn’t get registered properly, especially if you drag the control onto the page in code view mode. In that case you need to manually add:

<%@ Register tagprefix="WebControls" namespace="PublishingControls.WebControls" 
    assembly="PublishingControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f4e37cb7a9516aad" %>

The control takes three main parameters:

FormatText: – This is a standard .net composite formatting string.

NullText:- This contains the text to be displayed if the value of any (or all) of the child ocntrols are null

NullBehaviour:- This enumeration determines how null values should be handled. The options are:

  • None – Do nothing. Formatting is handled by the formatting string only. This is the default value.
  • Any – if any of the child values are null, the text defined by the NullText property is displayed instead of the the formatted string.
  • All – If all of the child values are null, the text defined by the NullText property is displayed instead of the formatted string.

To continue with the example used above, if you add the following to your page:

<WebControls:FormattedValue runat="server" FormatText="{0} and {1:dd-MMM-yyyy}" >    

    <SharePointWebControls:TextField runat="server" FieldName="Title"/>    

    <PublishingWebControls:PublishingScheduleFieldControl FieldName="PublishingStartDate" runat="server"/>

</WebControls:FormattedValue>

You’ll see a correctly formatted date on your rendered page:

TestPage and 28-Aug-2009 

Conclusion

At the end of the day we had to unleash Visual Studio to add some functionality that really would have been nice to have seen out-of-the-box. Lets be honest though, we like writing code and banging our heads against brick walls trying to find a solution. That’s why we’re developers, if there was no puzzle to solve we’d all go do something else with our time!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)