从这次开始,将逐步介绍如何创建一个自定义字段类型,以及通过什么样的手段来完成我们需要的功能。
随着例子从简单到复杂,所涉及的内容也有所增长,所以大致的计划流程是这样的:字段类型、xml描述文件、输入控件、值类型、自定义属性控件、自定义属性bug的解决方案、(自定义字段间的通信),大概先分为这几期吧(看来是一个任重而道远的工程……)
那么本期先通过一个最简单的自定义字段类型的例子,来看一下如何创建自定义字段类型中的字段类。
在这个例子中,我们将编写一个Email地址的字段,它可以完成Email地址的合法性验证。
首先,自定义字段类型在SharePoint Extension for Visual Studio 2005里其实是有模板的,但是我们在创建一个项目的时候却看不到它,其实它的创建流程是这样的:
先创建一个空的SharePoint解决方案:
然后在新建一个Item的时候,在可选的模板中就能够看到一个“Field Control”了,选择它:
点确定之后,我们会发现模板为我们创建出了两个文件,xxx.Field.cs和xxx.FieldControl.cs,这两个文件分别定义了字段的类和字段的输入控件。并且自动生成了一个强命名的key,使得我们可以把编译得到的dll放到GAC中。这次主要对字段类进行介绍。
在上上次中介绍过SharePoint的内置的字段类型,每一个字段类型都有自己的类(例如单行文本对应SPFieldText),这些类全部都是SPField的子类。所以,当我们编写一个自己的字段类型的时候,也需要让这个字段类型是SPField的子类,这样才能够完成字段类型的功能。但实际上,我们往往并不需要直接继承SPField,而是找一个与我们所需要的功能最接近的字段类型继承它,这样的优点就是我们可以使用这种内置字段类型的一些特殊属性(比如单行文本中的最长字符数限制等),并且能够使用内置字段类型的默认输入界面,另一个优点就是如果要直接继承SPField,要多写一个东西(但是我忘了是什么了……汗……)。
在这个例子中,Email地址的输入和单行文本非常类似,于是我们继承SPFieldText。
好,可能有人会发现了,默认由模板创建出来的这个项目中它就是继承了SPFieldText的,在我们继续之前,不妨先来看一看这默认生成的代码中都有什么东西。
我们已经看到默认的模板创建出来了Field类和FieldControl类,并且Field类继承了SPFieldText,也就是单行文本。然后在这个Field类中,我们还可以看到如下两大部分内容:
第一部分:构造函数
public EmailFieldField(SPFieldCollection fields, string fieldName) : base(fields, fieldName) { } public EmailFieldField(SPFieldCollection fields, string typeName, string displayName) : base(fields, typeName, displayName) { }
SPFieldText并不支持不带参数的默认构造函数,所以要重载这两种构造函数,而这两种构造函数也是SPField所使用的,默认情况下直接使用相同的参数调用父类的构造函数。通过Reflector我们可以看到,在SPField的这两种构造函数中都是根据参数设置了一些初始值。因此,如果需要有一些特殊的初始值要设置,我们可以放在这个构造函数中,例如如果我们希望这个Email地址栏只在新建界面中出现,而不让用户在默认界面上修改它,我们可以在构造函数中添加:
this.ShowInEditForm = false;
我们对这两个构造函数的修改一般也仅限于此类操作了。(其实我们几乎很少需要手动去调用Field系列的构造函数,在创建一个字段时我们往往使用的是SPFieldCollection的Add方法或者AddLookup方法)
在一个自定义的字段类型中,这个构造函数是字段类唯一一个必须重载的地方,其他内容都可以不写(当然如果你其他内容都不写的话,这个字段比起它的父类来说,无非就是多了一些初始值——当你需要这么做的时候,你就可以很简单地这么干来创建一种新的字段类型。。。)
第二部分:关联的控件
在默认生成的代码中,重载了SPFieldText的一个属性叫做FieldRenderingControl。顾名思义,这个只读属性是用来返回与此字段相关联的显示/输入控件的。这部分内容我们放在下下次的内容做介绍。
在这个Email地址的例子中,我们可以直接使用SPFieldText字段类型的默认输入控件(至于显示我们可以放到xml里来定义),因此我们无需重载它,把它删掉好了(同时也可以把那个xxxx.FieldControl.cs也删掉,而且它其实就是空的……)
既然是自定义字段类型,那么必然和默认的单行文本要有所区别,在这个例子中,我们所提供的额外功能就是Email地址的验证,于是我们就需要:
第三部分:数据验证
数据验证功能一般来说都是放在输入界面里做的,但是其实我们也可以把类似的功能放到字段类中(因为我们使用的是默认输入界面)。
在SPField中,有这样一个方法我们可以重载:
public override string GetValidatedString(object value)
这个方法的目的就是根据这个字段的值(object),返回一个通过验证的字符串。其主要目的第一在于验证数据,第二在于做数据串行化(Serialization),把数据值都转换成字符串。这个方法在每次提交值的时候都会执行到,所以可以用来进行数据验证。
在重载这个方法的时候,一般我们都先需要判断一下数据是否为空,然后再做真正的自定义的数据校验。如果在校验过程中发现了不合法的数据,我们需要使用一个特殊的Exception类:SPFieldValidationException。这个异常的表现形式就是我们在填错什么东西时,字段输入控件下面会出现的那行红字。
在这个例子中,我们使用正则表达式来验证Email地址,整个函数的函数体如下:
public override string GetValidatedString(object value) { if (Required && (value == null || value.ToString() == "")) throw new SPFieldValidationException(SPResource.GetString(Strings.MissingRequiredField)); // Validate email format string emailRE = @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"; if (!Regex.IsMatch(value.ToString(), emailRE)) throw new SPFieldValidationException("Invalid Email Address."); if (value == null) return string.Empty; else return value.ToString(); }
这个例子写到现在,功能已经完成了,这就是最简单的一个自定义字段类型的开发。当然到现在我们还不能用它,还需要写一个xml描述文件,放在下次介绍。
那么在这个字段类中,我们通常还可以写哪些内容呢?
第四部分:自定义属性
每个字段类型都有它自己特殊的一些属性,我们称之为自定义字段属性(比如单行文本的最大长度、数字的最大值最小值这样的)。
在SDK中编写自定义属性的方法中,并不需要在字段类中加什么内容,但由于那个bug的存在,在两种解决方案中,都需要在字段类中加上自己的属性。有关这一部分,也等到专门自定义属性的时候再说。
第五部分:获取字段值
在SPField中,获取一个字段的值通常有多种手段,完成不同的用途。例如刚刚介绍过的那个GetValidatedString。
有时为了完成其他一些用途,或者和我们其他的一些代码配合,可能需要重载这些方法,它们有:
public object GetFieldValue(string value)
——根据字符串的值获得真正的值类型(简单类型如double,复杂类型如SPFieldxxxxValue),可以把它理解为反串行化的过程。
public string GetFieldValueAsText(object value)
——根据值得到文本,与GetValidatedString方法不同,这个方法并不是总被执行,它主要还是用于显示。
public string GetFieldValueAsHtml(object value)
——和上面的方法类似,只不过是用Html的格式来显示,这两个方法已经在(0)里面提到过了。
public string GetFieldValueForEdit(object value)
——在编辑时获取一个字符串,其实这是一个有点奇怪的方法,因为后面我们在介绍输入控件的时候,一般都是用object作为Value的。难道Edit时的value会和一般的value不同么?我发现这个方法的重载是在SPFieldNumber里,当它设置为用百分比显示时,想一想编辑时填的值和真正的值有什么区别(30% vs 0.3),就可以理解它的意义了。
上面这些方法就不再一一进行介绍了,有兴趣的可以用Reflector看一看Microsoft.SharePoint.dll中,内置的那些字段类型是怎么写的,会有很大的帮助。
第六部分:其他
其实,上面的那些内容除了自定义属性之外,都是来自SPField的属性或者方法,当然我们也可以根据需要重写它的其他属性和方法。也可以写自己的……
言归正传,我们已经写了一个最简单的Email地址字段类,那么怎么样使用和部署?用什么方式对它进行描述?请看下期……
(未完待续)