[SharePoint 2010] 客制化欄位類型

[SharePoint 2010] 客制化欄位類型
客戶提出了一個需求,在一個公告的清單中,有個負責人姓名及Email的欄位,希望是可以在新增時就預設帶建檔者的名稱跟Email.

這樣的需求或許有其他的方式可以解決,不過我所想到的方式是客制化一個欄位類型,在新增欄位時,可以設定要存取UserProfile中的哪個欄位.

開發環境

Win 2008 R2 + VS 2010 + SharePoint 2010

前言

客戶提出了一個需求,在一個公告的清單中,有個負責人姓名及Email的欄位,希望是可以在新增時就預設帶建檔者的名稱跟Email.

這樣的需求或許有其他的方式可以解決,不過我所想到的方式是客制化一個欄位類型,在新增欄位時,可以設定要存取UserProfile中的哪個欄位.

功能畫面如下

CustomFieldType009

 

開始進行開發

這個專案中大致上會有下列檔案

  1. DefValueHelper.cs
    提供一個GetProfileValue的函數,傳入propertyName後取得在UserProfile中該欄位的資料
  2. DefValueField.cs
    繼承Microsoft.SharePoint.SPFieldText,
    客製化的欄位類型基本上都是繼承自SPField,而SPField中提供了數種類型,而我們這次所要的是文字欄位所以是繼承SPFieldText
    而繼承後主要要override的是FieldRenderingControl,
    在這個FieldRenderingControl中,我們會建立我們所定義的FieldControl (DefValueFieldControl定義在下一個檔案中)
  3. DefValueFieldControl.cs
    主要用來render客製化的控制項,可以設定控制項在新增,編輯,顯示模式下,所要呈現的樣子.
    主要繼承自BasicFieldControl,而我們所要呈現的是文字方塊,所以我們是繼承TextField,
    但在這邊,我們不動態產生控制項,而是配合RenderingTemplate,使用ascx來拉出我們要顯示的控制項樣子,
    然後在這裡面判斷是在新增,編輯或是顯示模式,然後呈現我們要顯示控制項範本及資料.
  4. DefValueFieldControl.ascx
    RenderingTemplate,在需求中,新增,編輯模式下,我們要顯示一個文字方塊,
    在顯示的模式下,我們只需要顯示文字標籤,所以我們在這邊定義了兩個Template,分別只放Textbox跟Label.
  5. fldtypes_DefValueField.xml
    欄位類型的定義,包含顯示的名稱,客製化的屬性名稱設定及所使用的FieldType Class

首先先建立專案結構.

  1. 建立Empty Sharepoint Project,在這我使用DefValue_FieldType為專案名稱
    CustomFieldType000
  2. 在設定site and security level for debugging時,選擇 Deploy as a farm solution.
    CustomFieldType002
  3. 建立完成後在跟目錄下新增三支Code File檔案, 分別為DefValueField.cs, DefValueFieldControl.cs, DefValueHelper.cs
  4. 設定兩個SharePoint Mapper Folder,分別加入CONTROLTEMPLATES 與 XML
    (這是VS2010的新功能,在vs2008+MOSS2007要開發這樣的功能,是要自己寫設定檔的.)
    CustomFieldType003 
    CONTROLTEMPLATES : {SharepointRoot} / TEMPLATE / CONTROLTEMPLATES
    CustomFieldType004 
    XML : {SharepointRoot} / TEMPLATE / XML
  5. 在CONTROLTEMPLATES 的資料夾中新增 DefValueFieldControl.ascx
    新增完ascx後,會自動產生*.ascx.cx與*.ascx.designer.cs,請直接刪除這兩個檔案.
    而*.ascx中的<%@ Control Language="C#" AutoEventWireup="true"… %>需改成<%@ Control Language="C#"%>
  6. 在XML資料夾中新增fldtypes_DefValueField.xml
  7. 新增下列的reference, 因為我們需要存取UserProfile
    Microsoft.Office.Server.UserProfiles
    Microsoft.Office.Server
  8. [非必要,但跟後面的程式有關] 設定專案屬性
    將Assembly name改為 : AJKits.SharePoint.DefValueFieldType
    將Default namespace改為 : AJKits.SharePoint
    CustomFieldType008

到這,專案的結構差不多建好了.

 

