MOSS2007的自定义字段类型
v3 Creating Custom Field Types
Custom field types in SharePoint 2007 are pretty darn cool. They allow you to create your own subclasses of the existing SharePoint field types. You can control pretty much every aspect of the fields behavior. Things such as how the field is displayed/rendered in the SharePoints interface, how the data is formatted when it gets stored in the field, validation, and all kinds of other cool stuff.
So to try out custom field types I wanted to create a field for storing telephone numbers.
The field uses javascript to format the phone number automatically as the user types. When the item is saved the custom field class strips out all the formatting characters that the javascript puts in and it also checks for proper length. There are three main components to building a custom field…
- Custom Field Class – Should inherit from an existing SPField class. Ie. SPFieldText
- ASCX Control (Optional) – Defines a SharePoint:RenderingTemplate element whichs tells SharePoint how to render your control.
- Custom Control Class(Optional) – The code-behind for your ascx control which defines how your control is rendered.
To get started open up vs 2005, create a new class library project, and add a reference to the Microsoft.SharePoint.dll. Now create a new class file and name it TelephoneField.cs. This will contain the code for our custom field class. Add the following using directives to the top of TelephoneField.cs…
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using Microsoft.SharePoint;
using System.Text.RegularExpressions;
Now copy and paste the following code into the body of the class (make sure to leave your namespace intact)…
public class TelephoneField : SPFieldText
{
#region Contstructors
public TelephoneField(SPFieldCollection fields, string fieldName) : base(fields, fieldName)
{
}
public TelephoneField(Microsoft.SharePoint.SPFieldCollection fields, string typeName, string displayName) : base(fields, typeName, displayName)
{
}
#endregion
/// <summary>
/// This method validates the data as it is entered into the column. If it doesnt match the criteria a sharepoint exception is thrown.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public override string GetValidatedString(object value)
{
string myVal = value as string;
if (myVal == null)
return String.Empty;
//Strip formating characters from the value..
myVal = myVal.Replace("(", "");
myVal = myVal.Replace(")", "");
myVal = myVal.Replace("-", "");
myVal = myVal.Replace("+", "");
myVal = myVal.Replace(" ", "");
myVal = myVal.Trim();
//Use regex to makes the string only contains numbers and is within the correct range.
Regex foo = new Regex("^//d{10,11}$");
if (foo.IsMatch(myVal))
{
}
else
{
throw new Microsoft.SharePoint.SPFieldValidationException("The Telephone number field must contain only numbers, (, ), -, or + characters. It must also be between 10 and 11 digits. val:" + myVal);
}
return myVal;
}
/// <summary>
/// Here we can apply formating to our number that will show up on the edit page.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public override object GetFieldValue(string value)
{
if (String.IsNullOrEmpty(value))
return null;
//TODO - Format the phone number here (XXX)XXX-XXXX
return value;
}
public override Microsoft.SharePoint.WebControls.BaseFieldControl FieldRenderingControl
{
get
{
Microsoft.SharePoint.WebControls.BaseFieldControl phoneNumberFieldControl = new TelephoneFieldControl();
phoneNumberFieldControl.FieldName = InternalName;
return phoneNumberFieldControl;
}
}
}
As you can see our custom field class inherits from SPFieldText but you can inherit from pretty any other type of SharePoint field. This class essentially controls what happens to the formatting of our fields data as it is retrieved from and inserted into the database. You can see that in the GetValidatedString method we are also performing some validation and providing a message to the user if the data is incorrect.
Now add a text file to your solution and name it TelephoneFieldControl.ascx Copy and past the following into the TelephoneFieldControl.ascx file…
<%@ Control Language="C#" Debug=true %>
<%@Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@Register TagPrefix="SharePoint" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" namespace="Microsoft.SharePoint.WebControls"%>
<SharePoint:RenderingTemplate ID="TelephoneFieldControl" runat="server">
<Template>
<script language="javascript">
<!-- This script is based on the javascript code of Roman Feldblum (web.developer@programmer.net) -->
<!-- Original script : http://javascript.internet.com/forms/format-phone-number.html -->
<!-- Original script is revised by Eralper Yilmaz (http://www.eralper.com) -->
<!-- Revised script : http://www.kodyaz.com -->
var zChar = new Array(' ', '(', ')', '-', '.');
var maxphonelength = 13;
var phonevalue1;
var phonevalue2;
var cursorposition;
function ParseForNumber1(object){
phonevalue1 = ParseChar(object.value, zChar);
}
function ParseForNumber2(object){
phonevalue2 = ParseChar(object.value, zChar);
}
function backspacerUP(object,e) {
if(e){
e = e
} else {
e = window.event
}
if(e.which){
var keycode = e.which
} else {
var keycode = e.keyCode
}
ParseForNumber1(object)
if(keycode > 48){
ValidatePhone(object)
}
}
function backspacerDOWN(object,e) {
if(e){
e = e
} else {
e = window.event
}
if(e.which){
var keycode = e.which
} else {
var keycode = e.keyCode
}
ParseForNumber2(object)
}
function GetCursorPosition(){
var t1 = phonevalue1;
var t2 = phonevalue2;
var bool = false
for (i=0; i<t1.length; i++)
{
if (t1.substring(i,1) != t2.substring(i,1)) {
if(!bool) {
cursorposition=i
bool=true
}
}
}
}
function ValidatePhone(object){
var p = phonevalue1
p = p.replace(/[^/d]*/gi,"")
if (p.length < 3) {
object.value=p
} else if(p.length==3){
pp=p;
d4=p.indexOf('(')
d5=p.indexOf(')')
if(d4==-1){
pp="("+pp;
}
if(d5==-1){
pp=pp+")";
}
object.value = pp;
} else if(p.length>3 && p.length < 7){
p ="(" + p;
l30=p.length;
p30=p.substring(0,4);
p30=p30+")"
p31=p.substring(4,l30);
pp=p30+p31;
object.value = pp;
} else if(p.length >= 7){
p ="(" + p;
l30=p.length;
p30=p.substring(0,4);
p30=p30+")"
p31=p.substring(4,l30);
pp=p30+p31;
l40 = pp.length;
p40 = pp.substring(0,8);
p40 = p40 + "-"
p41 = pp.substring(8,l40);
ppp = p40 + p41;
object.value = ppp.substring(0, maxphonelength);
}
GetCursorPosition()
if(cursorposition >= 0){
if (cursorposition == 0) {
cursorposition = 2
} else if (cursorposition <= 2) {
cursorposition = cursorposition + 1
} else if (cursorposition <= 5) {
cursorposition = cursorposition + 2
} else if (cursorposition == 6) {
cursorposition = cursorposition + 2
} else if (cursorposition == 7) {
cursorposition = cursorposition + 4
e1=object.value.indexOf(')')
e2=object.value.indexOf('-')
if (e1>-1 && e2>-1){
if (e2-e1 == 4) {
cursorposition = cursorposition - 1
}
}
} else if (cursorposition < 11) {
cursorposition = cursorposition + 3
} else if (cursorposition == 11) {
cursorposition = cursorposition + 1
} else if (cursorposition >= 12) {
cursorposition = cursorposition
}
var txtRange = object.createTextRange();
txtRange.moveStart( "character", cursorposition);
txtRange.moveEnd( "character", cursorposition - object.value.length);
txtRange.select();
}
}
function ParseChar(sStr, sChar)
{
if (sChar.length == null)
{
zChar = new Array(sChar);
}
else zChar = sChar;
for (i=0; i<zChar.length; i++)
{
sNewStr = "";
var iStart = 0;
var iEnd = sStr.indexOf(sChar[i]);
while (iEnd != -1)
{
sNewStr += sStr.substring(iStart, iEnd);
iStart = iEnd + 1;
iEnd = sStr.indexOf(sChar[i], iStart);
}
sNewStr += sStr.substring(sStr.lastIndexOf(sChar[i]) + 1, sStr.length);
sStr = sNewStr;
}
return sNewStr;
}
</script>
<asp:TextBox ID="txtNumber" runat="server" MaxLength="15" Size="20" οnkeydοwn="javascript:backspacerDOWN(this,event);" οnkeyup="javascript:backspacerUP(this,event);"/>
</Template>
</SharePoint:RenderingTemplate>
This file is defining what the display of the field on the edit form will look like. It contains javascript to perform the formatting of the text and also a textbox control named txtNumber. It would probly be best to put the javascript in an include file but for now we will just include it inline w/ the field.
Now create another new class file and name it TelephoneFieldControl.cs. This will contain the codebehind for the ascx file we created previously. Add the following using directives to the top of the TelephoneFieldControl.cs file…
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
using Microsoft.SharePoint.WebControls;
Then add the following code to the body of the class…
public class TelephoneFieldControl : BaseFieldControl
{
protected TextBox txtNumber;
protected override string DefaultTemplateName
{
get
{
return "TelephoneFieldControl";
}
}
public override object Value
{
get
{
EnsureChildControls();
return txtNumber.Text.Trim();
}
set
{
EnsureChildControls();
txtNumber.Text = (string)this.ItemFieldValue;
}
}
public override void Focus()
{
EnsureChildControls();
txtNumber.Focus();
}
protected override void CreateChildControls()
{
if (Field == null) return;
base.CreateChildControls();
if (ControlMode == Microsoft.SharePoint.WebControls.SPControlMode.Display)
return;
txtNumber = (TextBox)TemplateContainer.FindControl("txtNumber");
if (txtNumber == null)
throw new ArgumentException("txtNumber is null. Corrupted TelephoneFieldControl.ascx file.");
txtNumber.TabIndex = TabIndex;
txtNumber.CssClass = CssClass;
txtNumber.ToolTip = Field.Title + " Phone Number";
}
}
The DefaultTemplateName property lets sharepoint know which rendering template to use this class for. The value property controls how the data that SharePoint provides gets parsed into our control. The rest of the code is pretty standard as far as ascx controls go.
To finish the assembly project go ahead and create a key file and sign the assembly. It needs to be signed so we can install it into the GAC.
Before we can deploy the new field type we need to create a file called FLDTYPES_Telephone.xml. This is esentially a definition file that makes SharePoint aware of the new field type. Copy and paste the follow into the FLDTYPES_Telephone.xml file (Make sure to replace the assembly name and public key token in the FieldTypeClass element with the correct values for your assembly)…
<?xml version="1.0" encoding="utf-8"?>
<FieldTypes>
<FieldType>
<Field Name="TypeName">PhoneNumber</Field>
<Field Name="ParentType">Text</Field>
<Field Name="TypeDisplayName">Phone Number</Field>
<Field Name="TypeShortDescription">Phone Number</Field>
<Field Name="UserCreatable">TRUE</Field>
<Field Name="ShowInListCreate">TRUE</Field>
<Field Name="ShowInSurveyCreate">TRUE</Field>
<Field Name="ShowInDocumentLibraryCreate">TRUE</Field>
<Field Name="ShowInColumnTemplateCreate">TRUE</Field>
<Field Name="FieldTypeClass">TelephoneFieldType.TelephoneField,TelephoneFieldType,Version=1.0.0.0,Culture=neutral,PublicKeyToken=722d1e996cfca5d7</Field>
<!-- We will implement this later to add display formating for the fields value. ie (xxx)xxx-xxxx
<RenderPattern Name="DisplayPattern">
<Switch>
<Expr>
<Column />
</Expr>
<Case Value="" />
<Default>
<HTML><![CDATA[^]]></HTML>
<Column HTMLEncode="TRUE" />
</Default>
</Switch>
</RenderPattern>
-->
</FieldType>
</FieldTypes>
I’ve commented out the render pattern section but it basically allows you to control how the field data is rendered in standard list views, headers, ect. w/ the use of CAML expressions. Its pretty powerful. There is more detailed info about render patterns in the wss3 SDK.
Deploying the custom field
- Build your assembly and install it in the GAC on the server.
- Copy the ascx control to C:/Program Files/Common Files/Microsoft Shared/web server extensions/12/TEMPLATE/CONTROLTEMPLATES
- Copy the FLDTYPES_Telephone.xml to C:/Program Files/Common Files/Microsoft Shared/web server extensions/12/TEMPLATE/XML
- Reset IIS
Now when you add fields to a list you should have to option of choosing Phone Number as a field type. Go try it out!
Resources used to write this post:
- http://blog.joycode.com/choral/articles/78026.aspx (Youll probly wanna bable fish it cuz its in Chinese)