演练:创建自定义字段类型
本主题提供了在以下最常见情况下创建自定义字段类型的分步指南:在列表视图和“显示”模式下字段的呈现由字段定义文件中的 RenderPattern 处理,而在“新建”和“编辑”模式下字段的呈现由与 CreateChildControls 方法一起使用的单个呈现模板处理。您将创建一个字段,以存储 10 位数的国际标准书号 (ISBN)。
先决条件
此演练要求您在运行 Windows Server 2003 操作系统的计算机上安装以下应用程序:
Windows SharePoint Services 3.0
Microsoft Visual Studio 2005
Microsoft Visual Studio 2005 Extensions for Windows SharePoint Services 3.0 1.0 或更高版本
设置项目
在 Visual Studio 2005 中,选择“文件”菜单上的“新建项目”。
在“新建项目”对话框中,在“项目类型”窗口中选择“SharePoint”。(如果该窗口中没有 SharePoint 项目选项,则需要安装 Visual Studio 2005 Extensions for Windows SharePoint Services 3.0 1.0 或更高版本。)
在“模板”窗口中选择“空白”。
在“名称”框中,键入 ISBN_Field_Type。
单击“确定”。
在“解决方案资源管理器”中,右键单击项目名称 ISBN_Field_Type,并选择“属性”,然后选择“生成事件”选项卡。
在“后期生成事件命令行”框中键入下面的内容。这些行可确保每当重新生成项目时,均会将最新版本的项目文件复制到正确的位置,并可确保重新启动 Windows SharePoint Services 3.0 以便加载最新版本的程序集。
cd "$(ProjectDir)" "%programfiles%\microsoft visual studio 8\sdk\v2.0\bin\gacutil" /i "$(TargetPath)" /nologo /f %systemroot%\system32\iisapp.vbs /a "SharePoint_App_Pool" /r xcopy *.ascx "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\CONTROLTEMPLATES\" /y xcopy fldtypes*.xml "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML\" /y
用分配给您的 Windows SharePoint Services 3.0 Web 应用程序的 Internet Information Server (IIS) 应用程序池的实际名称替换 SharePoint_App_Pool。此名称通常与承载应用程序的 IIS 网站的名称相同;例如“SharePoint - 80”。(如果此名称中没有空格,则可以省略引号。)
单击该选项卡上已启用的任何其他控件,以便 Visual Studio 能够检测到您所做的更改,同时选项卡标签上会显示一个星号,然后从“文件”菜单中选择“全部保存”。
在“解决方案资源管理器”中,右键单击项目名称 ISBN_Field_Type,选择“添加”,再选择“新建项目”。
在“添加新项”对话框中,在“类别”窗口中选择“SharePoint”,然后在“模板”窗口中选择“字段控件”。
在“名称”框中,键入 ISBN,然后单击“添加”。此操作会创建两个将在后续步骤中编辑的文件 ISBN.Field.cs 和 ISBN.FieldControl.cs。同时,还会创建一个强名称并将其存储到您的项目中称为 Temporary.snk 的文件中。
在“解决方案资源管理器”中,右键单击项目名称 ISBN_Field_Type,并选择“属性”。
打开“应用程序”选项卡,将“程序集名称”更改为 MyCompany.SharePoint.ISBN_Field_Type。(在此演练中,使用您的公司的名称替换 MyCompany。)
将“默认命名空间”更改为 MyCompany.SharePoint。
单击工具栏上的“全部保存”按钮。
在“解决方案资源管理器”中,右键单击“引用”节点,然后选择“添加引用”。
在“添加引用”对话框上,打开“浏览”选项卡,导航到 C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\
。
选择 PresentationFramework.dll
,然后单击“确定”。(此程序集包含 ValidationRule 类的定义,将在您在下一过程中创建的文件中引用该类。)
创建验证规则类
在“解决方案资源管理器”中,右键单击项目名称 ISBN_Field_Type,选择“添加”,再选择“类”。
在“类别”窗口中选择“Visual C# 项目项”,然后在“模板”窗口中选择“类”。
在“名称”框中,键入 ISBN10ValidationRule.cs,然后单击“添加”。
在所创建的 ISBN10ValidationRule.cs
文件中,添加下面的 using 语句:
C#
using System.Text.RegularExpressions; using System.Windows.Controls; using System.Globalization;
更改命名空间,使它们符合 Namespace Naming Guidelines 中的准则。在此演练中,使用 MyCompany.System.Windows.Controls。
用下面的代码替换类声明:
C#
public class ISBN10ValidationRule : ValidationRule { private const Int32 ISBNMODULO = 11; public override ValidationResult Validate(object value, CultureInfo cultureInfo) { String iSBN = (String)value; String errorMessage = ""; Regex rxISBN = new Regex(@"^(?'GroupID'\d{1,5})-(?'PubPrefix'\d{1,7})-(?'TitleID'\d{1,6})-(?'CheckDigit'[0-9X]{1})$"); if (!rxISBN.IsMatch(iSBN)) { errorMessage = "An ISBN must have this structure:\n1-5 digit Group ID, hyphen, \n1-7 digit Publisher Prefix, hyphen, \n1-6 digit Title ID, hyphen, \n1 Check Digit (which can be \"X\" to indicate \"10\").\n"; } if (errorMessage == "") // Matched the RegEx, so check for group length errors. { Match mISBN = rxISBN.Match(iSBN); GroupCollection groupsInString = mISBN.Groups; String groupID = groupsInString["GroupID"].Value; String pubPrefix = groupsInString["PubPrefix"].Value; if ((groupID.Length + pubPrefix.Length) >= 9) { errorMessage = "The Group ID and Publisher Prefix can total no more than 8 digits.\n"; } String titleID = groupsInString["TitleID"].Value; if (((groupID.Length + pubPrefix.Length) + titleID.Length) != 9) { errorMessage = errorMessage + "The Group ID, Publisher Prefix, and \nTitle ID must total exactly 9 digits.\n"; } if (errorMessage == "") //No group length errors, so verify the check digit algorithm. { Int32 checkDigitValue; String checkDigit = groupsInString["CheckDigit"].Value; // To ensure check digit is one digit, "10" is represented by "X". if (checkDigit == "X") { checkDigitValue = 10; } else { checkDigitValue = Convert.ToInt32(checkDigit); } String iSBN1st3Groups = groupID + pubPrefix + titleID; //Concatenate without the hyphens. // Sum the weighted digits. Int32 weightedSum = (10 * Convert.ToInt32(iSBN1st3Groups.Substring(0, 1))) + (9 * Convert.ToInt32(iSBN1st3Groups.Substring(1, 1))) + (8 * Convert.ToInt32(iSBN1st3Groups.Substring(2, 1))) + (7 * Convert.ToInt32(iSBN1st3Groups.Substring(3, 1))) + (6 * Convert.ToInt32(iSBN1st3Groups.Substring(4, 1))) + (5 * Convert.ToInt32(iSBN1st3Groups.Substring(5, 1))) + (4 * Convert.ToInt32(iSBN1st3Groups.Substring(6, 1))) + (3 * Convert.ToInt32(iSBN1st3Groups.Substring(7, 1))) + (2 * Convert.ToInt32(iSBN1st3Groups.Substring(8, 1))) + checkDigitValue; Int32 remainder = weightedSum % ISBNMODULO; // ISBN is invalid if weighted sum modulo 11 is not 0. if (remainder != 0) { errorMessage = "Number fails Check Digit verification."; } if (errorMessage == "") // Passed check digit verification. { return new ValidationResult(true, "This is a valid ISBN."); }// end check digit verification passed else // the check digit verification failed { return new ValidationResult(false, errorMessage); } }// end no group length errors else // There was some error in a group length { return new ValidationResult(false, errorMessage); } }// end RegEx match succeeded else // There was a RegEx match failure { return new ValidationResult(false, errorMessage); } }// end Validate method }// end ISBN10ValidationRule class
您刚创建的验证规则类包含所有详细验证逻辑。有关验证规则类的详细信息,请参阅 System.Text.RegularExpressions 和 ValidationRule。
创建自定义字段类
打开 ISBN.Field.cs
文件。其中提供了一个 GUID、两个必需的构造函数和 FieldRenderingControl 属性的重写。您基本上不需要修改这些控件。默认情况下,您的新字段类声明为从 SPFieldText 继承。为进行此演练,这是正确的选择。(有关从中派生自定义字段类型的其他类的详细信息,请参阅自定义字段类。
向 ISBN.Field.cs
中添加下面的 using 语句。一定要将“MyCompany”更改为您公司的名称:
C#
using System.Windows.Controls; using System.Globalization; using <em>MyCompany</em>.SharePoint.WebControls; using <em>MyCompany</em>.System.Windows.Controls;
将命名空间更改为 MyCompany.SharePoint。
将 GetValidatedString 方法的以下重写添加到 ISBNField 类中:
C#
public override string GetValidatedString(object value) { if ((this.Required == true) && ((value == null) || ((String)value == ""))) { throw new SPFieldValidationException(this.Title + " must have a value."); } else { ISBN10ValidationRule rule = new ISBN10ValidationRule(); ValidationResult result = rule.Validate(value, CultureInfo.InvariantCulture); if (!result.IsValid) { throw new SPFieldValidationException((String)result.ErrorContent); } else { return base.GetValidatedString(value); } } }// end GetValidatedString
此重写说明了 GetValidatedString 重写的通用模式:
GetValidatedString 方法的重写会检查该字段是否是必需的,如果是,则重写的方法会在值为 null 或为空 String 时引发 SPFieldValidationException 异常。如果用户尝试保存要创建或编辑的列表项,则“新建项目”和“编辑文档”页面会捕获该异常。在这种情况下,页面仍保持打开状态,但该异常的 Message 属性会导致在空字段的下方显示一则错误消息。
当值无效时,重写 GetValidatedString 会引发 SPFieldValidationException,从而导致在无效字段下方显示错误消息。
如果值通过自定义验证,则 GetValidatedString 重写会调用基 GetValidatedString。
保存并关闭该文件。
创建字段呈现控件
打开 ISBN.FieldControl.cs
。已为该控件提供了一个 GUID。默认情况下,您的新字段类声明为从 TextField 继承。为进行此演练,这是正确的选择。(有关可从中派生自定义字段控件的其他类的详细信息,请参阅字段呈现控件。
添加以下 using 语句:
using System.Web.UI.WebControls;
将命名空间更改为 MyCompany.SharePoint.WebControls。
如果将在每个 ISBN 编号之前添加“ISBN”前缀的 ASP.NET Label Web 控件的呈现模式为“新建”或“编辑”,则为该控件添加一个受保护的字段。不需要添加受保护的 TextBox 字段来保留 ISBN 编号本身,因为自定义 ISBNFieldControl 从 TextField 继承该字段。
C#
protected Label ISBNPrefix;
接下来添加 DefaultTemplateName 属性的以下重写。您分配给此属性的 String 是您将在稍候步骤中创建然后部署到 C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\CONTROLTEMPLATES
文件夹的 .ascx 文件中 RenderingTemplate 对象的 ID。在此示例中,由于不重写 ControlTemplate、Template 或 TemplateName,因此将按以下方式调用 RenderingTemplate:ControlTemplate 将返回 Template,接着后者将返回由 TemplateName 命名的任意 RenderingTemplate 的 Template 属性。最后,TemplateName 的 get 取值函数将返回 DefaultTemplateName。在更复杂的情况下,例如,您具有一些用于“新建”和“编辑”模式的单独模板,则需要重写前面的一个或多个属性,可能还需要重写 AlternateTemplateName 或 DefaultAlternateTemplateName 属性。
C#
protected override string DefaultTemplateName { get { return "ISBNFieldControl"; } }
添加 CreateChildControls 方法的以下重写:如果基础 ISBNField 为 null(如果 ISBNFieldControl 是独立于 ISBNField 的 FieldRenderingControl 属性的资源库创建的,则它可能为 null,请参见 ISBN.Field.cs
中的 FieldRenderingControl 重写),则重写不执行任何功能。如果 SPControlMode 为“显示”,则该方法也不会执行任何功能。这是因为通常情况下将通过您将此本演练的稍后部分中创建的字段定义中所定义的 RenderPattern 元素来处理字段的“显示”模式呈现。在其他情况下,“显示”模式呈现是由 DisplayTemplateName 属性所标识的特定模板完成的。CreateChildControls 很少在“显示”模式下对自定义字段类型执行任何操作。(有关呈现自定义字段的各种方式的详细信息,请参阅自定义字段呈现模式。)
C#
protected override void CreateChildControls() { if (this.Field != null && this.ControlMode != SPControlMode.Display) { }// end if there is a non-null underlying ISBNField and control mode is not Display //Do nothing if the ISBNField is null or control mode is Display. }
将对基方法的以下调用添加到条件语句的第一行。此类调用通常是必需的,以确保当继承的子控件是由基 CreateChildControls 而不是由模板全部或部分呈现时能够创建这些子控件。例如,DefaultTemplates.ascx
中的“TextField”模板(位于 C:\program files\common files\microsoft shared\web server extensions\12\template\controltemplates
中)呈现子 TextBox,但 CreateChildControls 方法会调整 TextBox 的最大大小,使其符合基础 SPFieldText 字段的最大大小。基 CreateChildControls 还可能创建动态 BaseValidator 控件。但通常情况下,您无权访问基方法的源代码,因此需要进行尝试以确定是否需要调用此基方法,如果需要,则应确定在重写中的调用位置。
C#
// Make sure inherited child controls are completely rendered. base.CreateChildControls();
添加下面的行,以将呈现模板中的子控件与在您的自定义字段控件中声明的(或从其父级继承的)子控件字段相关联。尽管您不会在执行后续步骤之前创建该呈现模板,但您也必须立即执行该操作,因为对基 CreateChildControls 的调用会将继承的子控件与您的自定义字段类的父级所使用的呈现模板相关联,而不是与自定义呈现模板相关联,因此必须使用新的关联替换基础关联。
C#
// Associate child controls in the .ascx file with the // fields allocated by this control. this.ISBNPrefix = (Label)TemplateContainer.FindControl("ISBNPrefix"); this.textBox = (TextBox)TemplateContainer.FindControl("TextField");
在控件关联代码下方添加以下结构。您的代码不应对回发执行任何操作,因为重新初始化回发将取消用户对子控件的值所做的任何更改。
C#
if (!this.Page.IsPostBack) { }// end if this is not a postback //Do not reinitialize on a postback.
在最后一步中所添加的条件结构内,添加下面的内部条件,以便在控件模式为“新建”时,使用默认的 ISBN 值初始化 TextBox 子控件。
C#
if (this.ControlMode == SPControlMode.New) { textBox.Text = "0-000-00000-0"; } // end assign default value in New mode
不需要在“编辑”模式下执行任何操作,因为 OnLoaD 方法会将 ISBNFieldControl.Value 初始化为 ItemFieldValue 的值,以便在内容数据库中保留该字段的当前值。此时,CreateChildControls 的重写应如下所示。
C#
protected override void CreateChildControls() { if (this.Field != null && this.ControlMode != SPControlMode.Display) { // Make sure inherited child controls are completely rendered. base.CreateChildControls(); // Associate child controls in the .ascx file with the // fields allocated by this control. this.ISBNPrefix = (Label)TemplateContainer.FindControl("ISBNPrefix"); this.textBox = (TextBox)TemplateContainer.FindControl("TextField"); if (!this.Page.IsPostBack) { if (this.ControlMode == SPControlMode.New) { textBox.Text = "0-000-00000-0"; } // end assign default value in New mode }// end if this is not a postback //Do not reinitialize on a postback. }// end if there is a non-null underlying ISBNField and control mode is not Display // Do nothing if the ISBNField is null or control mode is Display. }
添加 Value 属性(它是 UI 中字段的属性)的以下重写。如果最终用户更改了该值但尚未保存,则 Value 属性不一定是基础 ISBNField(派生自 SPFieldText)对象的实际值,或内容数据库中字段的值。请注意,调用 EnsureChildControls(将根据需要调用 CreateChildControls)将同时启用 get 取值函数和 set 取值函数。必须调用 EnsureChildControls,除非首先调用基属性,并且您知道该基属性的 set 和 get 取值函数将调用 EnsureChildControls。如果使用类型完全不同的控件(例如下拉列表框)替换了继承自 TextField 的基础 TextBox 子控件,则 set 和 get 取值函数将需要直接设置此控件而不是调用基属性。为确保初始加载控件时使用的是基础 ISBNField 对象中的值,OnLoad 方法会将 ISBNFieldControl.Value 设置为 ItemFieldValue 的值,它是 基础 ISBNField 对象的值。
C#
public override object Value { get { EnsureChildControls(); return base.Value; } set { EnsureChildControls(); base.Value = (String)value; // The value of the ISBNPrefix field is hardcoded in the // template, so it is not set here. } }
创建字段呈现模板
在“解决方案资源管理器”中,右键单击项目名称 ISBN_Field_Type,选择“添加”,再选择“新建项目”。
在“类别”窗口中选择“Visual C# 项目项”,然后在“模板”窗口中选择“文本文件”。
在“名称”框中,键入 ISBNFieldControl.ascx 并单击“添加”。(不得 将该文件放置在项目文件夹的子文件夹中,否则在此演练的前面步骤中创建的后期生成命令将找不到该文件。)
在所创建的 ISBNFieldControl.ascx
文件中,添加以下标记文本:
Xml
<%@ Control Language="C#" %> <%@ 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="ISBNFieldControl" runat="server"> <Template> <asp:Label ID="ISBNPrefix" Text="ISBN" runat="server" /> <asp:TextBox ID="TextField" runat="server" /> </Template> </SharePoint:RenderingTemplate>
请注意有关此标记的以下事实:
RenderingTemplate 的 ID 必须与用于 DefaultTemplateName 属性重写的字符串相同。
模板中 Label 控件的 Text 属性是在此处设置的,因为它不会发生任何变化。
两个控件之间有一个 HTML“ ”元素。
TextBox 定义与在 C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\CONTROLTEMPLATES\DefaultTemplates.ascx
中定义的“TextField”RenderingTemplate 中的定义相同。但必须在此处重复该定义,因为 DefaultTemplateName 重写指向该自定义模板而不是“TextField”模板。在该自定义模板中使用的是相同的 ID,因为 CreateChildControls 基方法(参见上文)可能根据此 ID 引用控件。
保存并关闭该文件。
创建字段类型定义
在 Visual Studio 中,生成该项目。该项目尚未完成,但此时您需要为程序集生成一个 GUID 和一个公钥标记。
在“解决方案资源管理器”中,右键单击项目名称 ISBN_Field_Type,选择“添加”,再选择“新建项目”。
在“类别”窗口中选择“Visual C# 项目项”,然后在“模板”窗口中选择“XML 文件”。
在“名称”框中,键入 fldtypes_ISBNField.xml,然后单击“添加”。(该文件名必须 以“fldtypes”开头。此外,也不能 将其放置到项目文件夹的子文件夹中,否则在此演练的前面步骤中创建的后期生成命令将找不到该文件。)
在所创建的 fldtypes_ISBNField.xml
文件中,添加以下标记文本:
Xml
<?xml version="1.0" encoding="utf-8" ?> <FieldTypes> <FieldType> <Field Name="TypeName">ISBN</Field> <Field Name="ParentType">Text</Field> <Field Name="TypeDisplayName">ISBN</Field> <Field Name="TypeShortDescription">ISBN for a book</Field> <Field Name="UserCreatable">TRUE</Field> <Field Name="ShowOnListCreate">TRUE</Field> <Field Name="ShowOnSurveyCreate">TRUE</Field> <Field Name="ShowOnDocumentLibraryCreate">TRUE</Field> <Field Name="ShowOnColumnTemplateCreate">TRUE</Field> <Field Name="FieldTypeClass"><em>MyCompany</em>.SharePoint.ISBNField, <em>MyCompany</em>.SharePoint.ISBN_Field_Type, Version=1.0.0.0, Culture=neutral, PublicKeyToken=<em>token</em></Field> </FieldType> </FieldTypes>
该文件定义 Windows SharePoint Services 3.0 的自定义字段类型。有关它的元素的用途和含义的详细信息,请参阅自定义字段类型定义、FieldTypes 元素(字段类型)、FieldType 元素(字段类型)以及 Field 元素(字段类型)。请注意,<Field Name="FieldTypeClass"> 元素必须全部在一行上。
在 <Field Name="FieldTypeClass"> 元素中,用您公司的名称替换完全限定类名和程序集名称中的 MyCompany;用您的程序集的公钥标记替换 标记。若要获取公钥标记,请从“工具”菜单中选择“获取程序集公钥”。公钥标记将显示在“输出”窗口中。
现在,将下面的标记文本添加到该文件中最后一个 Field 元素的下方,并在与其他 Field 元素相同的级别上进行缩进:
Xml
<RenderPattern Name="DisplayPattern"> <Switch> <Expr> <Column/> </Expr> <Case Value=""> </Case> <Default> <HTML><![CDATA[ISBN ]]></HTML> <Column HTMLEncode="TRUE"/> </Default> </Switch> </RenderPattern>
该类型为 DisplayPattern 的 RenderPattern 元素在列表视图和“显示”模式下呈现字段。如果字段值(由 Column 元素表示)为空字符串,则它不执行任何操作。如果该字段具有值,则会呈现“ISBN”后跟一个空格,然后显示该字段的值。列表视图模式下的列标题由自定义字段类型将继承自其父“文本”字段类型的 RenderPattern(类型为 HeaderPattern)呈现。(有关呈现模式的详细信息,请参阅 RenderPattern 元素(字段类型))。
小心:
如果您正在使用 1.0 版 Windows SharePoint Services 3.0 工具 Visual Studio 2005 Extensions,请不要使用 Visual Studio“构建”菜单中的“部署”选项,否则会部署一个简化的 fldtypes*.xml
文件来替换您所创建的文件。该简化的版本将不包含该自定义 RenderPattern 模板。
生成和测试自定义字段类型
在“生成”菜单上选择“重新生成”。因为您已在前面创建了后期生成命令,所以会将各种文件自动复制到所需位置。
在您的 SharePoint Web 应用程序中打开一个网站并创建一个名为“Books”的列表。
将一个新列添加到该列表中。在“创建列”页上,输入“ISBN”作为列名称。
单击“ISBN for a book”单选按钮。
单击“是”单选按钮,创建所需字段。
保留“添加到默认视图”复选框为启用状态。
单击“确定”。
向该列表添加项。
在“新建项目”页上,验证该字段是否初始设置为默认值“0-000-00000-0”。
输入无效的 ISBN 值,以查看在尝试保存该项目时所遇到的各种错误。
查看如果将该字段完全留空将会发生什么情况。
最后,输入 0-262-61108-2 或您所知道是有效的另一个值,然后单击“确定”。(如果您获得一个针对有效 ISBN 的错误,请确保该值的末尾没有空格)。
确保列表视图中该值的前面包含“ISBN”和一个空格。
单击项目标题以打开“显示”页面。确保字段的呈现方式与它在列表视图中的呈现方式相同。
单击“编辑项目”以编辑该字段。确保该字段初始设置为其当前值,而不是默认值。
将字段值更改为无效,并确保在编辑模式下出现验证错误,就像在新建模式下一样。