Click here to Skip to main content
Click here to Skip to main content

Utilizing SharePoint Form Controls (like OWSDateField)

, 3 Jan 2006
Rate this:
Please Sign up or sign in to vote.
This article discusses some ways to take advantage of the SharePoint client side controls (like the OWSDate Control) within the SharePoint web parts.

Introduction

If you have ever developed a WebPart for Windows SharePoint services, then you probably have tried to take advantage of some of the client-side controls, like the OWSDateField. If you have, then you know how much of a pain in the rear it is to use them.

The problem

There are several issues involved with using the OWS controls that come built-in with SharePoint services that are in the Microsoft.SharePoint.WebControls namespace:

  1. If you are going to use a control like the OWSDateField, then you must add an OWSForm control to the WebPart's child control collection. If you do not, then you will not be able to utilize the OWSField controls.
  2. If you wish to get the data from the control at the server (in other words, have the data posted back), you must create an OWSSubmitButton control and add it to the form. If you do not, any other posting mechanism will not post the user's input back to the server.

    This is the problem with OWSSubmitButton: there is no server side click event. Microsoft will tell you to check the "IsPostBack" property on the page, but that is not a viable option if you need more than one button that posts back the data, but does different tasks with it.

  3. None of the OWS form controls in the Microsoft.SharePoint.WebControls namespace have the data change events. Even if the data is not posted back, it will not fire an event.

To be blunt, the server-side functionality of the OWS control set is not very robust, yet the client side scripting works quite well.

The solution

For an application I developed in my current position, I needed the aforementioned functionality. However, I found nothing on the Internet that really helped me. Therefore, I dug deep into the server-side and client-side functionality of the OWS control set, and I found an elegant way of fixing the above limitations.

First, I created a base class called OWSBase that handled all of the data postback and value change event handling for all of the derived controls. Next, I created a set of controls (OWSTextField, OWSRichTextField, OWSDateField, OWSNumberField, etc.) derived from this base class, and each generated similar client-side JavaScripting that the SharePoint controls did. In the end, I created a submit button (a new OWSSubmitButton) that causes the form to postback the data and fire a server-side click event.

Here is the code for the OWSBase class:

public abstract class OWSBase : OWSControl, IPostBackDataHandler
{
    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Displayed information (possibly not used)
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public string Display
    {
        get { return "" + ViewState["Display"]; }
        set { ViewState["Display"] = value; }
    }

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Gets or the display text of the item.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public virtual string Text
    {
        get { return Value; }
        set { /* Do Nothing */ }
    }

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// The value of the text control
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public virtual string Value
    {
        get { return "" + ViewState["Value"]; }
        set { ViewState["Value"] = value; }
    }

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Indicates whether or not the control is a required field. 
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public bool Required
    {
        get
        {
            if (ViewState["Required"] == null)
                Required = false;
            return (bool)ViewState["Required"];
        }
        set { ViewState["Required"] = value; }
    }

    #region IPostBackDataHandler Members

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Gets the posted back data.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    /// <span class="code-SummaryComment"><PARAM name="postDataKey">data key for this control</PARAM>
</span>
    /// <span class="code-SummaryComment"><PARAM name="postCollection">collection information</PARAM>
</span>
    /// <span class="code-SummaryComment"><RETURNS>true if to fire event</RETURNS>
</span>
    public virtual bool LoadPostData(string postDataKey, 
                         NameValueCollection postCollection)
    {
        string present = Value;
        Value = postCollection[postDataKey];
        return (present != Value);
    }

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Raises the text changed event
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public virtual void RaisePostDataChangedEvent()
    {
        OnValueChanged(EventArgs.Empty);
    }

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Helper to raise the value changed event
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    /// <span class="code-SummaryComment"><PARAM name="e">value</PARAM>
</span>
    private void OnValueChanged(EventArgs e)
    {
        if (ValueChanged != null)
            ValueChanged(this, e);
    }

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// fires when the value changes after a postback. 
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public event EventHandler ValueChanged;
    
    #endregion

