Validación en acción con el control DataForm de Silverlight (1)

El control DataForm brinda al programador de Silverlight la posibilidad de desarrollar formularios de datos a través de los cuales los usuarios pueden realizar la edición de colecciones de objetos.

La flexibilidad de la maquinaria interna del DataForm permite al desarrollador crear, desde formularios sencillos a partir de una mínima cantidad de código, hasta formularios más complejos con características de edición avanzadas, que requieran un mayor trabajo de codificación.

En este artículo vamos a focalizar nuestro punto de atención en un interesante aspecto del DataForm como es la validación de los datos que maneja el control, tanto a la hora de la edición en los campos del formulario, como al confirmar o descartar las modificaciones realizadas sobre la colección de datos.

Sin embargo, antes de entrar directamente a explicar las tareas de validación, comentaremos los aspectos correspondientes a la preparación y carga de los datos que serán consumidos por el DataForm, con el fin de que el lector pueda tener una mejor visión de los pasos necesarios previos a la edición en el formulario de datos.

Dada la extensión de las cuestiones a tratar, dividiremos el artículo en dos partes: en la primera abordaremos la configuración y carga de los datos; mientras que en la segunda explicaremos los detalles relacionados con la validación.

 

La fuente de datos

Para mantener el ejemplo a desarrollar lo más sencillo posible, vamos a crear una base de datos compuesta por una única tabla, sobre la que realizaremos las diferentes pruebas que iremos describiendo a lo largo de este artículo. A continuación se ofrecen las sentencias SQL para crear la tabla y llenarla con algunos valores de muestra.

CREATE DATABASE PruebasSL
GO

USE PruebasSL
GO

CREATE TABLE Customer
(
    CustomerID int IDENTITY(1,1) NOT NULL,
    FirstName varchar(50) NULL,
    LastName varchar(50) NULL,
    LastOrder datetime NULL,
    City varchar(50) NULL,
    Country varchar(50) NULL,
    PostalCode varchar(5) NULL,
    Credit money NULL,
    CONSTRAINT PK_Customer PRIMARY KEY CLUSTERED (CustomerID ASC)
)
GO

INSERT INTO Customer VALUES ('Alberto','Puente','20070512','Sevilla','España','41013',127.89)
INSERT INTO Customer VALUES ('Jane','Pym','20091225','Londres','Reino Unido','77554',95.40)
INSERT INTO Customer VALUES ('Steve','Rogers','20100208','Dublín','Irlanda','43888',105.70)
INSERT INTO Customer VALUES ('Elena','Manzano','20080925','Madrid','España','28020',200.01)
INSERT INTO Customer VALUES ('María','Iglesias','20080701','Barcelona','España','08027',99.95)
INSERT INTO Customer VALUES ('Pierre','Dupont','20091022','París','Francia','11224',175.75)
INSERT INTO Customer VALUES ('Rose','Douglas','20090327','Liverpool','Reino Unido','47040',190.88)
INSERT INTO Customer VALUES ('Michael','Parker','20070616','Manchester','Reino Unido','45050',222.44)
INSERT INTO Customer VALUES ('Meryl','Watson','20081118','Tucson','USA','90511',100.90)
INSERT INTO Customer VALUES ('Sharon','Bacall','20081217','Chicago','USA','92722',115.99)
INSERT INTO Customer VALUES ('Sophie','Mimieux','20070515','Toulouse','Francia','12774',150.32)
INSERT INTO Customer VALUES ('John','Stewart','20091118','San Francisco','USA','92755',100.90)
INSERT INTO Customer VALUES ('Helen','Hepburn','20091217','Portland','USA','92311',115.99)
INSERT INTO Customer VALUES ('Jaqueline','Montand','20071207','Poitiers','Francia','15995',150.32)
INSERT INTO Customer VALUES ('Spencer','Grant','20101118','Denver','USA','96877',200.90)

 

El proyecto a desarrollar

Como siguiente paso, crearemos en Visual Studio 2010 un nuevo proyecto de tipo Silverlight Application al que daremos el nombre ValidacionAccion (el proyecto conteniendo el código fuente del ejemplo está disponible aquí); marcando en su cuadro de diálogo New Silverlight Application la casilla Enable WCF RIA Services, ya que esta será la tecnología de acceso a datos que vamos a emplear. Para poder utilizar WCF RIA Services en nuestros desarrollos deberemos tener instalado Silverlight 4 Tools for Visual Studio 2010. De igual manera, para usar el control DataForm tendremos que instalar el Silverlight 4 Toolkit.

 

Definiendo el entorno de datos

Situándonos en el proyecto Web (servidor) de la solución, añadiremos un nuevo elemento de tipo ADO.NET Data Model (localizado en la categoría Data del diálogo Add New Item) con el nombre PruebasSLModel.

Esta acción iniciará el asistente de creación del modelo de datos, donde definiremos una nueva conexión para la base de datos PruebasSL.

 

