定义自定义的field types
====================
在前面的部分, 你看到了使用WSS内建的field type(Text, Choice, 和Notes)来创建自定义的site column definitions 的例子. WSS 3.0还引入了在一个更低的层次上工作的能力, 你可以定义你自己自定义的custom field type. 这样做的动机是获得对column之底层的初始化, 渲染, 和数据合法性验证的更高水平的控制.
比如说, 你需要为一个custom list创建一个column, 这个column展现一个user, 还带有一个drop-down list, 里面的数据时从一个后台数据库或者web service中获得的. 另一个场景是, 你需要一个column, 其中的数值被限制为必须满足domain-specific的合法性验证逻辑, 这个逻辑是你用托管代码(C# 或Visual Basic .NET)编写的. 在这些场景下, 创建一个custom field type是合情合理的.
custom field type 代表着一个column的新的数据类型. Custom field types 对于.net 的developer是有吸引力的, 因为他们使用托管代码编写的, 可以被编译为程序集然后部署到GAC中. 除了让托管代码执行初始化, 合法性验证以外, custom field type 还在一个或多个ASP.NET server-side controls 里有定义, 这给了你控制渲染的能力, 还使得你可以使用标准ASP.NET 开发中的流行技术.
为了创建一个custom field type, 你应该在Visual Studio中创建一个新的class library工程. 这一章的实例代码已经包含了一个例子工程, 名字叫做LitwareFieldTypes , 其中提供了所有用于实现和部署两个custom field types所需的代码. 注意, LitwareFieldTypes 工程被配置为编译到它自己的output之下, 带有一个强命名, 所以呢它可以被部署到GAC中. 部署到GAC中是部署和测试custom field type所需要的.
在源文件中, 包含着一个叫做LitwareFieldTypes.cs的代码文件, 其中有两个托管的类用来定义名为CompanySize的custom field type. 第一个类名字叫做CompanySizeField , 被用来定义custom field type 本身. 这个类继承自SPFieldText 类, SPFieldText 是定义在核心的WSS程序集Microsoft.SharePoint.dll中的. 第二个类名为CompanySizeFieldControl. 这个类是用来创建和初始化一个ASP.NET的DropDownList 控件的, 这个控件会给我们custom field type 一个自定义的渲染行为. 注意CompanySizeFieldControl 类继承自Microsoft.SharePoint.dll 中的一个名为BaseFieldControl的类.
提示:
完整的custom field type base classes(custom field type基类), 可以参考SDK中的“Custom Field Type Classes”主题. 所有的Field Type名字都是由SPField开始的, 包括Boolean, choice, currency, date, text, URL, 和multiple column fields.
下面的代码属于CompanySizeField类. 这个类提供了所有custom field types都会使用的两个标准的构造函数. 还有一个公共的只读属性, 叫做FieldRenderingControl , 它是用来创建CompanySizeFieldControl 类的实例的. 最后, 有一个方法名为GetValidatedString的重写了的方法. 你重写这个方法来添加你自己的自定义合法性验证逻辑. 在我们的例子中, 我们简单地验证了一下这个代码是不是empty的. 然而, 你应该添加额外的验证逻辑, 来满足你复杂商业应用的需要.
The CompanySizeField Class
public class CompanySizeField : SPFieldText { // each field type requires two standard constructors public CompanySizeField(SPFieldCollection fields, string fieldName) : base(fields, fieldName) { } public CompanySizeField(SPFieldCollection fields, string typeName, string displayName) : base(fields, typeName, displayName) { } // public property used to instantiate control used for rendering public override BaseFieldControl FieldRenderingControl { get { BaseFieldControl control = new CompanySizeFieldControl(); control.FieldName = this.InternalName; return control; } } // Standard method override used to add validation logic public override string GetValidatedString(object value) { if (this.Required || value.ToString().Equals(string.Empty)) { throw new SPFieldValidationException("Company size not assigned"); } return base.GetValidatedString(value); } }
下一步, 我们来看看CompanySizeFieldControl 是如何被书写, 以便于与名为CompanySizeFieldControl.ascx的ASP.NET User Control 来协同工作的. 特别地, CompanySizeFieldControl 类使用名为RenderingTemplate 的控件, 该控件定义在CompanySizeFieldControl.ascx中. 你还应该注意到在install.bat中, 还书写了命令把CompanySizeFieldControl.ascx 文件拷贝到它需要被部署的目录TEMPLATE\CONTROLTEMPLATES 之下. 正如你所看到的, CompanySizeFieldControl.ascx 中RenderingTemplate 控件的定义并不是非常复杂.
<SharePoint:RenderingTemplate ID="CompanySizeFieldControl" runat="server"> <Template> <asp:DropDownList ID="CompanySizeSelector" runat="server" /> </Template> </SharePoint:RenderingTemplate>
现在, 我们来看看下面的CompanySizeFieldControl 的实现. 这个类别敖汉一个叫做CompanySizeSelector 的field, 它基于名为DropDownList的ASP.NET控件. 还有, 在CreateChildControls 方法中, 还有代码把这个field与定义在CompanySizeFieldControl.ascx中的DropDownList 的实例绑定了起来. 这使得这个类中的代码可以用一系列的items来实例化DropDownList控件.
The CompanySizeFieldControl Class
public class CompanySizeFieldControl : BaseFieldControl { protected DropDownList CompanySizeSelector; protected override string DefaultTemplateName { get { return "CompanySizeFieldControl"; } } public override object Value { get { EnsureChildControls(); return this.CompanySizeSelector.SelectedValue; } set { EnsureChildControls(); this.CompanySizeSelector.SelectedValue = (string)this.ItemFieldValue; } } protected override void CreateChildControls() { if (this.Field == null || this.ControlMode == SPControlMode.Display) return; base.CreateChildControls(); this.CompanySizeSelector = (DropDownList)TemplateContainer.FindControl("CompanySizeSelector"); if (this.CompanySizeSelector == null) throw new ConfigurationErrorsException("Error: cannot load .ASCX file!"); if (!this.Page.IsPostBack) { this.CompanySizeSelector.Items.AddRange(new ListItem[] { new ListItem(string.Empty, null), new ListItem("Mom and Pop Shop (1-20)", "1-20"), new ListItem("Small Business (21-100)", "21-100"), new ListItem("Medium-sized Business (101-1000)", "101-1000"), new ListItem("Big Business (1001-20,000)", "1001-20000"), new ListItem("Enterprise Business (over 20,000)", "20000+")}); } } }
CompanySizeFieldControl 类重写(overrides )了一个名为DefaultTemplateName的只读属性, 这个属性返回在CompanySizeFieldControl.ascx中定义的RenderingTemplate 的名字. CompanySizeFieldControl 类的基类中有代码使用这个字符串来在运行时从CompanySizeFieldControl.ascx加载RenderingTemplate.
你还应该检查CompanySizeFieldControl 类中被重写了的Value 属性. 内部的get方法从DropDownList 控件返回值. 内部的set方法使用基类中定义的ItemFieldValue 属性, 把这个值赋给DropDownList 控件.
现在你已经看过在ascx文件中的两个类和一个RenderingTemplate 是如何协同工作来提供一个custom field type的实现的了. 这个谜题的最后一部分就是让WSS识别到你已经通过在一个基于CAML的文件中添加一个Field Schema , 而引入一个新的custom field type 到服务器场中了, 这个文件会被拷贝到一个大家都熟知的地方.
Field types 被定义在名为fldtypes*.xml 的文件中, 这些文件必须被部署在TEMPLATE \XML 目录下. 在我们的例子里, 我们的名为CompanySize 的custom field type 的field schema , 使用被使用CAML被定义在名为fldtypes_Litware.xml的文件中. 用来定义field schema的CAML再下面列了出来. 请注意, install.bat会在你编译工程, 安装名为LitwareFieldTypes.dll 的程序集到GAC中的时候, 自动地拷贝这个文件到TEMPLATE\XML目录下. 编译工程之后, 你就能使用CompanySize 这个custom field type了.
A Custom Field Type Definition File
<?xml version="1.0" encoding="utf-8" ?> <FieldTypes> <FieldType> <Field Name="TypeName">CompanySize</Field> <Field Name="ParentType">Text</Field> <Field Name="TypeDisplayName">Company Size</Field> <Field Name="TypeShortDescription">Company Size</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"> LitwareFieldTypes.CompanySizeField, LitwareFieldTypes, ... </Field> <RenderPattern Name="DisplayPattern"> <Switch> <Expr> <Column/> </Expr> <Case Value=""></Case> <Default> <HTML><![CDATA[<span style="color:Red"><b>]]></HTML> <Column SubColumnNumber="0" HTMLEncode="TRUE"/> <HTML><![CDATA[</b></span>]]></HTML> </Default> </Switch> </RenderPattern> </FieldType> </FieldTypes>在custom field type被部署到服务器场中了之后, 你能够使用它来添加一个新的column到列表中, 也可以通过基于浏览器的WSS界面来创建一个新的site column. 下图展现了
CompanySize 出现在WSS秀给用户的标准页面里为列表添加一个新column的例子.
在列表里创建了一个新的基于CompanySize 这个custom field type类型的column后, 你可以测试它的样子和行为. 下图展现了一个为基于CompanySize field type的列复制的例子. 注意, 这个custom field type渲染成一个DropDownList 控件, 其中显示用户预定义的一系列值. 尽管这个例子中, 我们在DropDownList 中使用的是hard-coded的item, 你自己写的时候可以扩展这个代码为从其他数据源获得item的值.
摘译自:
Inside Microsoft Windows SharePoint Services 3.0 第六章