    #region Helper Properties

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Formats the string for javascripting.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    protected string JavaScriptValue
    {
        get 
        {
            Microsoft.SharePoint.WebControls.
            return Value
                .Replace(@"\", @"\\")
                .Replace("\"", "\\\"")
                .Replace("'", "\'")
                .Replace("\r", "")
                .Replace("\n", "\\n");
            ; 
        }
    }
    #endregion
}

Here is the code for one of the derived controls, OWSDateField. (Note: This OWSDateField is not the same as the OWSDateField in the Microsoft.SharePoint.WebControls namespace):

public class OWSDateField : OWSBase
{
    #region construction

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Default constructor
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public OWSDateField() { }

    #endregion

    #region custom properties

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Gets or set the number date in a 
    /// .NET date time format. 
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public DateTime DateTime
    {
      get { 
       return 
          SPUtility.CreateSystemDateTimeFromXmlDataDateTimeFormat(Value); 
      }
      set { 
        Value = SPUtility.CreateISO8601DateTimeFromSystemDateTime(value); 
      }
    }

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Gets or set whether or not this control 
    /// presents a date only value.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public bool DateOnly
    {
        get
        {
            if (ViewState["DateOnly"] == null)
                DateOnly = false;
            return (bool)ViewState["DateOnly"];
        }
        set { ViewState["DateOnly"] = value; }
    }

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Gets or set whether or not to hide descriptions.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public bool HideDescription
    {
        get
        {
            if (ViewState["HideDescription"] == null)
                HideDescription = false;
            return (bool)ViewState["HideDescription"];
        }
        set { ViewState["HideDescription"] = value; }
    }

    #endregion

    #region overridden properties

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Gets the test string representation of the date.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public override string Text
    {
        get 
        { 
            if (DateOnly)
                return DateTime.ToString("dddd, MMMM d, yyyy");
            return DateTime.ToString("dddd, MMMM d, yyyy h:mm tt");
        } 
    }

    #endregion

    #region overridden methods

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Renders the OWS Control
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    /// <span class="code-SummaryComment"><PARAM name="wtr">writes out the text control information
</span>
    /// <span class="code-SummaryComment"></PARAM>
</span>
    protected override void Render(HtmlTextWriter wtr)
    {
        wtr.Write(
            "<script>" +
            "fld=new DateField(frm, '{0}', '{1}', '{2}'); " +
            "fld.fDateOnly = {3}; " +
            "fld.fHideDescription = {4}; " +
            "fld.caltype = 1; " +
            "fld.fRequired = {5}; " +
            "fld.BuildUI(); " +
            "</script>",
            UniqueID,
            Display,
            JavaScriptValue,
            DateOnly.ToString().ToLower(),
            HideDescription.ToString().ToLower(),
            Required.ToString().ToLower()
        );
    }
    #endregion
}

Here is the code for the submit button:

/// <span class="code-SummaryComment"><SUMMARY>
</span>
/// This type is used to specify in what 
/// format this submit button will render.
/// <span class="code-SummaryComment"></SUMMARY>
</span>
public enum OWSButtonType
{
    Button,
    HyperLink
}

/// <span class="code-SummaryComment"><SUMMARY>
</span>
/// This control will cause the postback of 
/// sharepoint stuff. 
/// <span class="code-SummaryComment"></SUMMARY>
</span>
public class OWSSubmitButton : WebControl, IPostBackEventHandler
{
    #region properties

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Gets or sets the type of link
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public OWSButtonType ButtonType
    {
        get
        {
            if (ViewState["ButtonType"] == null)
                ButtonType = OWSButtonType.HyperLink;
            return (OWSButtonType)ViewState["ButtonType"];
        }
        set { ViewState["ButtonType"] = value; }
    }

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// The display text of the button
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public string Text
    {
        get { return "" + ViewState["Text"]; }
        set { ViewState["Text"] = value; }
    }

    #endregion