開始寫程式

  1. DefValueHelper.cs
    using System; 
    using Microsoft.SharePoint;
    using System.Web;
    using Microsoft.Office.Server.UserProfiles;

    namespace AJKits.SharePoint
    {
    public class DefValueHelper
    {
    public string GetProfileValue(string propertyName)
    {
    string defValue = string.Empty;
    if (!string.IsNullOrEmpty(propertyName))
    {
    string userName = HttpContext.Current.User.Identity.Name;
    if (!string.IsNullOrEmpty(userName))
    { //SPServiceContext是Sharepoint 2010的新功能
    UserProfileManager _mng = new UserProfileManager(SPServiceContext.Current);
    if (_mng != null)
    {
    if (_mng.UserExists(userName))
    {
    UserProfile profile = _mng.GetUserProfile(userName);
    if (profile != null)
    {
    defValue = Convert.ToString(profile[propertyName].Value);
    }
    }
    }
    }
    }
    return defValue;
    }

    }
    }

  2. DefValueField.cs
    using System; 
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebControls;
    using Microsoft.SharePoint.Security;
    using System.Security.Permissions;

    using AJKits.SharePoint.WebControls;

    namespace AJKits.SharePoint
    {
    public class DefValueField : SPFieldText
    {
    public DefValueField(SPFieldCollection fields, string fieldName)
    : base(fields, fieldName)
    {
    }

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

    public override BaseFieldControl FieldRenderingControl
    {
    [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
    get
    {
    BaseFieldControl fieldControl = new DefValueFieldControl();
    fieldControl.FieldName = this.InternalName;
    return fieldControl;
    }
    }

    }
    }

  3. DefValueFieldControl.cs
    using System; 
    using System.Web.UI.WebControls;

    using Microsoft.SharePoint.WebControls;

    namespace AJKits.SharePoint.WebControls
    {
    public class DefValueFieldControl : TextField
    {
    protected Label DefValueForDisplay;

    //取得Template Name,在顯示模式下,回傳DefValueFieldControlForDisplay,其它模式下回傳DefValueFieldControl
    // DefValueFieldControlForDisplay與DefValueFieldControl為ascx中的template id
    protected override string DefaultTemplateName
    {
    get
    {
    if (this.ControlMode == SPControlMode.Display)
    {
    return "DefValueFieldControlForDisplay";
    }
    else
    {
    return "DefValueFieldControl";
    }
    }
    }

    protected override void CreateChildControls()
    {

    if (this.Field != null)
    {
    base.CreateChildControls();

    this.textBox = (TextBox)TemplateContainer.FindControl("TextField");
    this.DefValueForDisplay = (Label)TemplateContainer.FindControl("DefValueForDisplay");

    if (this.ControlMode != SPControlMode.Display)
    {
    if (!this.Page.IsPostBack)
    {
    if (this.ControlMode == SPControlMode.New)
    {
    //新增模式,取得自訂的propertyName(定義在xml中),在透過DefValueHelper取得UserProfile資料
    string propertyName = (String)Field.GetCustomProperty("DefaultValueName");
    DefValueHelper helper = new DefValueHelper();
    textBox.Text = helper.GetProfileValue(propertyName);
    }
    }
    }
    else // 顯示模式
    {
    // 顯示所儲存的資料
    this.DefValueForDisplay.Text = (String)this.ItemFieldValue;
    }

    }

    }
    public override object Value
    {
    get
    {
    EnsureChildControls();
    return base.Value;
    }
    set
    {
    EnsureChildControls();
    base.Value = (String)value;
    }
    }

    }

    }

  4. CONTROLTEMPLATES \ DefValueFieldControl.ascx
    <%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %> 
    <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
    <%@ Import Namespace="Microsoft.SharePoint" %>
    <%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Control Language="C#" %>
    <SharePoint:RenderingTemplate ID="DefValueFieldControl" runat="server">
    <Template>
    <asp:TextBox ID="TextField" runat="server" />
    </Template>
    </SharePoint:RenderingTemplate>
    <SharePoint:RenderingTemplate ID="DefValueFieldControlForDisplay" runat="server">
    <Template>
    <asp:Label ID="DefValueForDisplay" runat="server" />
    </Template>
    </SharePoint:RenderingTemplate>

  5. XML \ fldtypes_DefValueField.xml
    FieldTypeClass需要將這個專案編譯後,在透過取得Assembly Name的工具取得,
    所以上面幾個程式檔案寫好後,先Build完這個專案,然後就可以夠過GetAssemblyName工具取得FieldTypeClass的內容
    GetAssemblyName只會取得AJKits.SharePoint.DefValueFieldType, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1fda5a8d8aef5781
    最前面的AJKits.SharePoint.DefValueField (ClassName),需要自己在設定,讓SharePoint知道這個FieldType是使用哪個Class
    <?xml version="1.0" encoding="utf-8" ?> 
    <FieldTypes>
    <FieldType>
    <Field Name="TypeName">DefValue</Field>
    <Field Name="ParentType">Text</Field>
    <Field Name="TypeDisplayName">UserProfileData</Field>
    <Field Name="TypeShortDescription">Text field with default value from user profile field</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">AJKits.SharePoint.DefValueField, AJKits.SharePoint.DefValueFieldType, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1fda5a8d8aef5781</Field>
    <PropertySchema>
    <Fields>
    <Field Name="DefaultValueName" DisplayName="Property Name" MaxLength="50" DisplaySize="25" Type="Text">
    <Default>PreferredName</Default>
    </Field>
    </Fields>
    </PropertySchema>
    </FieldType>
    </FieldTypes>

到這,專案跟程式就寫好了.

 

佈署測試

再來點選專案按右鍵選擇[Deploy],就會開始編譯並佈署到建立專案時所設定的網站了,

然後開啟該網站,建一個新的清單,新增欄位,就可以看到這個新的欄位類型了.
只要設定對的propertyName,就可以取得UserProfile中的欄位資料了.
預設是PreferredName,其它的欄位名稱可以參考後面這篇

CustomFieldType009

 

參考資料

How to: Create a Custom Field Type

Walkthrough: Creating a Custom Field Type

How to: Create a Tool to Get the Full Name of an Assembly

原始碼 : DefValue_FieldType.zip

 

[update:2011/03/16] 更新DefValueFieldControl.cs及原始碼