How to Customize SharePoint 2007 Forms - Part 3 (Field Validations)

0 nhận xét

In continuation of previous series of posts, for exploring options to customize SharePoint 2007 list forms, I am further introducing ways to deal with yet another limitation reminiscent in forms with SharePoint 2007 – Column Data Validations.

  1. How to customize SharePoint 2007 Forms – Part 1
  2. How to customize SharePoint 2007 Forms – Part 2

Note! – This post is to share conceptual details. It is NOT ready for production usage, and I will share “Alpha” bits shortly. I haven’t had time lately, and instead of delays I think a good portion of my readers can benefit from it in the meantime. I’m glad to receive all feedback and ideas in interim.

I’d also like to hear what everyone thinks about SharePoint 2010 – list and column Validation settings. Perhaps I can have some inspiration to improve below, which was conceptualized long before I came across SP 2010 approach of using formulae. Anyhow, let’s get started…

Problem Case – Where are my data validations?

Now, SharePoint offers great flexibility to users for coming up with desired custom lists, with existing or custom column-types of choosing. Existing column-types while allow most common scenario’s of column (field) types, they are limited in their scope to control consistency of data – particularly so when it comes down to applying custom logic, or even even extending basic definitions of column types.

Some examples of limitations, and existing options are:

  • Need to capture e-mail of contacts, and entries should be with valid format. Use column of type ‘Single line of Text’ instead.
  • Need to ensure that value of one column is correct, based on current items value in other column. Not possible, without writing JavaScript based hacks.
  • Need to have Record-ID field with custom format, say XXX-DD-XXX. (X=Alphabets, D=Numeric’s). Use column of type ‘Single line of Text’ instead.

List of data validation requirements goes on and on. So much that I have noticed that developer’s have at times resorted to creating custom field, simply to either encapsulate custom logic, or simple things like Regex validation. It’s clearly evident that what we are missing is the option of being able to extend existing column types and apply custom desired validations against existing or new lists.

Solution Approach – Early Preview!

A means to describe and execute the data validation against columns that we want specify. From my previous posts you’d remember how we introduced our custom Field type via usage of Rendering Template for select list types.

   1: <sharepoint:renderingtemplate id="ListForm" runat="server">
   2:     <Template>
   3:         <span id='part1'>
   4:             <SharePoint:InformationBar runat="server" />
   5:             <wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbltop"

RightButtonSeparator=" "
   6:                 runat="server">
   7:                 <template_rightbuttons>
   8:                         <SharePoint:NextPageButton runat="server"/>
   9:                         <SharePoint:SaveButton runat="server"/>
  10:                         <SharePoint:GoBackButton runat="server"/>
  11:                     template_rightbuttons>
  12:             wssuc:ToolBar>
  13:             <SharePoint:FormToolBar runat="server" />
  14:             <table class="ms-formtable" style="margin-top: 8px;"

border="0" cellpadding="0" cellspacing="0"
  15:                 width="590px">
  16:                 <SharePoint:ChangeContentType runat="server" />
  17:                 <SharePoint:FolderFormFields runat="server" />
  18:                 <OfficeToolbox:FormFieldIterator runat="server" />
  19:                 <SharePoint:ApprovalStatus runat="server" />
  20:                 <SharePoint:FormComponent TemplateName="AttachmentRows"

runat="server" />
  21:             table>
  22:             <table cellpadding="0" cellspacing="0" width="100%">
  23:                 <tr>
  24:                     <td class="ms-formline">
  25:                         <img src="/_layouts/images/blank.gif" width="1"

height="1" alt="">
  26:                     td>
  27:                 tr>
  28:             table>
  29:             <table cellpadding="0" cellspacing="0" width="100%"

style="padding-top: 7px">
  30:                 <tr>
  31:                     <td width="100%">
  32:                         <SharePoint:ItemHiddenVersion runat="server" />
  33:                         <SharePoint:ParentInformationField runat="server" />
  34:                         <SharePoint:InitContentType runat="server" />
  35:                         <wssuc:ToolBar CssClass="ms-formtoolbar"

id="toolBarTbl"
RightButtonSeparator=" "
  36:                             runat="server">
  37:                             <template_buttons>
  38:                         <SharePoint:CreatedModifiedInfo runat="server"/>
  39:                     template_buttons>
  40:                             <template_rightbuttons>
  41:                         <SharePoint:SaveButton runat="server"/>
  42:                         <SharePoint:GoBackButton runat="server"/>
  43:                     template_rightbuttons>
  44:                         wssuc:ToolBar>
  45:                     td>
  46:                 tr>
  47:             table>
  48:         span>
  49:         <SharePoint:AttachmentUpload runat="server" />
  50:     Template>
  51: sharepoint:renderingtemplate>
  52: <sharepoint:renderingtemplate id="FormFieldIterator" runat="server">
  53:     <Template>
  54:         <tr>
  55:             <SharePoint:CompositeField 

TemplateName
="OfficeToolboxCompositeField" runat="server" />
  56:         tr>
  57:     Template>
  58: sharepoint:renderingtemplate>
  59: <sharepoint:renderingtemplate id="OfficeToolboxCompositeField" runat="server">
  60:     <Template>
  61:         <td nowrap="true" valign="top" width="190px" class="ms-formlabel">
  62:             <span>
  63:                 <h3 class="ms-standardheader">
  64:                     <SharePoint:FieldLabel runat="server" />
  65:                 h3>
  66:             span>
  67:         td>
  68:         <td valign="top" class="ms-formbody" width="400px">
  69:             <OfficeToolbox:FormField runat="server" />
  70:             <SharePoint:FieldDescription runat="server" />
  71:             <SharePoint:AppendOnlyHistory runat="server" />
  72:         td>
  73:     Template>
  74: sharepoint:renderingtemplate>

