📜 ⬆️ ⬇️

Creating a new field type for MS SharePoint using the example of a simple check field

When working with SharePoint, it is often necessary to make your own field for any specific tasks. One of these tasks is the ability to check text fields, for example, the correctness of filling in Email or any data about the Organization: TIN, PPC, etc.



The simplest and most convenient in this case is the use of regular expressions, so we’ll dwell on them.
Let's start with the internal view of the field in Sharepoint. This is a class inherited from the SPField class. The fields themselves are stored in the Sharepoint database as encrypted XML. Based on XML (field schema), an object inherited from SPField is created. The scheme of a plain text field looks like this:

<Field ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="Title" Group="_Hidden" Type="Text" DisplayName="" Required="TRUE" FromBaseType="TRUE" /> 

First, create a class for our new field:
')
 class RegExpField : SPFieldText { public RegExpField(SPFieldCollection fields, string fieldName) : base(fields, fieldName) { } public RegExpField(SPFieldCollection fields, string typeName, string displayName) : base(fields, typeName, displayName) { } } 

The field must have 2 custom properties - this is the property that stores the regular expression for its validation and the property that stores the text of the error displayed in cases of non-compliance of the string with the regular expression.
With custom properties, SharePoint fields have certain difficulties, the most optimal is to use forgive me. Gods of reflection. We need to add 2 methods to our field:

 //          private void SetFieldAttribute(string attribute, string value) { Type baseType; BindingFlags flags; MethodInfo mi; baseType = typeof(RegExpField); flags = BindingFlags.Instance | BindingFlags.NonPublic; mi = baseType.GetMethod("SetFieldAttributeValue", flags); mi.Invoke(this, new object[] { attribute, value }); } //          private string GetFieldAttribute(string attribute) { Type baseType; BindingFlags flags; MethodInfo mi; baseType = typeof(RegExpField); flags = BindingFlags.Instance | BindingFlags.NonPublic; mi = baseType.GetMethod("GetFieldAttributeValue", flags, null, new Type[] { typeof(String) }, null); object obj = mi.Invoke(this, new object[] { attribute }); if (obj == null) return ""; else return obj.ToString(); } 

Add 2 properties to the field:

 public string ValidRegExp { get { return GetFieldAttribute("ValidRegExp"); } set { SetFieldAttribute("ValidRegExp", value); } } public string ErrorMessage { get { return GetFieldAttribute("ErrorMessage"); } set { SetFieldAttribute("ErrorMessage", value); } } 

Now you need to add the ability to edit the field settings to the user. To do this, create a UserControl RegExpFieldEdit.ascx and inherit it from the IFieldEditor interface. The interface has one property and two methods that need to be overridden. It will look like this:

 public bool DisplayAsNewSection { get { return true; } } //     public void InitializeWithField(SPField field) { if (!IsPostBack) { if (field is RegExpField) { var Validfield = field as RegExpField; RegExp.Text = Validfield.ValidRegExp; ErrorMessage.Text = Validfield.ErrorMessage; } } } //     public void OnSaveChange(SPField field, bool isNewField) { if (field is RegExpField) { var Validfield = field as RegExpField; Validfield.ValidRegExp = RegExp.Text; Validfield.ErrorMessage = ErrorMessage.Text; } } 

The next step is to tell SharePoint that all we have created is a field. To do this, create an XML in which we will write a new field type. To get started, let's “patch” the XML folder from SharePoint into our project and create a new XML file with the name Fldtypes_RegExpField.xml, and the contents:

 <FieldTypes> <FieldType> <Field Name="TypeName">RegExpField</Field> <Field Name="ParentType">Text</Field> <Field Name="TypeDisplayName">  </Field> <Field Name="TypeShortDescription">  </Field> <Field Name="FieldTypeClass">RegExpField.RegExpField, $SharePoint.Project.AssemblyFullName$</Field> <Field Name="FieldEditorUserControl">/_controltemplates/15/RegExpFieldEdit.ascx</Field> </FieldType> </FieldTypes> 

Now you need to understand the methods of rendering the field. SharePoint has two methods for drawing fields:

For our field we will use client rendering. To begin with, we will create some JS file RegExpField.js in which the field will be drawn on the client. To bind a file to our field, the JSLink mechanism is used. Each field has a JSLink property. It specifies the JS files that must be uploaded to the client for the field drawing to work. Overload the property in our class field.

 public override string JSLink { get { return "/_layouts/15/RegExpField/RegExpField.js"; } } 

Still need to transfer our new parameters to the client. For this, there is a special method in SPField. We will overload and add our parameters to it. Here's what it looks like:

 public override Dictionary<string, object> GetJsonClientFormFieldSchema(SPControlMode mode) { var formtctx = base.GetJsonClientFormFieldSchema(mode); formtctx["ValidRegExp"] = ValidRegExp; formtctx["ErrorMessage"] = ErrorMessage; return formtctx; } 

Here we take the already formed context and add our properties to it. The data from this method is serialized by SharePoint in JSON using the JavaScriptSerializer class.
Now you need to register the template for the field field on the client. For this we will write the following code in the JS file:

 RegExpFieldTemplate = function RegExpFieldTemplate () { } RegExpFieldTemplate.$$cctor = function RegExpFieldTemplate $$$cctor() { if (typeof (SPClientTemplates) != "undefined") SPClientTemplates.TemplateManager.RegisterTemplateOverrides(RegExpFieldTemplate.createRenderContextOverride()); //      } RegExpFieldTemplate.createRenderContextOverride = function () { var RegExpFieldTemplateContext = {}; RegExpFieldTemplateContext.Templates = {}; RegExpFieldTemplateContext.Templates['Fields'] = { RegExpField: { View: RegExpFieldTemplate.renderViewControl, DisplayForm: RegExpFieldTemplate.renderDisplayControl, NewForm: RegExpFieldTemplate.renderEditControl, EditForm: RegExpFieldTemplate.renderEditControl, }//          ,           }; return RegExpFieldTemplateContext; } function RegExpField_init() { RegExpFieldTemplate.$$cctor(); }; RegExpField_init(); 

Now let's analyze the rendering of each display option separately.
Let's start c drawing on the display (View)

 RegExpFieldTemplate.renderViewControl = function (renderCtx, field, item, list) { if (renderCtx.inGridMode === true) { field.AllowGridEditing = false; //        GridView } return STSHtmlEncode(item[field.Name]);//    item.   Encode Html       } 

The html markup as a string is returned as the return value of the function.
On the form of viewing will use the following code:

 RegExpFieldTemplate.renderDisplayControl = function (renderCtx) { return STSHtmlEncode(renderCtx.CurrentFieldValue);//         Encode Html } 

The rendering of the field on the editing form and the element creation form remained. Here's what happened:
 //    RegExpFieldTemplate.ValidatorValue = function (stringRegExp, errorMessage) { //     1     RegExpFieldTemplate.ValidatorValue.prototype.Validate = function (value) { //         if (value && stringRegExp) { var reg = new RegExp(stringRegExp); //      if (!reg.test(value)) { //     , //       ,    return new SPClientFormsInclude.ClientValidation.ValidationResult(true, errorMessage);// } } return new SPClientForms.ClientValidation.ValidationResult(false); }; } RegExpFieldTemplate.renderEditControl = function (rCtx) { if (rCtx == null) return ''; var frmData = SPClientTemplates.Utility.GetFormContextForCurrentField(rCtx);//   ,    //           if (frmData == null || frmData.fieldSchema == null) return ''; var _inputElt; var _value = frmData.fieldValue != null ? frmData.fieldValue : '';//          var _inputId = frmData.fieldName + '_' + '_$RegExp' + rCtx.FormUniqueId;// Id input'  var validators = new Eos.Fields.ClientControls.ClientValidation.ValidatorSet();//  ,           if (frmData.fieldSchema.Required) {//         //              validators.RegisterValidator(new Eos.Fields.ClientControls.ClientValidation.RequiredValidator()); } //         validators.RegisterValidator(new RegExpFieldTemplate.ValidatorValue(rCtx.CurrentFieldSchema.ValidRegExp,rCtx.CurrentFieldSchema.ErrorMessage)); //   frmData.registerClientValidator(frmData.fieldName, validators); //     HTML   DOM frmData.registerInitCallback(frmData.fieldName, InitControl); //        ,  //       frmData.registerFocusCallback(frmData.fieldName, function () { if (_inputElt != null) { _inputElt.focus(); if (browseris.ie8standard) { var range = _inputElt.createTextRange(); range.collapse(true); range.moveStart('character', 0); range.moveEnd('character', 0); range.select(); } } }); //       frmData.registerValidationErrorCallback(frmData.fieldName, function (errorResult) { //      ,    span'a   SPFormControl_AppendValidationErrorMessage(_inputId, errorResult); }); //        frmData.registerGetValueCallback(frmData.fieldName, function () { return _inputElt == null ? '' : _inputElt.value; }); //      hidden (         ,   ) frmData.updateControlValue(frmData.fieldName, _value); //      var result = '<span dir="' + STSHtmlEncode(frmData.fieldSchema.Direction) + '">'; result += '<input type="text" value="' + STSHtmlEncode(_value) + '" maxlength="' + STSHtmlEncode(frmData.fieldSchema.MaxLength) + '" '; result += 'id="' + STSHtmlEncode(_inputId) + '" title="' + STSHtmlEncode(frmData.fieldSchema.Title); result += '" class="ms-long ms-spellcheck-true ' + (rCtx.CurrentFieldSchema.DoubleWidth ? 'InputDoubleWidth' : '') + ' " />'; result += '<br /></span>';// return result; //        DOM function InitControl() { //  Input _inputElt = document.getElementById(_inputId); if (_inputElt != null) //   AddEvtHandler(_inputElt, "onchange", OnValueChanged); } //    input function OnValueChanged() { if (_inputElt != null) //      hidden (         ,   ) frmData.updateControlValue(frmData.fieldName, _inputElt.value); } } 

Here I tried to describe everything as much as possible in the comments to the code, so I think there is nothing more to describe here.
In general, our field is ready. It remains to deploy the solution on SharePoint and configure our new field. The field in Sharepoint will look like this:



Creating a new type of field of this level takes a couple of hours. And it can greatly facilitate the work with user forms. It can also be easily extended by adding an input mask to it, for example, using the jQuery Masked Input library.

All sources are available on GitHub .

Source: https://habr.com/ru/post/262187/


All Articles