Wednesday, May 02, 2012

Control de Campo Personalizado en SharePoint 2010

Hola de nuevo. Cuantas veces hemos pensado en que una de las dolencias de SharePoint siempre ha sido no activar el control picker, que permite desplegar una ventana que fácilmente permita seleccionar elementos dados de alta en el sitio, que se encuentran por ejemplo en diferentes bibliotecas de documentos, como pueden ser: imágenes, videos, archivos en general, y que se obtenga la URL a dichos elementos, y esta quede guardada en una columna de una lista o una biblioteca?

La respuesta a este interrogante se conoce como los controles personalizados en SharePoint. Que es un mecanismo que permite que se puedan agregar tipos de campo especializados, que pueden ser reutilizados como cualquier tipo de columna, en listas o bibliotecas.

Esta entrada está dedicada a esa opción que SharePoint ofrece a través de programación .NET. Pongamos manos a la obra, y veamos cómo se puede implementar algo como  una columna de tipo picker que nos ayude a mejorar la experiencia de usuario. La siguiente imagen muestra dicho campo en funcionamiento:
Custom Control 1

En la imagen anterior se apreciaran claramente 2 campos que utilizan el control que desarrollaremos en esta entrada: Thumbnail y Url Video. La idea es poder seleccionar una imagen o un video y que en el campo respectivo quede asociada la URL a dicho recurso.
En general las listas en SharePoint hacen uso de conceptos generales que son requeridos para su funcionamiento, y que a su vez componen lo que se conoce como controles de campo personalizados. Entre estos conceptos se encuentran los que se conocen como campos (Fields), columnas de sitio y tipos de campos. Cada uno de esos campos o columnas de sitio, tienen o se constituyen por los tipos de campos: Texto, Enteros, Decimal, Fecha, LookUp, entre otros. Un tipo de campo personalizado, es lo que permite extender la plataforma SharePoint más allá de lo que ofrece, permitiendo así, generar soluciones ajustadas a los requerimientos de negocio. Un ejemplo podría ser un tipo de campo personalizado que dispare validaciones especiales acorde a la entrada de datos del usuario, que difícilmente podrá ser resuelto con facilidad con las funcionalidades por defecto de los tipos ya comentados.
Una de las cosas faltantes en las herramientas de desarrollo para SharePoint ofrecidas en VS. NET 2010 es una plantilla para crear tipos de campo personalizados, así que básicamente los pasos para iniciar una solución de este tipo es la siguiente:
  1. Crea un proyecto de SharePoint nuevo basado en la plantilla de Proyecto de SharePoint vacío.
  2. Cuando se haya creado tal proyecto adicionar una clase por cada tipo de campo personalizado que se piense crear.
A continuación se presenta la definición de la clase AssetUrlSelectorField que hereda de un tipo de campo base SPFieldText, que hace parte del desarrollo del control que se ha mostrado anteriormente:
class AssetUrlSelectorField: SPFieldText
    {
        // Methods
        public AssetUrlSelectorField(SPFieldCollection fields,string fieldName) : base(fields, fieldName)
        {}
        public AssetUrlSelectorField(SPFieldCollection fields,
        string typeName, string displayName) : base(fields, typeName, displayName)
        {}
        // Properties
        public override BaseFieldControl FieldRenderingControl
        {
            get
            {
                BaseFieldControl control = new AssetUrlSelectorFieldControl();
                control.FieldName = base.InternalName;
                return control;
            }
        }
    }

La clase AssetUrlSelectorField define el tipo de campo personalizado que vamos a utilizar en el desarrollo del control. Es muy importante y obligatorio implementar los dos constructores públicos, que son requeridos por SharePoint Foundation. Finalmente para este caso específico se ha sobrescrito una propiedad que esencialmente se encarga de retornar el control que definiremos en otra clase.
Ahora bien, hasta el momento hemos podido ver la definición base de nuestro campo personalizado. Pero adicionalmente podemos mejorar la experiencia de usuario con nuestro control, implementando lo que se conoce como un control de campo personalizado, que provee al usuario con una experiencia de edición personalizada. La definición e implementación de esta nueva clase se muestra a continuación:

class AssetUrlSelectorFieldControl : BaseFieldControl
    {
        protected AssetUrlSelector urlSelector;
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            // Set the value if this is a postback.
            if (this.Page.IsPostBack)
            {
                try
                {
                    this.ListItemFieldValue = urlSelector.AssetUrl;
                }
                catch (Exception ex)
                {
                    EventLog log = new EventLog();
                    if (!(log.Source == "Mi Log"))
                        log.Source = "Mi Log";
                    log.WriteEntry("Error en el Método OnLoad: " + ex.Message + "- InnerException: " + ex.InnerException + "- Source: " + ex.Source + "- StackTrace: " + ex.StackTrace,
                      EventLogEntryType.Error, 1);
                    throw;
                }
            }
        }
        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            // Add the asset picker when in edit mode.
            try
            {
                urlSelector = new AssetUrlSelector();
                this.Controls.Add(urlSelector);
            }
            catch (Exception ex)
            {
                EventLog log = new EventLog();
                if (!(log.Source == "Mi Log"))
                    log.Source = "Mi Log";
                log.WriteEntry("Error en el Método CreateChildControls: " + ex.Message + "- InnerException: " + ex.InnerException + "- Source: " + ex.Source + "- StackTrace: " + ex.StackTrace,
                  EventLogEntryType.Error, 1);
                throw;
            }
        }
        public override object Value
        {
            get
            {
                this.EnsureChildControls();
                return urlSelector.AssetUrl;
            }
            set
            {
                this.EnsureChildControls();
                urlSelector.AssetUrl = (string)this.ItemFieldValue;
            }
        }
    }

La clase anterior es la que se ha invocado inicialmente en la clase AssetUrlSelectorField, en la propiedad sobrescrita FieldRenderingControl. Esta clase aprovecha la clase AssetUrlSelector  la cual es la que provee la funcionalidad de tipo picker usada en SharePoint por defecto. Para mayor información de dicha clase se puede consultar su documentación en MSDN http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.publishing.webcontrols.asseturlselector.aspx

Para evitar confusiones, los namespaces utilizados en las clases mostradas son:

using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing.WebControls;
using Microsoft.SharePoint.WebControls;
using System.Web.UI;
using System.Diagnostics;

Con las clases definidas, muy fáciles de entender por cualquier desarrollador a continuación se requiere de la creación y definición de un archivo XML que al momento de hacer el despliegue de la solución en el servidor de SharePoint permite identificar el control que podrá ser utilizado dentro del sitio que se requiera o esté disponible la característica. SharePoint 2010 mantiene estos archivos XML en la ruta TEMPLATE/XML, en la cual muchos de sus tipos de campo están definidos. El nombrado de dicho archivo debe comenzar con el nombre fldtypes_, y de ahí se completa acorde a cada necesidad, en este caso por ejemplo: fldtypes_AssetUrlSelectorCustomField.xml. El contenido de este archivo XML que debemos agregar manualmente al folder mapeado XML, se presenta a continuación:

<?xml version="1.0" encoding="utf-8"?>
<FieldTypes>
  <FieldType>
    <Field Name="TypeName">AssetUrlSelectorField</Field>
    <Field Name="ParentType">Text</Field>
    <Field Name="TypeDisplayName">Picker</Field>
    <Field Name="TypeShortDescription">Picker de Activos</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">
      WebParts.AssetUrlSelectorCustomField.AssetUrlSelectorField,$SharePoint.Project.AssemblyFullName$
    </Field>
  </FieldType>
</FieldTypes>

La definición <Field Name="FieldTypeClass">
WebParts.AssetUrlSelectorCustomField.AssetUrlSelectorField,$SharePoint.Project.AssemblyFullName$
</Field> puede variar acorde a su implementación, por ejemplo el nombre del namespace, en este caso WebParts, que usted haya usado en su proyecto en Visual Studio .NET.

En general la solución debería verse así:
Custom Control 2
Debido a que se ha utilizado una carpeta mapeada (XML) a la ruta de SharePoint 2010 que se ha explicado mantiene los archivos XML, automáticamente VS.NET 2010 sabe donde desplegar el XML definido para esta solución.
Luego de hacer el despliegue con VS.NET 2010, se puede proceder navegar hasta una lista, crear una nueva columna y ahí deberá estar disponible el tipo de control personalizado, por ejemplo:
Custom Control 3

El título Picker de Activos es el que se definió en el valor de la propiedad TypeShortDescription.

En conclusión, aunque no se cuenta con una experiencia de desarrollo 100% definida en VS.NET 2010, los pasos para desarrollar un control de campo personalizado y un campo personalizado base, no son complejos, y realmente son la puerta a implementaciones mucho más avanzadas que permitan con mayor facilidad cumplir requerimientos de nuestros clientes, que suelen salirse de los límites de la funcionalidad por defecto de la Plataforma.

No comments: