DataForm EditTemplate y NewItemTemplate. Edición de datos en Silverlight mediante plantillas (1)

Continuando con la tónica iniciada en el artículo sobre la plantilla ReadOnlyTemplate del control DataForm, en esta ocasión abordaremos el desarrollo de un formulario centrándonos en los aspectos relativos a la edición de datos, utilizando las plantillas que a tal efecto este control proporciona.

En esta primera entrega crearemos el proyecto de ejemplo sobre el que trabajaremos, realizando una introducción al uso de la plantilla EditTemplate, donde explicaremos el comportamiento predeterminado que muestra durante la edición de los datos. En la segunda parte abordaremos la posibilidad de otorgar al usuario un mayor control sobre la operación de edición, así como el borrado de datos. También nos ocuparemos de la inserción de nuevos valores empleando la plantilla NewItemTemplate.

 

La fuente de datos

En el presente artículo volveremos a usar la base de datos MusicaGest, cuya tabla Invoice está basada en la tabla del mismo nombre de la base de datos Chinook, disponible en CodePlex. Podemos ver el script de creación en el artículo sobre la plantilla ReadOnlyTemplate anteriormente mencionado.

 

El proyecto de ejemplo

Antes de empezar a trabajar en el diseño de la plantilla crearemos en Visual Studio 2010 el proyecto de ejemplo (de tipo Silverlight Application con WCF RIA Services habilitado) que nos acompañará a lo largo del artículo, al que daremos el nombre de PlantillasEdicion, y cuyo código fuente podemos encontrar aquí.

Obtendremos como resultado una solución compuesta por dos proyectos: uno de ellos será un proyecto Web que representa a la parte servidora de la aplicación, y el otro un proyecto Silverlight que representa a la parte cliente.

Como elementos de partida de la aplicación, en el proyecto Web crearemos un modelo de datos conectado a la base de datos MusicaGest, y un servicio de dominio para manejar la entidad Invoice proporcionada por el modelo de datos.

public class MusicaGestDomainService : LinqToEntitiesDomainService<MusicaGestEntities>
{
    public IQueryable<Invoice> GetInvoices()
    {
        return this.ObjectContext.Invoices;
    }
    //....

 

Comportamiento predeterminado en edición

A continuación abriremos la página MainPage.xaml, añadiendo un DomainDataSource y un DataForm, en los que estableceremos la configuración mínima que permita su funcionamiento.

<UserControl x:Class="PlantillaEdicion.MainPage"    
xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices"
xmlns:domainctx="clr-namespace:PlantillaEdicion.Web"    xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"  
    <--....-->
    <riaControls:DomainDataSource x:Name="ddsInvoices" QueryName="GetInvoices">
        <riaControls:DomainDataSource.DomainContext>
            <domainctx:MusicaGestDomainContext />
        </riaControls:DomainDataSource.DomainContext>
    </riaControls:DomainDataSource>
    <!--....-->
    <toolkit:DataForm x:Name="frmInvoices" ItemsSource="{Binding ElementName=ddsInvoices, Path=Data}" Margin="5" >
    </toolkit:DataForm>
    <!--....-->

El DataForm generará un control de edición (campo) por cada propiedad de la entidad a la que se encuentra enlazada a través del DomainDataSource. Para los valores textuales y numéricos se utilizarán controles TextBox, para las fechas DatePicker, y para los lógicos CheckBox.

 

Esta sencillez en la creación del formulario de datos arroja algunos inconvenientes, entre los que destacaríamos el hecho de que el nombre de los campos de la tabla se usa para ordenar los campos del DataForm, aunque rara vez el orden de los campos resulta adecuado de esta manera. El nombre del campo también se emplea como título situado al lado de cada control de edición, lo que puede resultar poco descriptivo en ocasiones. Respecto a las dimensiones de los campos, encontramos que todos mantienen la misma anchura, siendo esto algo innecesario en campos como CustomerId, InvoiceDate o Total, por poner un ejemplo.

 

La plantilla EditTemplate

Si bien podríamos solucionar parte de los problemas que acabamos de plantear aplicando ciertos atributos del espacio de nombres DataAnnotations a los metadatos de la entidad Invoice, tal y como se explica aquí, en esta ocasión vamos a recurrir a la plantilla EditTemplate, que utilizaremos para aquellas operaciones de modificación de los valores existentes en la colección de entidades enlazadas al formulario de datos. Mediante esta plantilla podremos conseguir una interfaz de usuario mucho más flexible y personalizable que la generada por el DataForm automáticamente.

Volviendo al editor de código de la página MainPage.xaml, dentro de la declaración del DataForm situaremos la plantilla utilizando las etiquetas EditTemplate y DataTemplate. 

<toolkit:DataForm x:Name="frmInvoices" 
                    ItemsSource="{Binding ElementName=ddsInvoices, Path=Data}" 
                    Margin="5" >
    <toolkit:DataForm.EditTemplate>
        <DataTemplate>
            <StackPanel>
            <!--....-->
            </StackPanel>
        </DataTemplate>
    </toolkit:DataForm.EditTemplate>
</toolkit:DataForm>

Como acabamos de comprobar en el anterior bloque de código, también incluiremos un elemento contenedor dentro de DataTemplate (un panel StackPanel), ya que de no hacerlo así, se produciría un error que indicaría que la propiedad VisualTree está asignada más de una vez.

 

A partir de aquí, para cada campo que queramos añadir al DataForm agregaremos una etiqueta DataField (igual que en la plantilla ReadOnlyTemplate), en cuyo interior situaremos el control con el que editaremos el valor del campo, o bien, un control de sólo lectura en caso de que el campo no deba ser modificado. Por otro lado, también deberemos asignar a la propiedad del control encargada de visualizar el valor del campo una expresión de enlace a datos, que obtenga dicho valor y lo presente en el control.

<UserControl.Resources>
    <Style TargetType="TextBox" >
        <Setter Property="HorizontalAlignment" Value="Left" />
    </Style>
</UserControl.Resources>
<!--....-->

<toolkit:DataForm x:Name="frmInvoices" 
                    ItemsSource="{Binding ElementName=ddsInvoices, Path=Data}" 
                    Margin="5">

    <toolkit:DataForm.EditTemplate>
        <DataTemplate>
            <StackPanel>
                <toolkit:DataField Label="Código factura:">
                    <TextBox Text="{Binding Path=InvoiceId, Mode=TwoWay}" Width="40" />
                </toolkit:DataField>

                <toolkit:DataField Label="Código cliente:">
                    <TextBox Text="{Binding Path=CustomerId, Mode=TwoWay}" Width="40" />
                </toolkit:DataField>

                <toolkit:DataField Label="Fecha:">
                    <sdk:DatePicker SelectedDate="{Binding Path=InvoiceDate, Mode=TwoWay}" 
                                    Width="110" HorizontalAlignment="Left" />
                </toolkit:DataField>

                <toolkit:DataField Label="Dirección:">
                    <TextBox Text="{Binding Path=BillingAddress, Mode=TwoWay}" Width="150" />
                </toolkit:DataField>

                <toolkit:DataField Label="Ciudad:">
                    <TextBox Text="{Binding Path=BillingCity, Mode=TwoWay}" Width="120" />
                </toolkit:DataField>

                <toolkit:DataField Label="País:">
                    <TextBox Text="{Binding Path=BillingCountry, Mode=TwoWay}" Width="80" />
                </toolkit:DataField>

                <toolkit:DataField Label="Importe:">
                    <TextBox Text="{Binding Path=Total, Mode=TwoWay}" Width="50" />
                </toolkit:DataField>

                <toolkit:DataField Label="Primera factura?:">
                    <CheckBox IsChecked="{Binding Path=FirstInvoice, Mode=TwoWay}" />
                </toolkit:DataField>
            </StackPanel>
        </DataTemplate>
    </toolkit:DataForm.EditTemplate>

Gracias al uso de la plantilla EditTemplate podemos resolver el conjunto de inconvenientes mencionados en el apartado anterior. En primer lugar, el orden de los campos viene determinado por la posición con la que situemos las etiquetas DataField dentro de la plantilla y no por su nombre; para los títulos de los campos empleamos la propiedad Label del DataField; y para ajustar su tamaño, en el control de edición usamos la propiedad Width combinada con HorizontalAlignment, ésta última establecida como recurso para controles de tipo TextBox, que son la mayoría de los que componen el formulario.

  

Los controles de edición que estamos utilizando para los campos de la plantilla son iguales a los que emplea el DataForm cuando los genera automáticamente. Sin embargo, si hacemos una rápida revisión de los controles disponibles en la Barra de Herramientas de Visual Studio, podremos deducir que para los valores que deben manejar ciertos campos, el usuario obtendría una experiencia más satisfactoria usando otro tipo de controles más especializados. Este es un tema que tiene la suficiente entidad y amplitud para tratarse por separado, por lo que será abordado en próximos artículos.

 

Campos no editables

Puesto que el campo InvoiceId tiene activado el atributo Identity en la tabla de la base de datos, su valor es generado por el motor de datos y no debe ser modificado. El control DataForm es consciente de este tipo de situaciones, y por ello, el control TextBox que estamos empleando como campo del formulario no permite ser editado. Este comportamiento se produce porque, internamente, el DataForm está asignando el valor True a la propiedad IsReadOnly del DataField asociado al campo InvoiceId.

También podemos, naturalmente, asignar de forma manual esta propiedad a cualquier otro campo del formulario que necesitemos. A continuación lo vemos aplicado sobre el campo CustomerId.

<toolkit:DataField Label="Código cliente:" IsReadOnly="True">
    <TextBox Text="{Binding Path=CustomerId, Mode=TwoWay}" />
</toolkit:DataField>

  

Una vía alternativa, que será la que utilicemos en el proyecto de ejemplo de este artículo, consiste en emplear un control no editable (TextBlock, Label, etc.) para aquellos campos que no deban ser modificados. En nuestro caso emplearemos un TextBlock para el campo InvoiceId, volviendo a dejar editable el campo CustomerId.

<toolkit:DataField Label="Código factura:">
    <TextBlock Text="{Binding Path=InvoiceId, Mode=OneWay}" />
</toolkit:DataField>

 

Descripción extendida de campo

La posibilidad de que los campos del DataForm dispongan de un texto adicional, explicativo de su contenido, es una característica que podemos lograr a través de la propiedad Description de la etiqueta DataField.

<toolkit:DataField Label="Código factura:" Description="Número de factura">
    <TextBox Text="{Binding Path=InvoiceId, Mode=TwoWay}" Width="40" />
</toolkit:DataField>

<toolkit:DataField Label="Código cliente:" Description="Identificador del cliente">
    <TextBox Text="{Binding Path=CustomerId, Mode=TwoWay}" Width="40" />
</toolkit:DataField>

<toolkit:DataField Label="Fecha:" Description="Fecha de emisión de la factura">
    <sdk:DatePicker SelectedDate="{Binding Path=InvoiceDate, Mode=TwoWay}" 
                    Width="110" HorizontalAlignment="Left"  />
</toolkit:DataField>

<toolkit:DataField Label="Dirección:" Description="Dirección de entrega">
    <TextBox Text="{Binding Path=BillingAddress, Mode=TwoWay}" Width="150" />
</toolkit:DataField>

<toolkit:DataField Label="Ciudad:" Description="Localidad de entrega">
    <TextBox Text="{Binding Path=BillingCity, Mode=TwoWay}" Width="120" />
</toolkit:DataField>

<toolkit:DataField Label="País:" Description="Nacionalidad de entrega">
    <TextBox Text="{Binding Path=BillingCountry, Mode=TwoWay}" Width="80" />
</toolkit:DataField>

<toolkit:DataField Label="Importe:" Description="Total a pagar">
    <TextBox Text="{Binding Path=Total, Mode=TwoWay}" Width="50" />
</toolkit:DataField>

<toolkit:DataField Label="Primera factura?:" Description="Indica si es la primera vez que se factura al cliente">
    <CheckBox IsChecked="{Binding Path=FirstInvoice, Mode=TwoWay}" />
</toolkit:DataField>

Una vez asignada esta propiedad, en tiempo de ejecución aparecerá un icono junto al campo, que mostrará una etiqueta flotante al situar encima el cursor. En el caso de campos de sólo lectura como InvoiceId, esta característica no funciona aunque hayamos asignado valor a la propiedad.

 

 

Ubicación de títulos de campo y descripción

Por defecto, el control DataForm sitúa los títulos de los campos a la izquierda del control de edición y los iconos de descripción a la derecha. Sin embargo, este comportamiento puede ser modificado mediante las propiedades LabelPosition y DescriptionViewerPosition, que contienen, respectivamente, un valor de las enumeraciones DataFieldLabelPosition y DataFieldDescriptionViewerPosition, siendo posible, por ejemplo, situar el nombre del campo encima del control de edición del mismo, y el icono de descripción junto al nombre del campo, como vemos en el siguiente bloque de código, en el que también aprovechamos para presentar la propiedad Header, mediante la que asignamos un título al formulario.

<toolkit:DataForm x:Name="frmInvoices" 
                  ItemsSource="{Binding ElementName=ddsInvoices, Path=Data}" 
                  Margin="5" 
                  LabelPosition="Top"
                  DescriptionViewerPosition="BesideLabel"
                  Header="Edición de facturas">

  

Además del control DataForm, estas propiedades también están disponibles para el control DataField, por lo que pueden ser asignadas de forma independiente a cada campo del formulario que necesitemos, como vemos en el siguiente bloque de código.

<toolkit:DataForm.EditTemplate>
    <!--....-->
    <toolkit:DataField Label="Dirección:" Description="Dirección de entrega" LabelPosition="Top">
        <TextBox Text="{Binding Path=BillingAddress, Mode=TwoWay}" Width="150" />
    </toolkit:DataField>

    <toolkit:DataField Label="Ciudad:" Description="Localidad de entrega" DescriptionViewerPosition="BesideLabel">
        <TextBox Text="{Binding Path=BillingCity, Mode=TwoWay}" Width="120" />
    </toolkit:DataField>

    <toolkit:DataField Label="País:" Description="Nacionalidad de entrega" DescriptionViewerPosition="BesideLabel" LabelPosition="Top">
        <TextBox Text="{Binding Path=BillingCountry, Mode=TwoWay}" Width="80" />
    </toolkit:DataField>
    <!--....-->
</toolkit:DataForm.EditTemplate>

No obstante se trata de un ejemplo extremo, ya que el formulario obtenido resulta un tanto desorganizado.

  

Llegados a este punto, concluimos la primera parte de este artículo, esperamos que todos aquellos aspectos de edición con el control DataForm comentados hasta el momento os resulten de ayuda e interés.

Un saludo. 

1 Comentario

  1. 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