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

Después de la introducción a la plantilla EditTemplate del control DataForm realizada en la primera parte, en esta segunda entrega trataremos algunos aspectos adicionales de dicha plantilla, así como la inserción de nuevos datos utilizando la plantilla NewItemTemplate, proporcionada también por este control. Al igual que en la anterior entrega, el código fuente de los ejemplos está disponible en este enlace.

 

Deshabilitar el modo de edición automática

Cada vez que ejecutamos el formulario habremos comprobado que podemos editar directamente los campos de la entidad en la que nos posicionamos al navegar por la colección de entidades. Ello es debido a que la propiedad AutoEdit del DataForm tiene el valor true por defecto. Si queremos que sea el usuario quien decida cuándo quiere activar el modo de edición tendremos que asignar el valor false a dicha propiedad.

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

De esta manera, el DataForm ofrecerá inicialmente los campos en modo de lectura, incorporando un nuevo botón en la barra de herramientas con la imagen de un lapicero. Al hacer clic en dicho botón, el estado de los controles pasará de sólo lectura a edición. Para grabar los cambios realizados en los campos tan sólo es necesario hacer clic en cualquiera de los botones de navegación para movernos a otra entidad, o bien hacer clic en el botón Cancel para deshacer los cambios y devolver los campos a sus valores originales.

 

 

La propiedad AutoEdit y la plantilla ReadOnlyTemplate

Si pretendemos utilizar simultáneamente en el DataForm una plantilla EditTemplate y otra ReadOnlyTemplate, hemos de tener en cuenta que mientras que la propiedad AutoEdit tenga el valor true, el DataForm hará caso omiso de la plantilla ReadOnlyTemplate, utilizando solamente la plantilla de edición. Para solucionar este comportamiento bastará con asignar false a la propiedad AutoEdit como vemos a continuación.

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

    <toolkit:DataForm.ReadOnlyTemplate>
        <DataTemplate>
            <StackPanel>
                <toolkit:DataField Label="Código factura:" Description="Número identificador de la factura">
                    <TextBlock Text="{Binding Path=InvoiceId, Mode=OneWay}" />
                </toolkit:DataField>

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

                <toolkit:DataField Label="Fecha:">
                    <TextBlock Text="{Binding Path=InvoiceDate, Mode=OneWay}" />
                </toolkit:DataField>

                <toolkit:DataField Label="Dirección:">
                    <TextBlock Text="{Binding Path=BillingAddress, Mode=OneWay}" />
                </toolkit:DataField>

                <toolkit:DataField Label="Ciudad:">
                    <TextBlock Text="{Binding Path=BillingCity, Mode=OneWay}" />
                </toolkit:DataField>

                <toolkit:DataField Label="País:">
                    <TextBlock Text="{Binding Path=BillingCountry, Mode=OneWay}" />
                </toolkit:DataField>

                <toolkit:DataField Label="Importe:">
                    <TextBlock Text="{Binding Path=Total, Mode=OneWay}" />
                </toolkit:DataField>

                <toolkit:DataField Label="Primera factura?:">
                    <TextBlock Text="{Binding Path=FirstInvoice, Mode=OneWay}" />
                </toolkit:DataField>
            </StackPanel>
        </DataTemplate>
    </toolkit:DataForm.ReadOnlyTemplate>

    <toolkit:DataForm.EditTemplate>
        <!--....-->
    </toolkit:DataForm.EditTemplate>
</toolkit:DataForm>

  

Confirmando manualmente los cambios realizados a la entidad

Después de editar uno o varios campos del formulario, para que dichos cambios queden guardados en la colección de entidades asociada al DataForm, nos desplazaremos a otra entidad de la colección utilizando los botones de navegación de la barra de herramientas.

Pero en algunos casos puede resultar interesante obligar al usuario a aceptar de forma explícita tales modificaciones. Esto lo podemos conseguir asignando el valor false a la propiedad AutoCommit del control DataForm, la cual contiene true por defecto.

<toolkit:DataForm x:Name="frmInvoices" 
                  ItemsSource="{Binding ElementName=ddsInvoices, Path=Data}" 
                  Margin="5" 
                  Header="Edición de facturas" 
                  AutoEdit="False" 
                  AutoCommit="False">

Una vez hecha esta asignación, cada vez que editemos una entidad del DataForm se mostrará el botón OK, que utilizaremos para aceptar los cambios que hayamos efectuado en los campos del formulario.

  

El botón OK estará inicialmente deshabilitado y no se habilitará hasta que no hagamos cambios en alguno de los campos del formulario. A partir del momento en el que este botón esté disponible, los botones de la barra de herramientas se deshabilitarán, con lo que el usuario estará obligado a aceptar o cancelar haciendo clic, respectivamente, en OK o Cancel.

 

Una de las ventajas de confirmar la edición de campos mediante el botón OK radica en que una vez pulsado dicho botón seguiremos posicionados en la misma entidad que estábamos editando. Recordemos que cuando sólo disponemos del botón Cancel tenemos que movernos a otra entidad de la colección para hacer efectivos los cambios.

Como apunte estético adicional conviene saber que las propiedades CommitButtonContent y CancelButtonContent permiten asignar a estos botones un título distinto del que se ofrece por defecto.

<toolkit:DataForm x:Name="frmInvoices" 
                    ItemsSource="{Binding ElementName=ddsInvoices, Path=Data}" 
                    Margin="5" 
                    Header="Edición de facturas" 
                    AutoEdit="False" 
                    AutoCommit="False" 
                    CommitButtonContent="Aceptar cambios" 
                    CancelButtonContent="Salir de edición">

 

 

Borrar una entidad de la colección

Podemos utilizar el botón Borrar de la barra de herramientas para eliminar la entidad actual sobre la que está posicionado el DataForm, aunque debemos tener precaución, ya que el borrado se realiza sin pedir confirmación al usuario. 

 

Si queremos prevenir borrados accidentales crearemos un manipulador para el evento DeletingItem, que se producirá durante la solicitud de borrado de un elemento de la colección del formulario, lo cual nos permite anular la operación de borrado asignando el valor true a la propiedad Cancel del tipo CancelEventArgs que este manipulador recibe como parámetro.

<toolkit:DataForm x:Name="frmInvoices" 
                  <--....-->
                  DeletingItem="frmInvoices_DeletingItem">
private void frmInvoices_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e)
{
    if (MessageBox.Show("¿Borrar la factura?", "Atención", MessageBoxButton.OKCancel) == MessageBoxResult.Cancel)
    {
        e.Cancel = true;
    }
}

 

 

 

NewItemTemplate. Añadiendo nuevos elementos al DataForm

Gracias a la plantilla NewItemTemplate podemos crear una interfaz de usuario específica para los casos en que tengamos que agregar nuevos elementos a la colección de entidades del formulario haciendo clic en el botón Añadir. 

 

 El siguiente bloque de código XAML muestra el modo de declaración de esta plantilla.

<toolkit:DataForm ....>
    <!--....-->
    <toolkit:DataForm.NewItemTemplate>
        <DataTemplate>
            <StackPanel>
                <!--....-->
            </StackPanel>
        </DataTemplate>
    </toolkit:DataForm.NewItemTemplate>
</toolkit:DataForm>

La carencia de esta plantilla no significa que en el formulario no podamos añadir nuevas entidades, ya que en tal situación, el DataForm automáticamente hace uso de la plantilla EditTemplate, utilizándola como plantilla de inserción.

No obstante, la inserción es una operación de edición con ciertas particularidades, que no siempre van a poder ser sustituidas directamente mediante la plantilla EditTemplate.

Por ejemplo, supongamos que en nuestra aplicación, a la hora de crear una nueva factura, los campos del DataForm deben cumplir con una serie de pautas y restricciones, las cuales describiremos en los siguientes apartados, aportando una solución para cada una de ellas.

 

InvoiceId

La edición de este campo estará restringida en la creación de una factura, ya que como hemos comentado anteriormente, estamos ante un valor que genera automáticamente el motor de datos, por lo que resulta innecesaria su introducción manual por parte del usuario.

Por tal motivo, no incluiremos en la plantilla NewItemTemplate un control para editar este campo.

 

InvoiceDate

Este campo tampoco podrá ser editado por el usuario, ya que tomará su valor de la fecha del sistema. Sin embargo, a diferencia del anterior, su valor sí tendrá que ser visualizado.

La forma de resolver este problema consistirá, por una parte, en asignar el valor false a la propiedad IsEnabled del DataField que utilizaremos en la definición del campo. Por otro lado, en el code-behind de la página, crearemos un manipulador para el evento ContentLoaded; donde comprobaremos si la operación de edición corresponde a la creación de una nueva entidad (enumeración DataFormMode), y en caso afirmativo, obtendremos la entidad actualmente en curso, asignando la fecha del sistema a su propiedad InvoiceDate.

<toolkit:DataField Label="Fecha:" IsEnabled="False">
    <sdk:DatePicker x:Name="dtInvoiceDate"
                    SelectedDate="{Binding Path=InvoiceDate, Mode=TwoWay}" 
                    Width="110" HorizontalAlignment="Left" />
</toolkit:DataField>
private void frmInvoices_ContentLoaded(object sender, DataFormContentLoadEventArgs e)
{
    if (this.frmInvoices.Mode == DataFormMode.AddNew)
    {
        Invoice oInvoice = (Invoice)this.frmInvoices.CurrentItem;
        oInvoice.InvoiceDate = DateTime.Today;
        //....
    }
}

 

FirstInvoice

Otro campo que al igual que los dos anteriores no podrá ser modificado, debiendo tomar el valor true, o lo que es igual, mostrar un CheckBox con la casilla marcada.

<toolkit:DataField Label="Primera factura?:" IsEnabled="False">
    <CheckBox IsChecked="{Binding Path=FirstInvoice, Mode=TwoWay}" />
</toolkit:DataField>
private void frmInvoices_ContentLoaded(object sender, DataFormContentLoadEventArgs e)
{
    if (this.frmInvoices.Mode == DataFormMode.AddNew)
    {
        Invoice oInvoice = (Invoice)this.frmInvoices.CurrentItem;
        //....
        oInvoice.FirstInvoice = true;
    }
}

 

BillingAddress

A diferencia de los campos anteriores, la dirección de la factura sí podrá ser editada por el usuario. Sin embargo, este campo tendrá que tomar como valor inicial la cadena «C/», y adicionalmente, al recibir el foco, tendrá que posicionarse al final del texto del control; esto último lo lograremos codificando el evento GotFocus del control de edición.

<toolkit:DataField Label="Dirección:">
    <TextBox x:Name="txtBillingAddress" Text="{Binding Path=BillingAddress, Mode=TwoWay}" 
                Width="150" 
                GotFocus="txtBillingAddress_GotFocus" />
</toolkit:DataField>
private void txtBillingAddress_GotFocus(object sender, RoutedEventArgs e)
{
    ((TextBox)sender).Select(((TextBox)sender).Text.Length, 0);
}

CustomerId

El DataForm asigna de manera predeterminada el valor cero a los campos numéricos cuando se trata de nuevas entidades. En el campo CustomerId este comportamiento puede resultar especialmente incómodo, ya que en la práctica totalidad de las ocasiones, el usuario debe borrar el cero y teclear un código válido de cliente. Por tal causa, en el evento GotFocus de este control de edición añadiremos el código necesario para que automáticamente sea borrado su contenido si sólo hay un cero.

<toolkit:DataField Label="Código cliente:">
    <TextBox x:Name="txtCustomerId" Text="{Binding Path=CustomerId, Mode=TwoWay}" 
                Width="40" HorizontalAlignment="Left" 
                GotFocus="txtCustomerId_GotFocus" />
</toolkit:DataField>
private void txtCustomerId_GotFocus(object sender, RoutedEventArgs e)
{
    if (this.frmInvoices.Mode == DataFormMode.AddNew)
    {
        TextBox txtCustomerId = (TextBox)this.frmInvoices.FindNameInContent("txtCustomerId");

        if (txtCustomerId.Text == "0")
        {
            txtCustomerId.Text = string.Empty;
        }
    }
}

 

Tras escribir todo este código declarativo y de comportamiento para la plantilla de inserción, ejecutaremos la aplicación, que tendrá el aspecto de la siguiente figura durante la creación de una nueva entidad.

  

La creación de nuevos valores utilizando la plantilla NewItemTemplate pone punto final a este artículo, en el que nos hemos aproximado a las operaciones de edición que desde el control DataForm de Silverlight pueden realizarse a través de las plantillas que este control proporciona. Confiamos en que os resulte de utilidad.

Un saludo.

2 Comentarios

  1. anonymous

    Muy buenos tus artículos, sin embargo, tengo el problema de que al hacer cualquier movimiento no queda reflejado en la base de datos sino sólo en la pantalla, si edito un registro, voy a otro y regreso, el cambio permanece pero al salir y entrar del programa el dato queda como al principio. En la base de datos no hay cambio alguno, igual pasa al agregar o borrar una factura. ¿Por qué es eso?
    Gracias.

  2. lmblanco

    Hola Dabod

    Gracias por tu interés en los artículos. Respecto al comportamiento que mencionas sobre la grabación en la base de datos, ciertamente no indiqué este particular, que te explico ahora mismo.

    Para poder confirmar/grabar en la base de datos, los cambios que has realizado sobre las entidades desde el dataform, lo que debes hacer es llamar al método SubmitChanges del objeto DomainDataSource que tienes asociado/enlazado al DataForm. Esto lo puedes hacer, por ejemplo, desde el evento click de un botón de la página xaml, con un código parecido al siguiente:

    –XAML

    //————————————
    –code behind
    private void btnActualizarFuenteDatos_Click(object sender, RoutedEventArgs e)
    {
    this.NombreDomainDataSource.SubmitChanges();
    }

    Espero que te sirva de ayuda.

    Un saludo,
    Luismi

Deja un comentario

Tema creado por Anders Norén