So extending the base Field type, Microsoft.SharePoint.WebControls.FormField, with our FormField type as follows (just an example code…)

   1: namespace OfficeToolbox.SPForms {
   2:  
   3:     /// 
   4:     /// FormField control
   5:     /// 
   6:     public class FormField : Microsoft.SharePoint.WebControls.FormField {
   7:         protected override void OnInit(EventArgs e) {
   8:             base.OnInit(e);
   9:             // Set Field as Read-only, per Configuration
  10:             ...
  11:         }
  12:  
  13:         public FormSettings Settings { get; private set; }
  14:  
  15:         protected override void CreateChildControls() {
  16:             base.CreateChildControls();
  17:  
  18:              //Set Field's default value, per QueryString, only for New items
  19:             switch (this.FieldName)
  20:             {
  21:                 ...
  22:                 default:
  23:                     if (this.ControlMode == SPControlMode.New &&

!this.Page.IsPostBack &&
this.Page.Request.QueryString[this.Field.Title] != null)
  24:                     {
  25:                         try
  26:                         {
  27:                             switch (((Microsoft.SharePoint.

WebControls.FormField)(this)).Controls[0].GetType().Name)
  28:                             {
  29:                                 ...
  30:                                 case "SPBoundField":
  31:                                     this.Value = this.Page.Request.

QueryString[this.Field.Title].ToString();
  32:                                     break;
  33:                                 ...
  34:                             }
  35:                         }
  36:                         catch { }
  37:                     }
  38:                     break;
  39:             }
  40:         }
  41:  
  42:         protected override void RegisterFieldControl() {
  43:             this.ItemContext.FormContext.FieldControlCollection.Add(this);
  44:             this.Page.Validators.Add(this);
  45:         }
  46:  
  47:         protected override void Render(System.Web.UI.HtmlTextWriter output)

{
  48:             if (base.ControlMode != SPControlMode.Display) {
  49:                 base.RenderFieldForInput(output);
  50:             }
  51:             else
  52:                 base.RenderFieldForDisplay(output);
  53:         }
  54:  
  55:         /// 
  56:         /// Custom validation!
  57:         /// 
  58:         public override void Validate() {
  59:             if (((base.ControlMode != SPControlMode.Display) && base.IsValid)

&& this.Visible) {
  60:                 base.Validate();
  61:                 if (base.ControlMode != SPControlMode.Display) {
  62:                     FormSettings.Column c =

Settings.FindColumnByName(base.FieldName);
  63:                     if (c.Validators != null)
  64:                         switch (c.Validators.LogicalOp) {
  65:                             case

FormSettings.Validator.LogicalOperators.And: {
  66:                                     if (Validate(c.Validators.Validations[0])

&& Validate(c.Validators.Validations[1]))
  67:                                         base.IsValid = true;
  68:                                     else {
  69:                                       

base.ErrorMessage = c.Validators.Message;
  70:                                         base.IsValid = false;
  71:                                     }
  72:                                 }
  73:                                 break;
  74:                             case FormSettings.Validator.LogicalOperators.Or: {
  75:                                     if (Validate(c.Validators.Validations[0])

|| Validate(c.Validators.Validations[1]))
  76:                                         base.IsValid = true;
  77:                                     else {
  78:                                       

base.ErrorMessage = c.Validators.Message;
  79:                                         base.IsValid = false;
  80:                                     }
  81:                                 }
  82:                                 break;
  83:                             case

FormSettings.Validator.LogicalOperators.None: {
  84:                                     if (Validate(c.Validators.Validations[0]))
  85:                                         base.IsValid = true;
  86:                                     else {
  87:                                       

base.ErrorMessage = c.Validators.Message;
  88:                                         base.IsValid = false;
  89:                                     }
  90:                                 }
  91:                                 break;
  92:                         }
  93:                 }
  94:             }
  95:         }
  96:  
  97:         ...
  98:     }
  99: }

So above would give you an idea how you can intercept a Field’s lifecycle to do your server side validations that I also utilize for my project. Using root-folder of list as property bag for the settings such as validations you want for each column type, as a serialized xml string. Reading those settings and applying validations at runtime comes to our rescue.

Following are the settings available. Note you can set data validation for any (OOTB) column in the list,

image

Data comparison operators that I’m aiming for, to enable initially are:

image

You can engage up to 2 columns for your validation logic settings. Not just independently, but can set interdependency based on others value. Notice how I refer adjacent column, [Start Date], and compare against value of current column – Due Date – with an AND operator to Start Date validation against [Today].

image

   1: string GetValue(string fieldValue) {
   2:     switch(fieldValue.Trim()){
   3:         case "[Today]":
   4:             return DateTime.Now.ToString();
   5:         case "[Me]":
   6:             return DateTime.Now.ToString();
   7:         default:
   8:             if (this.Fields.ContainsField(fieldValue.TrimStart('[').TrimEnd(']'))) {
   9:                 return Microsoft.SharePoint.Utilities.SPEncode.HtmlEncode(Field.GetFieldValueForEdit(ListItem[fieldValue.TrimStart('[').TrimEnd(']')]));
  10:             }
  11:             else
  12:                 return fieldValue;
  13:     }
  14: }

Codeplex Project – Office Toolbox

I will be sharing the updated implementation as early Alpha with you, since there is still much to implement/cleanup to make validations seamlessly work with all/most out-of-the-box column types. I have a basic framework working at the moment, and above is an early preview.

Hope you find applicability of extended data validations useful. For some users above may seem a bit late in the game, given all the excitement around SP 2010 and included validation settings. But I’d like to hear feedback on above as well as what you think can still be improved with SP 2010 approach!

Keep the feedback coming, and updated post with working previews of validations is coming soon…

source:weblogs.asp.net