A better InputFormSection for SharePoint

One of my biggest annoyances with developing solutions SharePoint 2007 is the fact that many of the web controls available in the SharePoint framework do not support the same development paradigm that traditional ASP.Net controls support. Specifically, many controls in the Microsoft.SharePoint.WebControls namespace do not support two way data binding. The best example of this is the InputFormSection control. Like many ASP.Net Templated controls, the InputFormSection control allows you to encapsulate items of your choosing in regions within the control. The control takes care of the layout and positioning of your controls, and reduces the need for redundant declarative markup.

While the control works great with SharePoint Lists as a data source, it breaks down if you are trying to implement a custom application page with a traditional ASP.Net data source. With most ASP.Net Templated controls, the templates support two way data binding, but to my dismay, the SharePoint InpurFormSection does not. Because of this, I’ve seen numerous example across the web where bad practices are being introduced to make controls (and their values) accessible in code, when they could have simply used the ASP.Net data binding concepts to solve their problem.

Let’s jump into an example. Let’s say you are developing a custom application page for SharePoint, and you are using the OOTB InputFormSection control. You create your aspx page, add the appropriate declarative markup, and end up with a page that looks like this:

You will find that you are not able to access the “Product_Name” value when it comes time to Insert or Update the value. The method that I see most often discussed on the web to remedy this situation is to override the Inserting or Updating events on the FormView control to manually set the value for the specified fields.

This is a bad practice mainly because it should not be necessary. The controls inside the InputFormSection should be able to data bind to the FormView just as they do in any other situation, but they can’t because the InputFormSection is acting as a road block. To make things worse, because two way data binding does not work, you will find that on postback, controls that were accessible during page load are now null.

Why is this? If we open up Reflector, or my new favorite tool from JetBrains, dotPeek, we can see that the InputFormSection controls implements properties of Type ITemplate, not IBindableTemplate, and it does not implement IBindableControl, which is needed to support two-way data binding.

To remedy this situation, a colleague of mine (who prefers to remain anonymous), and I, set out to create a new InputFormSection control that works like the original SharePoint control, but supports two way data binding.

We opened the existing SharePoint InputFormSection control and copied out the declarative markup. We simplified some of the markup, and removed some of the SharePoint presentation features like collapsible sections, but they could be added back in if you want. Then, we created a class that implemented IBindableControl. The IBindableControl interface requires that you have a method named ExtractValues. This is how the parent control container can get the data bound values out of the template regions. In addition, the Type of template container used in the control is of Type IBindableTemplate, instead of ITemplate. Finally, in the OnInit event, we instantiate our bindable templates inside our controls content place holders.

The declarative markup:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="InputFormSectionControl.ascx.cs" Inherits="TCSC.SharePoint.Web.Controls.InputFormSectionControl, TCSC.SharePoint.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7992b8124f5b366" %>
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; width: 100%; table-layout: fixed;">
    <tr>
        <td height="1" class="ms-sectionline" colspan="2">
            <img width="1" height="1" alt="" src="/_layouts/images/blank.gif" complete="complete" />
        </td>
    </tr>
    <tr>
        <td class="ms-descriptiontext" style="vertical-align: top; width: 30%;">
            <table width="100%" border="0" cellspacing="0" cellpadding="1">
                <tr>
                    <td height="22" class="ms-sectionheader" valign="top" style="padding-top: 4px;">
                        <h3 class="ms-standardheader">
                            <asp:PlaceHolder ID="placeHolderLeftSection" runat="server"></asp:PlaceHolder>
                        </h3>
                    </td>
                </tr>
                <tr>
                    <td class="ms-descriptiontext ms-inputformdescription">
                        <asp:Label ID="labelDescription" runat="server"></asp:Label>
                    </td>
                </tr>
                <tr>
                    <td>
                        <img width="150" height="19" alt="" src="/_layouts/images/blank.gif" complete="complete" />
                    </td>
                </tr>
            </table>
        </td>
        <td align="left" class="ms-authoringcontrols ms-inputformcontrols" valign="top">
            <table width="100%" border="0" cellspacing="0" cellpadding="0">
                <tr>
                    <td>
                        <img height="7" alt="" src="/_layouts/images/blank.gif" complete="complete" />
                    </td>
                </tr>
                <tr>
                    <td class="ms-authoringcontrols">
                        <table width="100%" class="ms-authoringcontrols" border="0" cellspacing="0" cellpadding="0">
                            <tr>
                                <td class="ms-authoringcontrols" nowrap="nowrap" style="padding-left: 5px;">
                                    <asp:Label ID="labelName" runat="server"></asp:Label>
                                </td>
                            </tr>
                            <tr>
                                <td colspan="2">
                                    <img width="1" height="2" alt="" src="/_layouts/images/blank.gif" complete="complete" />
                                </td>
                            </tr>
                            <tr>
                                <td style="padding-left: 15px;">
                                    <asp:PlaceHolder ID="placeHolderRightSection" runat="server"></asp:PlaceHolder>
                                </td>
                            </tr>
                        </table>
                    </td>
                </tr>
                <tr>
                    <td>
                        <img width="1" height="6" alt="" src="/_layouts/images/blank.gif" complete="complete" />
                    </td>
                </tr>
            </table>
        </td>
    </tr>