    #region overridden methods

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Overrides the onload event, and emits the javascript.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    protected override void OnLoad(EventArgs e)
    {
        EmitProcessFormScript();
        base.OnLoad(e);
    }

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Renders the control
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    /// <span class="code-SummaryComment"><PARAM name="writer">the html output write to 
</span>
    /// which to render<span class="code-SummaryComment"></PARAM>
</span>
    protected override void Render(HtmlTextWriter writer)
    {
        switch (ButtonType)
        {
            case OWSButtonType.HyperLink:
                writer.Write(
                    "<A class='\"{0}\"' 
                        href='http://www.thecodeproject.com/"{1}/"'>{2}</A>",
                    CssClass,
                    string.Format(
                        "javascript:ProcessOwsForm('{0}', 'Click');",
                        UniqueID),
                    Microsoft.SharePoint.Utilities.SPEncode.HtmlEncode(Text)
                );
                break;
                
            case OWSButtonType.Button:
                writer.Write(
                    "<input class='\"{0}\"' 
                       onclick='\"{1}\"' type='\"button\"' value='\"{2}\"'>",
                    CssClass,
                    string.Format(
                        "ProcessOwsForm('{0}', 'Click');",
                        UniqueID),
                    Microsoft.SharePoint.Utilities.SPEncode.HtmlEncode(Text)
                );
                break;
        }
    }

    #endregion

    #region IPostBackEventHandler Members

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Causes the click event to fire
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    /// <span class="code-SummaryComment"><PARAM name="eventArgument">this is the 
</span>
    /// event argument<span class="code-SummaryComment"></PARAM>
</span>
    public void RaisePostBackEvent(string eventArgument)
    {
        OnClick(EventArgs.Empty);
    }

    #endregion

    #region events

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Fires when the submit button is clicked. 
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public event EventHandler Click;

    #endregion

    #region virtual methods

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Helper method for the onclick event
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    protected virtual void OnClick(EventArgs e)
    {
        if (Click != null)
            Click(this, e);
    }

    #endregion

    #region helper methods

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Emits scripting to force the processing 
    /// of the form script. 
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    private void EmitProcessFormScript()
    {
        if (!Page.IsClientScriptBlockRegistered(
                              "OWSProcessFormScript"))
        {
            Page.RegisterStartupScript(
                "OWSProcessFormScript",
                "<script> \r\n" +
                "function ProcessOwsForm(ctl, argument) \r\n" +
                "{ \r\n" +
                "    if (frm.fPreviewMode) \r\n" +
                "    { \r\n" +
                "        var L_cantSave_Text = \"This form cannot be " + 
                                "saved when previewing this page.\"; \r\n" +
                "        window.alert(L_cantSave_Text); \r\n" +
                "        return; \r\n" +
                "    } \r\n" +
                "    if (frm.FValidate(true)) \r\n" + 
                "    { \r\n" +
                "        frm.FPostProcess(); \r\n" +
                "        __doPostBack(ctl, argument); \r\n" +
                "    } \r\n" +
                "} \r\n" +
                "</script>"
            );
            Page.GetPostBackEventReference(this);
        }
    }
    #endregion
}

Included in the download files are the following:

  • OWSChoiceField, which uses an Items property with the ListItem's objects to manage items in the choice, it can either be a dropdown list or a radio button choice field. Use the ChoiceFormat property to specify this.
  • OWSTextField, which can be a single or multi-line text field, use the NumLines property.
  • OWSRichTextField, which can get the HTML encoded text with a nice user interface (uses SharePoint client scripting). Use the NumLines property.
  • OWSNumberField

Assumptions

I assume you are familiar with developing WebParts for WSS or SPS. I will not give help with Manifext.xml or DWP files. These controls are meant to be used within the context of a SharePoint WebPart.

About the ZIP file

All that is included in the ZIP file are the source files. There is no project file. You may add these files to an existing project, or you may create a base assembly that you can reference in your WebPart projects.

TestWebPart.cs contains a small example of how to use the controls.

Special considerations

I have only tested these controls when they have been compiled into a class library that has been installed in the Global Assembly Cache, and thus fully trusted by SharePoint. You may need to make changes to the code or .config files if you do not sign your assembly with a strong name and install it in the GAC (using the - globalinstall option with stsadm.exe).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

railerb
Web Developer
United States United States
No Biography provided

Comments and Discussions

 
GeneralJS Error PinmemberCarsten Isak Nielsen26-Jun-06 23:47 
AnswerRe: JS Error Pinmemberroxvogn7-Dec-06 10:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 3 Jan 2006
Article Copyright 2006 by railerb
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid