4.2. Custom Field Types

Although the out-of-the-box field types supplied with WSS v3 are numerous (see Figure 4-9), they may still be lacking in the sense that they cannot possibly support all of your data entry and validation needs. Surely, there will be a business need for a type of field type that is simply not provided by the WSS v3 framework. WSS v3 allows you to create your own custom fields to support your specific requirements and needs. You can control the custom field's properties and the field's rendering, as well as the field's validation requirements.

In order to create a custom field type, you first need to create a class that represents it. This class must inherit from one of the base field type classes. You must also create a field type definition file that contains the information that WSS v3 needs to correctly instantiate and run the field type. You can optionally include information that defines how WSS v3 should render the field type. This last task is beyond the scope of this book and will not be demonstrated; however, feel free to peruse the SDK and see how easy it is to accomplish.

4.2.1. Custom Field Type Classes

The process of creating a custom field class, as mentioned above, involves inheriting from either the SPField base class or one of the classes in the table showing field type classes supporting inheritance. Although it is possible to inherit from other field type classes not in this list, doing so is not supported. This class will control how your field data is retrieved from and inserted into the content database.

Here is how you would create a custom field to contain a Social Security number. The following class inherits from SPFieldText, which is the single line of text field. You go through this example in more detail at the end of this section.

using Microsoft.SharePoint;

public class SocialSecurityNumber : SPFieldText
{
}

Figure 4.9. Figure 4-9

ClassDescription
SPFieldBooleanRepresents a Boolean field type.
SPFieldChoiceRepresents a choice field type.
SPFieldCurrencyRepresents a currency field type.
SPFieldDateTimeRepresents a date-time field type.
SPFieldLookupRepresents a lookup field type. The field value for the SPFieldLookup class is contained in the SPFieldLookupValue class.
SPFieldMultiChoiceRepresents a multichoice field type. The field value for the PFieldMultiChoice class is contained in the SPFieldMultiChoiceValue class.
SPFieldMultiLineTextRepresents a multiline text field type.
SPFieldNumberRepresents a number field type.
SPFieldRatingScaleRepresents a ratings field type. The field value for the SPFieldRatingScale class is contained in the SPFieldRatingScaleValue class.
SPFieldTextRepresents a single-line text field type.
SPFieldUrlRepresents a URL field type. The field value for SPFieldUrl is contained in the SPFieldUrlValue class.
SPFieldUserRepresents a Windows SharePoint Services user.
SPFieldMultiColumnRepresents a multicolumn field type. SPFieldMultiColumn is a field type that is hidden from the user interface and is accessible only through the object model. The field value for the SPFieldMultiColumn class is contained in the SPFieldMultiColumnValue class.

The WSS v3 framework provides an extensive set of overridable functions and properties when you choose to derive from either the SPField class or any of the classes listed above. This is extremely valuable because most of the properties and behaviors that will make your custom field type unique are going to be contained in your derived class. For example, the SPField base class contains the members listed in the following table from the SDK, whose implementations can be overridden if necessary for your custom field type should you choose to inherit from SPField.

MemberDescription
FieldRenderingControlReturns the field type control used to render the field. This control is used to render the field in display, edit, and new forms, the Data Form Web Part, and any pages that use field controls.
FieldRenderingMobileControlReturns the field type control used to render the field in mobile applications.
GetFieldValueAsTextReturns the field data value as a string.
GetFieldValueAsHtmlReturns the field data value as a string, formatted as HTML. This HTML-formatted field value is most often used to render the field value directly on a page, the version history page, and other managed code rendering. However, this HTML-formatted field value is not used on the display form. Field rendering for the display form is the same as rendering for list views; that is, it is rendered from the Collaborative Application Markup Language (CAML) definition of the field type display pattern.
GetFieldValueForEditReturns the field value to be rendered in Edit Mode. This method is used for the Edit and New forms.
GetValidatedStringReturns the validated field value. Use this method for custom validation and data serialization logic. This method may, in turn, use the ToString method of the field value object to convert the field value object into a string.
GetFieldValueReturns the field value as a field value object. Use this method when the field type requires a complex data type that is different from that of the parent field type.
FieldValueTypeReturns the field value type. Use this method when the field type contains its own custom value type object.
PreviewValueTypedReturns a preview value of field data, for a design time view of the field control in display and Edit Mode.
OnAddedUse to specify custom field type logic after a field is added.
OnUpdatedUse to specify custom field type logic after a field is updated.
OnDeletingUse to specify custom field type logic before a field is deleted.

4.2.2. Custom Field Value Classes

If you have a need for a custom field type whose data is stored in a format not supported by any of the built-in field types from which you have derived your custom field type, there is no need to worry. WSS v3 has provided you with a custom field value class that you can structure in such a way that you can store any type of data as long as you abide by a couple of simple rules noted here:

  • It must be serializable. Each field value class must implement the ToString method to convert the field value to a format suitable for storage.

  • It must implement two constructors that can simply call the constructors of the parent class.

If your custom field class requires a custom field value class, you will most likely need to override the two methods listed in the following table.

MethodDescription
GetFieldValueThis method converts the specified string into a field type value object. Your custom field object should return the custom field value object when the SPField.GetFieldValue method is invoked. WSS v3 returns this field value object when the SPListItem.this["field name"] method is called.
GetValidatedStringThis method converts the field type value object into a serialized string.

4.2.3. Custom Field Data Validation

One of the most compelling benefits to creating your own custom field types is that you can also include your own custom validation logic. This creates an invaluable potential increase in business value to any SharePoint list. One of the most common complaints heard in the field is that the supplied columns do not provide the necessary validation required by some specific business rules. The options available to you in implementing your custom validation include inheriting the validation from the parent field class that you are deriving from, overriding the parent's validation or a combination of the two. The GetValidatedString method is where you specify your custom validation logic.

public override string GetValidatedString(object value)
{
}

Data validation can be performed in many other places, including form controls. Any server-side data validation logic that you think you will need should be added to the GetValidatedString method. This method is invoked whenever data is stored in a SPField class or a derived class, that is, your custom field type, using any of the following methods:

  • Through a form control

  • Through the user interface

  • Programmatically through the object model

As noted in the SDK, it is possible for users to update the data in a field that does not invoke the GetValidatedString method and therefore, any data validation logic contained in the method. This includes using unmanaged code or web service methods that do not invoke the SPListItem object to update the field data. For example, when a user updates list item data by using the datasheet view, the list items are updated without invoking the GetValidatedString method. It is very important that you remain aware of this and that you test your custom field type in all of the scenarios in which you envision it being used.

Your custom field type class can also use the GetValidatedString method to return string error messages to the calling application or form, allowing you to provide graceful display and handling of any data validation errors that may occur. End users like this type of behavior, so it's probably a good idea to take advantage of this feature. This code may appear in the body of the GetValidatedString function to return an error message that is stored in one of the field properties should the value of the field not be in the correct format.

throw new SPFieldValidationException
{
  GetCustomProperty("ExceptionMessage").ToString());
}

The SDK states that your field type should include validation logic that requires a specified value for the field, if the field is defined as required on a list. If you do not include such logic, users can submit the new or edit form without setting the field value. The Required property specifies whether a field is required.

4.2.4. Custom Field Type Definitions

Now that you have a good understanding of the code required for a custom field type as well as an understanding of the options available to you when creating your custom field types, let's discuss the contents of the field type definition file, the other required component for SharePoint to even be aware of all of the code you have written to support your custom field type. The SDK states that a field type definition file is an XML file that contains the following information.

  • The strong-named assembly (with version number and public key) and class name for the field type

  • The parent class of the field type class

  • Display name and description of the field type

  • Optionally, the schema for the field type properties, and rendering patterns for the field in the user interface

Field type definitions should be placed into an XML file named with the fldtypes*.xml format and placed in the same location as the out-of-the-box field type definition file ([WSS]TEMPLATEXMLFLDTYPES.XML). The out-of-the-box field type definition file, FLDTYPES.XML, should not be modified, since it could be modified with a future service pack, thereby removing any customizations you may have made (even though you shouldn't have). When creating your field type definitions, we encourage you to look at this file for guidance because the built-in field types are defined here and will provide you with most, if not all, of the information, you will need to define yours. A field type definition file can contain definitions for multiple field types, although when creating your own, it is recommended that you place one per file. By having a separate file for each custom field type, many developers can be working on their own field type definitions simultaneously, if needed. Also, it is much clearer to a new team member what a file named fldtypes_ssn.xml is as opposed to a file named fldtypes_custom.xml.

The outermost element in a field type definition is the FieldTypes element. It represents a collection of field types. The FieldType element represents a single field type definition. A FieldType element may contain multiple Field elements, each representing an attribute of a field type. It may also contain a PropertySchema element that represents the schema of the field type properties as well as up to two RenderPattern elements (DisplayPattern and/or HeaderPattern) that define the HTML and script that WSS v3 will use to render the field type in the user interface.

The Field element contains a single required attribute, Name, that represents the name of the specific aspect or property of the field type, which the Field element represents. The following table, from the SDK, summarizes the Field elements available for a given FieldType.

ValueDescription
TypeNameRequired String. Represents the name of the field type. TypeName should be a fixed, nonlocalizable string.
ParentTypeRequired String. Represents the name of the type from which the field class is derived.
TypeShortDescriptionRequired String. Represents the short description of the field type that is displayed in the user interface. TypeShortDescription should be a localizable string.
UserCreatableRequired Boolean. TRUE to enable users to add fields of this field type to lists. FALSE to allow use of the field type in list schemas, but to hide the field type from users.
FieldTypeClassRequired String. Represents the strong name of the field type class library. FieldTypeClass includes the class name and assembly name with Version, Culture, and PublicKeyToken.
ShowOnListAuthoringPagesOptional Boolean. The default is TRUE. Represents whether this field type is displayed for inclusion on lists. If set to TRUE, displays this field type on list authoring pages so that users can include the field type on their lists.
ShowOnDocumentLibraryAuthoringPagesOptional Boolean. The default is TRUE. Represents whether this field type is displayed for inclusion in document libraries. If set to TRUE, displays this field type on document library authoring pages so that users can include the field type in their document libraries.
ShowOnSurveyAuthoringPagesOptional Boolean. The default is TRUE. Represents whether this field type is displayed for inclusion on surveys. If set to TRUE, displays this field type on survey authoring pages so that users can include the field type in their surveys.
ShowOnColumnTemplateAuthoringPagesOptional Boolean. The default is TRUE. Represents whether this field type should be displayed as a column template field type. If set to TRUE, displays this field type on column template authoring pages so that users can select to create a column template of this field type.
AllowBaseTypeRenderingOptional Boolean. The default is FALSE. Represents whether a client application renders the field as its base type, if the client application cannot determine how to properly render the custom field type. If set to TRUE, and the client application cannot properly render the custom field type, the client application renders the field as the default Windows SharePoint Services parent field type from which it inherits. If this is set to TRUE for a field type, then in the field schema for a field of this field type, the Field element must include a BaseRenderingType attribute that specifies the default Windows SharePoint Services parent field type from which the field inherits.

The PropertySchema element represents the schema of the field type properties. The PropertySchema element contains a Fields element, which in turn contains a Field element for each field type property you want to define. You can use standard Collaborative Application Markup Language (CAML) within each Field element. If you define the field type properties in the PropertySchema element, the framework will render those properties based on that schema.

The rendering of our field type properties can also be defined using a field editor user control. By using the PropertySchema element to render your field properties, you are limited to the rendering options available using CAML and you will not be able to specify custom processing or validation handling of your field properties. Best practice dictates that you use this process for simple field type properties that do not require complicated processing or validation and to use a field editor control for field properties that require code to handle complicated property settings and interface rendering. The details of implementing field editor controls are beyond the scope of this book but, as always, are described in detail in the SDK. The two key points to implementing a field editor control are to inherit from the UserControl class (or a class derived from it), and implementing the IFieldEditor interface. This is how you might define a simple text property using a PropertySchema element within a field type definition.

<PropertySchema>
  <Fields>
    <Field
      Name="ExceptionMessage"
      DisplayName="Exception Message"
      MaxLength="255"
      DisplaySize="25"
      Type="Text">
      <Default>Invalid Social Security Number Format</Default>
    </Field>
  </Fields>
</PropertySchema>

The RenderPattern element defines the HTML and script that WSS v3 uses to render the field type in the user interface. It contains a single required attribute, Name, that may contain one of two possible values, DisplayPattern or HeaderPattern. These are the two possible render patterns that can be defined in this file. The patterns for rendering the field type on a New or Edit page are set through the use of form controls. If a particular render pattern in not specified for your custom field type, the field type will inherit the render pattern of its parent.

4.2.5. Deploying Custom Field Types

Custom field types are always deployed to an entire server farm. The WSS v3 Solution Framework should be used to deploy your custom field type and is discussed later in this chapter. At a high level, the steps required to manually deploy a custom field type include the following. They are looked at in greater detail in the following section.

  • The field type class must be deployed as a strong-named assembly to the Global Assembly Cache (GAC).

  • The field type definition XML file must be deployed to the same location as fldtypes.xml. That location is [WSS]TEMPLATEXML. Each field type definition should be included in a file named fldtypes*.xml.

  • If you have specified a custom field editor user control for the field type, it must be deployed to [WSS]TEMPLATECONTROLTEMPLATES.

4.2.6. Creating a Custom Field Type

The following example demonstrates how to create a custom field type to support the entry and validation of a Social Security number. The functionality that exists after you complete your custom field type is such that when a list item is created or updated, if the value entered in a text box does not match the format of a Social Security number specified through the use of a regular expression, the list item will not be saved to the content database and you are notified with a friendly error message. Once created and deployed, your custom field type can be used like any of the built-in field types: as a site column, as well as in content types, document libraries and lists.

4.2.7. Create a Custom Field Type class

The following steps teach you how to create a custom field type class:

  1. Create a new project by clicking Project under the New option on the File menu.

  2. Under Project types, select the Class Library template under Windows in the language of your choice.

  3. In the Location text box, browse to C:Documents and SettingsAdministratorMy DocumentsVisual Studio 2005projects, and click Open.

  4. Uncheck the Create directory for solution check box.

  5. Give your Custom Field Type project a meaningful name such as CustomFieldType, and click OK.

  6. Add a reference to System.Web.

  7. Add a reference to Microsoft.SharePoint. It is assumed that your development environment includes an installation of WSS. This particular assembly is listed as Windows SharePoint Services and is near the bottom of the list.

  8. Delete the Class1.cs file.

  9. Add a new class file, naming it SocialSecurityNumber.cs.

  10. Add a public modifier to the SocialSecurityNumber class.

    NOTE

    Although the last step may not be necessary if the public modifier is, in fact, already there, it is important to confirm that the class you created is indeed public. If the class file is created differently for some reason, and this can happen depending on your environment, the modifier may not be added. In this case, it takes on the default value, which is protected. When this happens, the class will not be visible outside of the assembly, rendering it unusable by WSS.

  11. Add using statements for both the Microsoft.SharePoint and System.Text.RegularExpressions namespaces.

  12. Edit the class declaration to inherit from SPFieldText.

    public class SocialSecurityNumber : SPFieldText
    {
    }

  13. Implement the constructors of the SPFieldText base class.

    public SocialSecurityNumber(SPFieldCollection fields,
      string fieldName)
      : base(fields, fieldName)
      {
      }
    
    public SocialSecurityNumber(SPFieldCollection fields,
      string typeName,
      string displayName)
      : base(fields, typeName, displayName)
      {
      }

The project should compile at this point.

4.2.7.1. Sign the Assembly

Custom field type assemblies must be deployed to the Global Assembly Cache (GAC), so be sure to sign the assembly prior to building it. The following steps guide you through the process of signing your assembly.

  1. Right-click on the project node, and select Properties from the context menu.

  2. Click the Signing tab.

  3. Check the Sign the assembly check box.

  4. Select <New...> from the "Choose a strong name key file" drop-down list.

  5. Enter CustomFieldType in the Key file name text box.

  6. Uncheck the Protect my key file with a password check box.

  7. Click OK.

  8. Save your changes and build the project.

You informed the WSS v3 framework of your custom Site Definition earlier in this chapter; you must also inform it in a similar fashion of your custom field type. To do this, create an XML file prefixed with fldtypes and place it in the [WSS]TEMPLATEXML directory. The following steps guide you through this process.

4.2.7.2. Create a Field Type Definition

Now you create a field type definition.

  1. Add an XML file to your project, and name it fldtypes_SocialSecurityNumber.xml.

  2. Add the required FieldType element to make WSS v3 aware of your new custom field type. The resulting file should resemble the following code snippet:

    <?xml version="1.0" encoding="utf-8" ?>
    <FieldTypes>
      <FieldType>

    <Field Name="TypeName">SocialSecurityNumber</Field>
        <Field Name="ParentType">Text</Field>
        <Field Name="TypeDisplayName">Social Security Number</Field>
        <Field Name="TypeShortDescription">Social Security Number (123-45-6789)</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="Sortable">TRUE</Field>
        <Field Name="Filterable">TRUE</Field>
        <Field Name="FieldTypeClass">
          CustomFieldType.SocialSecurityNumber,
          CustomFieldType,
          Version=1.0.0.0,
          Culture=neutral,
          PublicKeyToken=77d0bef6f861f263
        </Field>
        <Field Name="SQLType">nvarchar</Field>
        <PropertySchema>
          <Fields>
            <Field Name="ExceptionMessage"
              DisplayName="Exception Message"
              MaxLength="255"
              DisplaySize="25"
              Type="Text">
              <Default>Invalid Social Security Number Format</Default>
            </Field>
          </Fields>
        </PropertySchema>
      </FieldType>
    </FieldTypes>

The public key token in the above code must match exactly the public key token of your compiled assembly.

4.2.7.3. Extract the Public Key

The following steps outline the process required to extract the public key token from the compiled assembly.

  1. Open a Visual Studio 2005 Command Prompt window by clicking on Start Programs Microsoft Visual Studio 2005 Visual Studio Tools Visual Studio 2005 Command Prompt.

  2. Type sn -T <assembly> where <assembly> is the complete path to the Field Type's assembly within your project. If this path contains spaces, place it within double quotation marks. The command window should look similar to Figure 4-10.

    Figure 4.10. Figure 4-10

Before you dive into the implementation of the custom field class, it is worth noting that an emerging best practice when developing WSS v3 components is to build the component's frame, deploy it into development, and then add and test your code in an iterative fashion until you are satisfied with the result. This will reduce the time you spend debugging your deployment steps and allow you to concentrate more on the implementation of your custom business rules and other logic. Ideally, your custom field type should be deployed via a WSS v3 Solution, which is discussed later in this chapter. For the time being, you want to automate the deployment of your custom field type assembly to the Global Assembly Cache (GAC) and your fldtypes_SocialSecurityNumber.xml file to the correct location in your development environment's file system so that you can continue to work on it and more importantly view your changes as you go.

4.2.7.4. Automate Deployment

It's time to automate deployment, using the following steps:

  1. Right-click on the project node, and select Properties from the context menu.

  2. Click the Build Events tab.

  3. Enter the following command in the "Post-build event command line" text box to copy the FLDTYPES_SocialSecurityNumber.XML file to the correct location:

    xcopy ....FLDTYPES_SocialSecurityNumber.XML "C:Program FilesCommon
    FilesMicrosoft Sharedweb server extensions12TEMPLATEXML" /y

  4. Enter the following command in the "Post-build event command line" text box to copy the compiled assembly to the GAC:

    "C:Program FilesMicrosoft Visual Studio 8SDKv2.0BinGacUtil.exe" /nologo /i
    "$(TargetPath)" /f

  5. Enter the following command in the "Post-build event command line" text box to recycle the application pool so that your changes will immediately be visible. Be sure to replace MyApplicationPool with the correct name of your application pool.

    C:WINDOWSsystem32cscript.exe C:WINDOWSsystem32iisapp.vbs /a
    "MyApplicationPool" /r

  6. Save your changes and build the project.

At this point, you should be able to add your custom field to a list. Figure 4-11 illustrates what you will see at this point when adding your custom field to a Tasks list called Tasks.

Figure 4.11. Figure 4-11

The SDK states that you must be aware that a custom field class does not inherit the field type property renderings specified in its parent class. For example, if you derive a custom field type from the SPFieldText class, your new custom field type does not inherit the rendering information for any of the field properties of SPFieldText, such as MaxLength. To render any of the parent class's field type properties, you must specify your own renderings, using a field editor user control.

Now that the framework for your custom field type is in place, and it has been successfully deployed, you can add the additional business rules and logic to the code so that it will actually do what it is supposed to do, which is to accept and validate a Social Security number. At this point, it is simply a SPFieldText column without any of the additional field rendering properties that you do not get through inheritance, as noted above.

4.2.7.5. Implement Custom Field Type Functionality

You will need to add functionality to your custom field type. To do so, follow the instructions here:

  1. Override the GetValidatedString method by adding the following code to your custom field type class:

    public override string GetValidatedString(object value)
    {
      string _SSN = value.ToString();
    
      // only validate if a value was entered
      if (_SSN.Length > 0)
      {
        // Create a regular expression for a valid SSN
        Regex _Regex = new Regex(
          @"^d{3}-d{2}-d{4}$",
          RegexOptions.IgnoreCase);
    
        // if the value entered does not match the Regex,
        // present the user with the custom exception message
        // supplied by one of the custom field properties
        if (!_Regex.IsMatch(_SSN))
        {
          throw new SPFieldValidationException(
            GetCustomProperty("ExceptionMessage").ToString());
        }
        else
        {
          return _SSN;
        }
      }
      else
      {
        return _SSN;
      }
    }

  2. Build the project.

You should now be able to add only list items whose value in the custom field matches that of your built-in Regex for a Social Security number. Perhaps a more robust solution would be to create a custom field type that has a custom property that is itself a regular expression string and have the GetValidatedString evaluate the match based on that custom property. You can try that on your own if you wish.

As you can see, custom field types can be very powerful and flexible. Although this example did not explore the rendering options at length, you should have a good enough idea of what is possible to enable you to build your own custom field types that solve your very own complex business problems. The extensibility of the platform has been provided for exactly that purpose.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.16.229