Tuesday, October 27, 2009

Update SharePoint List Column Property Programatically

In order to update a list column (field) property in SharePoint, such as the setting of required or not, we can do the change through the column's setting page from the list's setting page. But some field properties don't display in setting page, and you can't make the change via UI.

Can we update a field property programatically? The answer is yes. For those field properties exposed directly to SPField, the update is simple. Following code sets a list column named "CustomField" to be hidden:
        using (SPSite site = new SPSite("http://localhost"))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists["MyCustomList"];
list.Fields["CustomField"].Hidden = true;
list.Update();
}
}
If the field property is not exposed to a SPField object, we can also make the change by updating the field's schema XML. SPField has a property called "ShowInNewForm" that determines whether the field shows in the NewForm page (the page to create a new list item). Following code demos how to set a site column not to display in NewForm page:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;

class Program
{
static void Main(string[] args)
{
TurnOffFieldInNewForm("http://localhost", "My Custom List", "CustomField");
}


static void TurnOffFieldInNewForm(string siteName, string listName, string fieldName)
{
using (SPSite site = new SPSite(siteName))
{
using (SPWeb web = site.OpenWeb("Team"))
{
SPList list = web.Lists[listName];
string origSchemaXml = web.Fields.GetFieldByInternalName(fieldName).SchemaXml;

string schemaXml = origSchemaXml.Replace("ShowInNewForm=\"TRUE\"", "ShowInNewForm=\"FALSE\"");
if (!schemaXml.Contains("ShowInNewForm="))
{
int index = schemaXml.IndexOf("></Field>");
schemaXml = schemaXml.Substring(0, index) + " ShowInNewForm=\"FALSE\"></Field>";
}

web.Fields.GetFieldByInternalName(fieldName).SchemaXml = schemaXml;
list.Fields.GetFieldByInternalName(fieldName).SchemaXml = schemaXml;
list.Update();
web.Update();
}
}
}
}

Note: updating the site column property doesn't have impact on existing lists that are using that site column, and the update only takes effect for new lists or new referencing to that site column. Because the list column copies the site column's schema xml once when it's first created. So we need to update the list column's property in existing lists separately.

Wednesday, October 14, 2009

JQuery Auto-complete Based On SPList Items In SharePoint Environment

Step 1. Download latest jQuery and jQuery Auto-complete plugin libraries.

Step 2. Included jQuery, auto-complete plugin js and its css files in the Page. This can be done in ASP.NET page, Master page, Control or WebPart:
    if (!Page.ClientScript.IsClientScriptBlockRegistered("jQueryScript"))
{
string path = "<script type='text/javascript' language='javascript'
src='/_layouts/Autocomplete/jQuery.js'></script>"
;
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "jQueryScript", path);
}
if (!Page.ClientScript.IsClientScriptBlockRegistered("AutocompleteScript"))
{
string path = "<script type='text/javascript' language='javascript'
src='/_layouts/Autocomplete/jquery.autocomplete.js'></script>"
;
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "AutocompleteScript", path);
}
if (!Page.ClientScript.IsClientScriptBlockRegistered("AutocompleteCss"))
{
string path = "<link rel='stylesheet' type='text/css'
href='/_layouts/Autocomplete/jquery.autocomplete.css' />"
;
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "AutocompleteCss", path);
}

Step 3: Add following script on the page to enable the auto-complete on the textbox (WebUrl, ListName, FieldName need to be set in the code behind, or hard-coded in the script):
<script type="text/javascript">
$(document).ready(function () {
var handlerUrl = "/_layouts/Autocomplete/AutoCompleteHandler.ashx";
var request = handlerUrl + '?WebUrl=<%= WebUrl %>&ListName=<%= ListName %>&FieldName=<%= FieldName %>';
var options = { max: 20, multiple: true, multipleSeparator: ';' };
$("textarea[id='" + '<%= txtBox.ClientID %>' + "']").autocomplete(request, options);

});
</script>

Step 4: Add javascripts, css file, and the auto-complete HttpHandler inside the layout folder (C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\LAYOUTS\Autocomplete\). The auto-complete HttpHandler includes two parts:

AutoCompleteHandler.ashx:
<%@ Assembly Name="Project assembly fully qualified name" %>
<%@ WebHandler Language="C#" Class="AutoCompleteHandler" %>

AutoCompleteHandler.ashx.cs:
using System;
using System.Web;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;

public class AutoCompleteHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string prefixText = string.Empty;
string inputText = context.Request["q"];
string webUrl = context.Request["WebUrl"];
string listName = context.Request["ListName"];
string fieldName = context.Request["FieldName"];
if (!string.IsNullOrEmpty(inputText))
{
int lastDelimiter = inputText.LastIndexOf(";");
lastDelimiter = (lastDelimiter > 0) ? lastDelimiter : 0;
prefixText = inputText.Substring(lastDelimiter).TrimStart(new char[] { ',', ';', ' ' });
}
if (string.IsNullOrEmpty(prefixText) || string.IsNullOrEmpty(webUrl)
|| string.IsNullOrEmpty(listName) || string.IsNullOrEmpty(fieldName))
{
return;
}

SendAutoComplete(context, inputText, webUrl, listName, fieldName);
}

private void SendAutoComplete(
HttpContext context,
string prefixText,
string webUrl,
string listName,
string fieldName)
{
List<string> nameList = new List<string>();
try
{
SPList list = SPListHeler.GetList(webUrl, listName);
if (list != null)
{
SPQuery spQuery = new SPQuery();
string query = @"<Where><BeginsWith>
<FieldRef Name='$NAME$'/>
<Value Type='Text'>$TEXT$</Value>
</BeginsWith></Where>"
;
spQuery.Query = query.Replace("$NAME$", fieldName).Replace("$TEXT$", prefixText);
int limit;
if (int.TryParse(context.Request["limit"], out limit) && limit > 0)
spQuery.RowLimit = (uint)limit;
SPListItemCollection items = list.GetItems(spQuery);
foreach (SPListItem item in items)
{
nameList.Add(item[fieldName].ToString());
}
}

nameList.Sort();
StringBuilder sb = new StringBuilder();
foreach (string tag in nameList)
{
sb.Append(tag + Environment.NewLine);
}

context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.ContentType = "text/plain";
context.Response.Write(sb.ToString() + " ");
}
catch (Exception ex)
{
// Log error
}
}

public bool IsReusable
{
get { return false; }
}
}

Note: To improve the performance, we can cache all the names and query the memory objects inside the http handler instead of CAML query each time.

Wednesday, October 07, 2009

SharePoint InputFormTextBox Validation

RequiredFieldValidator can not work with SharePoint InputFormTextBox. You have to client side JavaScript validation instead. There's an out-of-box function RTE_GetRichEditTextOnly come handy to retrieve the value from InputFormTextBox when doing JavaScript validation:
function ValidateInputFormTextbox() {
    var inputText = RTE_GetRichEditTextOnly("<%= inputControl.ClientID %>");
    if (inputText)
        return true;
    else
        return false;
}