WSS 3.0 Custom Fields
Data: 7/7/2007
Categoria: Sharepoint
Codice d'esempio


Capita molto spesso, nello sviluppo di applicazioni personalizzate con Windows Sharepoint Services, di dover salvare alcuni tipi di dati che non rispecchiano la conformazione dei tipi di campi disponibili nell'installazione di default del prodotto. Per fare un semplice esempio, se avessimo la necessità di inserire un campo per salvare dei numeri di carta di credito, o dei numeri di telefono, o comunque delle informazioni che necessitano un formato ben preciso, con l'utilizzo di campi di testo normali, non saremmo in grado di validare l'input dell'utente e come conseguenza, ci ritroveremmo con dati non consistenti.
Fino alla precedente versione questo era un grosso deterrente, in quanto dovevamo limitarci all'utilizzo di campi di testo, multi linea o scelta (o comunque di uno dei campi base di Sharepoint) per salvare dei dati che, magari, necessitavano di strutture di memorizzazione ben diverse.
Ora, nella versione 3.0, è possibile creare i nostri tipi di campi personalizzati potendo attuare sia sulla validazione dell'input dell'utente, che sul rendering del campo stesso, il tutto senza perdere le funzionalità di base dei campi gia presenti.
Esistono due tipi di campi da implementare (si tratta semplicemente di due definizioni, poiché possono essere inserite entrambe le implementazioni all'interno di un unico campo):
  • Field type – sono tipi di campi personalizzati, dove si attua la validazione delle informazioni.
  • Field value – sono valori di un campo, per quei campi che necessitano di una struttura dati differente da quella di default per le proprie informazioni. Tramite questa implementazione, per esempio, possiamo creare un unico campo che contiene un indirizzo, un numero civico e la città per un singolo contatto.

Il nostro campo personalizzato

Per la creazione di un campo custom, dobbiamo creare due distinti oggetti:
  • Una classe .NET che rappresenti il campo e che contenga le logiche di validazione e di rendering. Tale classe deve ereditare da uno dei campi di base di Sharepoint.
  • Un file XML di definizione del campo custom, dove dobbiamo specificare il titolo, il tipo base del nostro campo, l'assembly che lo contiene e le proprietà legate al campo che verranno poi settate dall'utente una volta che il campo viene creato.
Come esempio, ho deciso di creare un campo personalizzato (di tipo Field type) per l'inserimento di un GUID. Dapprima tale campo avrà solo la funzione di validazione delle informazioni inserite al suo interno, poi vedremo come estendere il rendering dell'interfaccia di inserimento per fornire funzionalità in più per i nostri utenti.

Per prima cosa, creiamo un progetto in Visual Studio .NET di tipo Class Library e aggiungiamo la reference per il namespace Microsoft.SharePoint (ricordiamoci inoltre di segnare l'assembly come sicuro, in modo tale da poterlo inserire nella GAC del server). Poi creiamo una classe, che erediti da SPFieldText, per far si che il nostro campo si avvalga di tutte le funzionalità offerte dal campo base di testo.
Infine, per controllare che i valori inseriti nel nostro campo siano effettivamente dei GUID validi, dobbiamo effettuare l'overload del metodo GetValidatedString, metodo che viene chiamato dall'interfaccia di inserimento di Sharepoint ogni volta che viene inserito un nuovo valore all'interno del nostro campo personalizzato.

using System;

using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace Peppe.Sharepoint.Fields
{
public class GuidFieldValidator : SPFieldText
{
public GuidFieldValidator(SPFieldCollection fields, string fieldName)
: base(fields, fieldName)
{ }

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

public override string GetValidatedString(object value)
{
string textValue = value.ToString();
try
{
Guid g = new Guid(value.ToString());
return textValue;
}
catch
{
throw new SPFieldValidationException(GetCustomProperty("ErrorMessage").ToString());
}
}
}
}

Come potete vedere, all'interno del metodo GetValidatedString, viene controllato che il valore inserito sia un GUID valido; se non lo è, viene lanciata un eccezione di tipo SPFieldValidationException che stampa a video un messaggio di errore e ferma l'inserimento del nuovo valore.
Il testo del messaggio di errore, viene prelevato direttamente dalle proprietà custom del nostro campo. Tali proprietà, sono presenti all’interno del file XML di definizione del campo (come sotto-elementi dell’elemento Fields) e possono essere modificate dall’utente nel momento dell’inserimento del campo all'interno di una particolare lista.
Il file di definizione, per il nostro campo custom è il seguente:

fldtypes_GuidFieldValidator.xml
<?xml version="1.0" encoding="utf-8" ?>

<FieldTypes>
<FieldType>
<Field Name="TypeName">GuidFieldValidator</Field>
<Field Name="ParentType">Text</Field>
<Field Name="TypeDisplayName">GUID value</Field>
<Field Name="FieldTypeClass">
Peppe.Sharepoint.Fields.GuidFieldValidator,
Peppe.Sharepoint.Fields, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d59d3937bcd07e20
</Field>
<PropertySchema>
<Fields>
<Field Name="ErrorMessage" DisplayName="Messaggio di errore"
MaxLength="255" DisplaySize="35" Type="Text"
>
<Default>Non hai inserito un GUID valido !</Default>
</Field>
</Fields>
</PropertySchema>
</FieldType>
</FieldTypes>

Il file di definizione va quindi salvato sotto la directory:
Local_Drive:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML
seguendo questo formato per il nome del file: fldtypes_[NomeClasse].xml, che nel nostro caso diventa: fldtypes_GuidValueValidator.xml

Infine, per completare l'installazione del nostro campo custom, dobbiamo inserire la dll contenente la classe .NET vista precedentemente ed effettuare un IISRESET.

Fatte queste operazioni, possiamo creare una nuova lista ed aggiungere una nuova colonna di tipo GuidFieldValidator; nella schermata di aggiunta di una nuova colonna (figura 1) l'utente può modificare il valore del messaggio di errore (la proprietà ErrorMessage vista precedentemente nel file di definizione).

Creazione di una nuova colonna di tipo GuidFieldValidator
Figura 1 - Creazione di una nuova colonna di tipo GuidFieldValidator

Ora, se proviamo ad aggiungere un nuovo elemento all'interno della nostra lista e proviamo ad inserire un valore che non sia un GUID all'interno del nostro campo, vedremo stampato a video il messaggio d'errore scelto.

Il custom field validator in azione
Figura 2 - Il custom field validator in azione

Aggiunta di funzionalità avanzate al nostro campo custom
Ora, proviamo ad aggiungere delle nuove funzionalità al nostro custom field. In particolare ho intenzione di aggiungere una label che ricordi all'utente il formato proprio di ogni GUID e un tasto che, tramite javascript (quindi senza che la pagina effettui un postback) aggiunga un nuovo GUID in automatico all'interno del campo di testo personalizzato.

Per effettuare queste modifiche al rendering del campo, dobbiamo utilizzare due ulteriori oggetti:
  • uno UserControl .ascx, che definisca i controlli da visualizzare nell'interfaccia di inserimento di nuovi valori all'interno del nostro campo custom e, nel nostro caso, la funzione javascript per la creazione di un nuovo GUID;
  • una classe, figlia di BaseFieldControl, che implementi tutte le operazioni di rendering dei controlli e che sovrascriva il modo in cui viene prelevato il valore da salvare all'interno dell’item aggiunto o modificato.

GuidField.ascx
<%@ Control Language="C#" Debug="true"  %>

<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%
>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls"
Assembly="Microsoft.SharePoint, Version=12.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%
>

<SharePoint:RenderingTemplate ID="GuidFieldControl" runat="server">
<Template>
<script language="javascript" type="text/javascript">
function generateGuid(id)
{
var result, i, j;
result = "";
for(j=0; j<32; j++)
{
if(j==8 || j==12 || j==16 || j==20)
result = result + '-';
i = Math.floor(Math.random() * 16).toString(16).toUpperCase();
result = result + i;
}
document.getElementById(id).value = result;
}
</script>
<asp:Label ID="lblGuid" runat="server" Text="Label"></asp:Label><br />
<asp:TextBox ID="txtGuid" runat="server"></asp:TextBox>
<a id="linkGuid" runat="server">Generate GUID</a>
</Template>
</SharePoint:RenderingTemplate>

Nota: ogni UserControl da utilizzare all’interno di Sharepoint, va salvato all'interno della directory:
Local_Drive:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\CONTROLTEMPLATES
E, ogni volta che viene modificato, deve essere effettuato un IISRESET.


Lo UserControl è davvero semplice. La cosa più importante da notare è l'utilizzo del controllo RenderingTemplate, attraverso il quale è possibile modificare il rendering del nostro campo custom, inserendo nuovi controlli web ASP.NET e il codice HTML (o javascript) per la personalizzazione del layout.
Nel nostro caso, abbiamo inserito una label che ricorda all’utente il formato generale di ogni GUID (la label lblGuid), una textbox per l’inserimento del nuovo valore e un link per la generazione automatica del nuovo GUID tramite javascript.
Tutte le altre proprietà relative a tali controlli, sono settate all'interno della classe che eredita da BaseFieldControl e che si occupa di modificare il modo in cui il valore viene salvato all’interno dell'item aggiunto o modificato.

public class GuidFieldControl : BaseFieldControl

{
private TextBox txtGuid;
private Label lblGuid;
private HtmlAnchor linkGuid;

public GuidFieldControl()
{ }

protected override void CreateChildControls()
{
if (Field == null) return;
base.CreateChildControls();

if (ControlMode == SPControlMode.Display)
return;

txtGuid = (TextBox)TemplateContainer.FindControl("txtGuid");
if (txtGuid == null)
throw new ArgumentException("TextBox non trovata");
txtGuid.TabIndex = TabIndex;
txtGuid.MaxLength = 36;
txtGuid.CssClass = CssClass;
txtGuid.Width = 280;

lblGuid = (Label)TemplateContainer.FindControl("lblGuid");
if (lblGuid == null)
throw new ArgumentException("Label non trovata");
lblGuid.Text = "Guid sample: 00000000-0000-00000-0000-000000000000";
lblGuid.TabIndex = TabIndex;
lblGuid.CssClass = CssClass;

linkGuid = (HtmlAnchor)TemplateContainer.FindControl("linkGuid");
if(linkGuid == null)
throw new ArgumentException("Link non trovato");
linkGuid.HRef = "javascript:generateGuid('" + txtGuid.UniqueID + "')";
}

protected override string DefaultTemplateName
{
get { return "GuidFieldControl"; }
}

public override void Focus()
{
EnsureChildControls();
txtGuid.Focus();
}

public override object Value
{
get
{
EnsureChildControls();
return txtGuid.Text.Trim();
}
set
{
EnsureChildControls();
txtGuid.Text = (string)value;
}
}
}

Tale classe deve implementare l'overload di questi tre membri:
  • metodo CreateChildControls – in modo tale da modificare le varie proprietà dei controlli propri dello UserControl visto in precedenza
  • metodo Focus – per far si che quando il campo prenda il focus, questo venga attivato su uno dei controlli che abbiamo inserito (nel nostro caso nella textbox di inserimento)
  • proprietà DefaultTemplateName – in modo tale da referenziare ed eseguire il giusto UserControl (poiché il valore di tale proprietà, deve essere lo stesso del valore dell'attributo ID, del controllo RenderingTemplate)
  • proprietà Value – per far si che, il valore salvato all’interno dell'item, sia preso dalla nostra textbox.
L'unica modifica che dobbiamo fare alla classe relativa al nostro campo custom (la classe GuidValueValidator), sarà quindi quella di sovrascrivere la proprietà FieldRenderingControl per far si che questa ritorni un’istanza della nostra classe di rendering (la classe GuidFieldControl).

public class GuidFieldValidator : SPFieldText

{
public GuidFieldValidator(SPFieldCollection fields, string fieldName)
: base(fields, fieldName)
{ }

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

public override string GetValidatedString(object value)
{
string textValue = value.ToString();
try
{
Guid g = new Guid(value.ToString());
return textValue;
}
catch
{
throw new SPFieldValidationException(GetCustomProperty("ErrorMessage").ToString());
}
}

public override BaseFieldControl FieldRenderingControl
{
get
{
BaseFieldControl guidFieldControl = new GuidFieldControl();
guidFieldControl.FieldName = InternalName;
return guidFieldControl;

}
}
}

Ora, il nostro campo custom, ha delle funzionalità aggiuntive rispetto ai campi di base forniti da Sharepoint. Facendo click, infatti, sul link "Generate GUID", verrà inserito nella textbox un nuovo GUID, che sarà ritenuto valido dal sistema di validazione del nostro campo.

Funzionalità avanzate per il nostro campo custom
Figura 3 - Funzionalità avanzate per il nostro campo custom

Conclusioni
In questo articolo abbiamo visto la maggior parte delle tecniche con cui creare dei campi personalizzati per le liste dei nostri siti Sharepoint. Abbiamo visto che è possibile sia validare le informazioni inserite che modificare il rendering del campo per aggiungere funzionalità avanzate.
Anche in questo caso, i possibili sviluppi di campi custom sono tantissimi. Quindi… buon lavoro !