</table>

The code behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
using System.Collections.Specialized;

namespace TCSC.SharePoint.Web.Controls
{
    [ParseChildren(true)]
    public partial class InputFormSectionControl : System.Web.UI.UserControl, IBindableControl
    {
        private IBindableTemplate _leftSection;

        private IBindableTemplate _rightSection;

        public string Name { get; set; }

        public string Description { get; set; }

        [PersistenceMode(PersistenceMode.InnerProperty),
        TemplateContainer(typeof(IDataItemContainer),
        System.ComponentModel.BindingDirection.TwoWay)]
        public IBindableTemplate LeftSection
        {
            get
            {
                return _leftSection;
            }
            set
            {
                _leftSection = value;
            }
        }

        [PersistenceMode(PersistenceMode.InnerProperty),
        TemplateContainer(typeof(IDataItemContainer),
        System.ComponentModel.BindingDirection.TwoWay)]
        public IBindableTemplate RightSection
        {
            get
            {
                return _rightSection;
            }
            set
            {
                _rightSection = value;
            }
        }

        public void ExtractValues(IOrderedDictionary dictionary)
        {
            this.CopyExtractValues(dictionary, this._leftSection.ExtractValues(this.placeHolderLeftSection));

            this.CopyExtractValues(dictionary, this._rightSection.ExtractValues(this.placeHolderRightSection));
        }

        private void CopyExtractValues(IOrderedDictionary dictionaryContainer, IOrderedDictionary dictionaryTemplate)
        {
            foreach (string s in dictionaryTemplate.Keys)
            {
                if (dictionaryContainer.Contains(s))
                    dictionaryContainer[s] = dictionaryTemplate[s];
                else
                    dictionaryContainer.Add(s, dictionaryTemplate[s]);
            }
        }

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

            if (this._leftSection != null)
                _leftSection.InstantiateIn(this.placeHolderLeftSection);

            if (this._rightSection != null)
                this._rightSection.InstantiateIn(this.placeHolderRightSection);

            this.labelName.Text = this.Name;
            this.labelDescription.Text = this.Description;
        }
    }
}

The declarative markup when using the control on a page looks like this:

<TCSC:InputFormSectionControl ID="inputFormSectionControlName"
                              runat="server"
                              Name="Product Name"
                              Description="Provide a name for this product.">
    <LeftSection>Task Description</LeftSection>
    <RightSection><%# Bind(“Product_Name”)%></RightSection>
</TCSC:InputFormSectionControl>

The result is a control that looks like the SharePoint InpurFormControl, but allows us to use traditional ASP.Net two way data binding! There is no need to override the FormView Inserting or Updating events to programmatically set values.

avatar

About Everett Comstock

Everett is a Senior Consultant with TCSC. Specializing in SharePoint development he has been a part of the .Net community in Richmond for the past six years.
This entry was posted in Development, SharePoint 2007 and tagged , , , . Bookmark the permalink.

One Response to A better InputFormSection for SharePoint

  1. Pingback: Visual Studio Styles | TCSC Blog

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>