Finalizaremos este asistente seleccionando la tabla Customer de la base de datos.

 

Tras generar la solución obtendremos una clase denominada Customer, derivada de EntityObject, que servirá como base para la creación de una colección que contendrá los registros de la tabla Customer, los cuales editaremos a través del DataForm. El siguiente bloque de código muestra un fragmento de dicha clase.

[EdmEntityTypeAttribute(NamespaceName="PruebasSLModel", Name="Customer")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class Customer : EntityObject
{
    //....    
    [EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
    [DataMemberAttribute()]
    public global::System.Int32 CustomerID
    {
        get
        {
            return _CustomerID;
        }
        set
        {
            if (_CustomerID != value)
            {
                OnCustomerIDChanging(value);
                ReportPropertyChanging("CustomerID");
                _CustomerID = StructuralObject.SetValidValue(value);
                ReportPropertyChanged("CustomerID");
                OnCustomerIDChanged();
            }
        }
    }
    private global::System.Int32 _CustomerID;
    partial void OnCustomerIDChanging(global::System.Int32 value);
    partial void OnCustomerIDChanged();
    
    [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
    [DataMemberAttribute()]
    public global::System.String FirstName
    {
        get
        {
            return _FirstName;
        }
        set
        {
            OnFirstNameChanging(value);
            ReportPropertyChanging("FirstName");
            _FirstName = StructuralObject.SetValidValue(value, true);
            ReportPropertyChanged("FirstName");
            OnFirstNameChanged();
        }
    }
    private global::System.String _FirstName;
    partial void OnFirstNameChanging(global::System.String value);
    partial void OnFirstNameChanged();
//....

 

El Servicio y Contexto de Dominio

El siguiente paso consistirá en agregar, también al proyecto Web de nuestra solución, un elemento Domain Service Class con el nombre PruebasSLDomainService, situado en este caso dentro del apartado Web del cuadro de diálogo Add New Item, que representará al servicio de dominio de la aplicación, y cuyo código contendrá las operaciones a realizar con las entidades en el lado servidor de la aplicación.

 

Nada más aceptar este diálogo se abrirá uno nuevo en el que especificaremos los valores iniciales de configuración para el servicio de dominio. De forma automática se habrá detectado la presencia del modelo de datos creado anteriormente, por lo que el cuadro de diálogo nos ofrecerán las entidades que lo componen. Marcaremos la entidad Customer (única disponible en este ejemplo), así como la posibilidad de editarla y generar clases de metadatos, aspectos que abordaremos más adelante.

 

Al aceptar este cuadro de diálogo se generará el código para el servicio de dominio, con las operaciones CRUD correspondientes.

[EnableClientAccess()]
public class PruebasSLDomainService : LinqToEntitiesDomainService<PruebasSLEntities>
{
    public IQueryable<Customer> GetCustomers()
    {
        return this.ObjectContext.Customers;
    }

    public void InsertCustomer(Customer customer)
    {
        if ((customer.EntityState != EntityState.Detached))
        {
            this.ObjectContext.ObjectStateManager.ChangeObjectState(customer, EntityState.Added);
        }
        else
        {
            this.ObjectContext.Customers.AddObject(customer);
        }
    }

    public void UpdateCustomer(Customer currentCustomer)
    {
        this.ObjectContext.Customers.AttachAsModified(currentCustomer, this.ChangeSet.GetOriginal(currentCustomer));
    }

    public void DeleteCustomer(Customer customer)
    {
        if ((customer.EntityState == EntityState.Detached))
        {
            this.ObjectContext.Customers.Attach(customer);
        }
        this.ObjectContext.Customers.DeleteObject(customer);
    }
}

Igualmente, en el proyecto Silverlight (cliente) de la solución, se habrá generado de forma automática el código correspondiente al contexto de dominio a partir del servicio de dominio. Dicho código se encontrará inicialmente oculto, por lo que para acceder al mismo, seleccionaremos el proyecto en el Explorador de Soluciones y haremos clic en el icono que muestra todos los archivos, desplegando el nodo Generated Code y abriendo el archivo de código que contiene.

 

En el siguiente bloque de código podemos ver un fragmento del contenido del contexto de dominio. Como nota destacable cabe mencionar que aquí, a los métodos de consulta obtenidos desde el servicio de dominio, se les añade la terminación Query.

public sealed partial class PruebasSLDomainContext : DomainContext
{
    //....
    public EntityQuery<Customer> GetCustomersQuery()
    {
        this.ValidateMethod("GetCustomersQuery", null);
        return base.CreateQuery<Customer>("GetCustomers", null, false, true);
    }
    //....

 

Creación de la fuente de datos en el cliente

Seguidamente abriremos la página XAML correspondiente al proyecto Silverlight de la solución, añadiendo a la misma un control DomainDataSource.

 

Como su propio nombre indica, este control tiene la misión de actuar como una fuente de datos, que tomando información de la base de datos a la que apunta el servicio de dominio, la transportará a través del contexto de dominio, exponiéndola en la interfaz de usuario mediante un control conectado al DomainDataSource, que en nuestro caso, como veremos más adelante, será el DataForm.

Para poner en funcionamiento el DomainDataSource pasaremos a su propiedad QueryName el nombre del método del servicio de dominio (GetCustomers) que devuelve la colección de entidades que vamos a editar. También deberemos pasar a la propiedad DomainContext el contexto de dominio, que habremos declarado como un espacio de nombres en el código XAML de la página.

<UserControl 
    x:Class="ValidacionAccion.MainPage"
    xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices"
    xmlns:domctx="clr-namespace:ValidacionAccion.Web"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <my:DomainDataSource x:Name="ddsCustomers" QueryName="GetCustomers">
            <my:DomainDataSource.DomainContext>
                <domctx:PruebasSLDomainContext />
            </my:DomainDataSource.DomainContext>
        </my:DomainDataSource>
        
        <StackPanel Background="SkyBlue">
            
        </StackPanel>
    </Grid>
</UserControl>

 

El formulario de datos

A continuación añadiremos a la página un control DataForm, asignando a su propiedad ItemsSource la fuente de datos representada por el DomainDataSource mediante data binding

 

<UserControl 
    x:Class="ValidacionAccion.MainPage"
    xmlns:my1="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"  
    xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices"
    xmlns:domctx="clr-namespace:ValidacionAccion.Web"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    
    <Grid x:Name="LayoutRoot" Background="White">
        <my:DomainDataSource x:Name="ddsCustomers" QueryName="GetCustomers">
            <my:DomainDataSource.DomainContext>
                <domctx:PruebasSLDomainContext />
            </my:DomainDataSource.DomainContext>
        </my:DomainDataSource>

        <StackPanel Background="SkyBlue">
            <my1:DataForm x:Name="frmCustomers" 
                ItemsSource="{Binding ElementName=ddsCustomers, Path=Data}"
                Margin="5" />
        </StackPanel>
    </Grid>
</UserControl>

Llegados a este punto compilaremos y ejecutaremos la solución, observando cómo el DataForm es cargado con la colección de entidades obtenidas de la base de datos; las cuales podremos editar, y por las que nos desplazaremos utilizando los botones de navegación de este control.

 

 

Presentación y ordenación de campos

Como podemos apreciar en la imagen anterior, los campos del DataForm se visualizan por defecto en el orden alfabético de las propiedades de la clase Customer, que sirve como base para crear la colección de entidades que se editan en el formulario.

Si necesitamos modificar este modo de presentación, cambiando el título y orden de los campos, tendremos que aplicar a las propiedades de la clase CustomerMetadata el atributo Display, que pertenece al espacio de nombres DataAnnotations.

La clase CustomerMetadata contiene los metadatos de la entidad Customer, y podemos encontrarla en el proyecto Web de la solución, dentro del archivo PruebasSLDomainService.metadata.cs.

using System.ComponentModel.DataAnnotations;
//....
[MetadataTypeAttribute(typeof(Customer.CustomerMetadata))]
public partial class Customer
{
    internal sealed class CustomerMetadata
    {
        private CustomerMetadata()
        {
        }

        [Display(Name = "Localidad:", Order = 5)]
        public string City { get; set; }

        [Display(Name = "Nación:", Order = 6)]
        public string Country { get; set; }

        [Display(Name = "Crédito", Order = 8)]
        public Nullable<decimal> Credit { get; set; }

        [Display(Name = "Código Cliente:", Order = 1)]
        public int CustomerID { get; set; }

        [Display(Name = "Nombre:", Order = 2)]
        public string FirstName { get; set; }

        [Display(Name = "Apellidos:", Order = 3)]
        public string LastName { get; set; }

        [Display(Name = "Fecha Último Pedido:", Order = 4)]
        public Nullable<DateTime> LastOrder { get; set; }

        [Display(Name = "Código Postal:", Order = 7)]
        public string PostalCode { get; set; }
    }
}

Tras aplicar esta modificación sobre el código, cuando volvamos a ejecutar la aplicación, el DataForm mostrará la nueva disposición de campos.

 

Llegado este punto, finalizamos la primera parte de este artículo. En la segunda entrega abordaremos los aspectos relativos a las operaciones de validación que podemos llevar a cabo utilizando el control DataForm.

Un saludo.

3 Comentarios

  1. anonymous

    Después de abordar en la primera parte de este artículo la configuración del entorno

  2. anonymous

    Continuando con la tónica iniciada en el artículo sobre la plantilla ReadOnlyTemplate del

  3. anonymous

    En el artículo dedicado a la edición de datos con plantillas en el DataForm, apuntábamos

Deja un comentario

Tema creado por Anders Norén