A raíz de un artículo que publiqué sobre diversos aspectos de la presentación de datos en el control DataGridView, hace unos días recibí un comentario de un lector, en el que me exponía el problema que tenía para visualizar una tabla de una base de datos mediante este control, ya que en dicha tabla, uno de sus campos contenía valores de fecha, pero almacenados como cadenas, en un estilo yyyymmddhhmm; por ejemplo: 200804251200.
Supongamos que la tabla a la que se hace mención, tiene una estructura y contenido similares a los que vemos en la siguiente imagen.

Una de las posibles formas de abordar el problema que se acaba de plantear consistiría en crear, en primer lugar, una consulta SQL contra la tabla, que convirtiera los valores del campo de tipo cadena a fecha.
SELECT ProductID, Name, Color, ListPrice,
CONVERT(datetime, SUBSTRING(SellDate,1,8), 101) AS SellDate
FROM Product
Esta consulta la utilizaremos dentro del código de nuestra aplicación para obtener un objeto DataTable que asignaremos al control DataGridView.
using System.Data.SqlClient;
//....
private void Form1_Load(object sender, EventArgs e)
{
// crear la sentencia sql para obtener las filas;
// convertir el campo SellDate de tipo cadena a un tipo fecha
StringBuilder sbSQL = new StringBuilder();
sbSQL.Append("SELECT ProductID, Name, Color, ListPrice, ");
sbSQL.Append("CONVERT(datetime, SUBSTRING(SellDate,1,8), 101) AS SellDate ");
sbSQL.Append("FROM Product");
// conectar con la base de datos,
// ejecutar la consulta y asignar el resultado al DataGridView
SqlConnection cnConexion = new SqlConnection();
cnConexion.ConnectionString = "Data Source=localhost;" +
"Initial Catalog=Pruebas;" +
"Integrated Security=True";
SqlCommand cmdComando = new SqlCommand(sbSQL.ToString(), cnConexion);
SqlDataAdapter daAdaptador = new SqlDataAdapter(cmdComando);
DataSet dsDatos = new DataSet();
cnConexion.Open();
daAdaptador.Fill(dsDatos, "Product");
cnConexion.Close();
this.dataGridView1.DataSource = dsDatos.Tables[0];
}
Finalmente, en el evento CellFormatting del DataGridView, detectaremos el momento en el que a la columna SellDate le toca ser formateada, aplicándole el formato de fecha que necesitemos.
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
// cuando la celda corresponda al campo SellDate
// formatear el valor de fecha
if (this.dataGridView1.Columns[e.ColumnIndex].Name == "SellDate")
{
e.CellStyle.Format = "dd-MM-yyyy";
}
}
Como resultado obtendremos en el DataGridView la columna con el modo de presentación deseado.

Espero que este pequeño truco le resulte de utilidad a quien pueda encontrarse en esta situación.
Un saludo.
Después de abordar en la primera parte de este artículo la configuración del entorno de datos de una aplicación Silverlight, para su posterior acceso mediante el control DataForm, en esta segunda parte entraremos a comentar los mecanismos de validación que podemos implementar al usar este control.
Tipos de validación
A continuación describiremos los tipos de validación disponibles, así como su forma de utilización en la aplicación de ejemplo que estamos desarrollando, cuyo código fuente se encuentra disponible aquí.
Dependiendo del modo de edición que realicemos con el DataForm, podemos clasificar las operaciones de validación en las categorías que relacionamos seguidamente, las cuales serán desarrolladas en los próximos apartados:
--Durante la edición.
--Previa a la edición.
--Al cancelar la edición.
--Al borrar un elemento de la colección.
Validación durante la edición
Para validar los datos que el usuario introduce en los diferentes campos del DataForm tenemos a nuestra disposición un conjunto de atributos, incluidos también en el espacio de nombres DataAnnotations, que aplicaremos a la clase de metadatos de la entidad: CustomerMetadata. Todos ellos disponen del parámetro ErrorMessage, mediante el cual asignamos un texto informativo que se visualizará en la parte inferior del formulario, en el caso de que no se cumpla la validación. A continuación se describen dichos atributos:
--Required. Calificando la propiedad LastName con este atributo, obligaremos al usuario a introducir un valor en el campo del DataForm asociado a dicha propiedad, como vemos en el siguiente bloque de código.
internal sealed class CustomerMetadata
{
//....
[Required(ErrorMessage = "Este campo no puede quedar vacío")]
public string LastName { get; set; }
//....
}
El mecanismo de validación del DataForm detectará, a partir de este momento, si el campo LastName queda vacío, mostrando el mensaje de aviso oportuno.

Cuando no se cumple una condición de validación, podemos observar que el DataFom alerta al usuario de tal circunstancia a través de diversos elementos visuales, tales como el cambio de color en la etiqueta y borde del campo, así como un bloque resumen en la parte inferior del formulario, que mostrará todas las validaciones fallidas. También puede aparecer encima del nombre del campo una etiqueta flotante con el texto descriptivo del error; en el caso de que dicho texto no aparezca automáticamente, podemos visualizarlo situando el cursor encima de la esquina superior derecha del campo, cuyo borde aparece remarcado.

--StringLength. Mediante este atributo establecemos la longitud máxima de texto que podrá tener el campo del DataForm asociado a la propiedad. Como parámetros del atributo pasaremos dicho valor de longitud y el mensaje a mostrar en el caso de que la validación no tenga éxito.
internal sealed class CustomerMetadata
{
//....
[StringLength(10, ErrorMessage = "El nombre de localidad es demasiado largo")]
public string City { get; set; }
//....
}

--RegularExpression. Utilizando este atributo podemos aplicar una expresión regular a la propiedad de la clase de entidad, que nos permitirá establecer en el formulario de datos la pauta de entrada del valor en el campo. En nuestro ejemplo lo emplearemos para la propiedad PostalCode, de forma que el usuario tenga que teclear un valor compuesto por cinco números.


--Range. Este atributo nos permite establecer un intervalo de los valores que podremos introducir en el campo del DataForm asociado a la propiedad sobre la que aplicamos el atributo. En nuestro caso lo emplearemos para limitar los valores de la propiedad Credit.
internal sealed class CustomerMetadata
{
//....
[Range(120.75, 180.25, ErrorMessage = "El crédito debe estar en el intervalo de 120.75 a 180.25")]
public Nullable<decimal> Credit { get; set; }
//....
}

Validación previa a la edición
Las operaciones que se describen a continuación, si bien no constituyen acciones de validación al uso, ya que no se llevan a cabo cuando el usuario está editando los campos del formulario, hemos decidido incluirlas, ya que nos permiten controlar qué elementos de la entidad formarán parte del DataForm como campos editables.
Los atributos que utilizaremos en este apartado serán los siguientes:
--Editable. Recibe como parámetro un valor lógico, mediante el cual podemos conseguir que el campo del formulario no pueda ser modificado. En el caso de la propiedad CustomerID de la clase CustomerMetadata, dicho campo no puede ser editado para los valores existentes, ya que se trata de la clave primaria de la tabla. Como además, en la definición de la tabla es un campo de tipo identidad, no sería conveniente que el usuario pudiera introducir un valor, ya que en definitiva, es el motor de datos el que generará este valor para los nuevos registros. Por dicho motivo calificaremos la mencionada propiedad con este atributo.
internal sealed class CustomerMetadata
{
//....
[Editable(false)]
public int CustomerID { get; set; }
//....
}
--Display.AutoGenerateField. Retornamos nuevamente al atributo Display, para utilizar en esta ocasión su parámetro AutoGenerateField, que nos permite impedir la generación de un campo en el DataForm si le asignamos el valor false, lo cual haremos para la propiedad Country de la clase de metadatos.
internal sealed class CustomerMetadata
{
//....
[Display(AutoGenerateField = false, Name = "Nación:", Order = 6)]
public string Country { get; set; }
//....
}

Validación al cancelar la edición
Si nos encontramos en pleno proceso de edición y hacemos clic en el botón Cancel del DataForm, perderemos todos los cambios realizados en los campos del formulario, ya que se volverán a cargar los valores iniciales de la entidad editada.
No obstante es posible dar una oportunidad más al usuario de confirmar la cancelación, puesto que a pesar de haber presionado el botón Cancel, si añadimos un manipulador para el evento EditEnding del DataForm, podremos controlar si finalmente dicha cancelación se lleva a cabo.
El procedimiento a seguir consistirá en tomar el parámetro DataFormEditEndingEventArgs y consultar su propiedad EditAction. Si efectivamente estamos en una operación de cancelación, pedimos al usuario confirmación, asignando el valor true a la propiedad DataFormEditEndingEventArgs.Cancel en el caso de que el usuario opte por conservar las modificaciones que lleva realizadas hasta el momento.
<my1:DataForm x:Name="frmCustomers"
ItemsSource="{Binding ElementName=ddsCustomers, Path=Data}"
Margin="5"
EditEnding="frmCustomers_EditEnding" />
private void frmCustomers_EditEnding(object sender, DataFormEditEndingEventArgs e)
{
if (e.EditAction == DataFormEditAction.Cancel)
{
if (MessageBox.Show("¿Seguro que quiere cancelar?", "¡Atención!",
MessageBoxButton.OKCancel) == MessageBoxResult.Cancel)
{
e.Cancel = true;
}
}
}

Validación al borrar un elemento de la colección
Haciendo clic en el botón con signo menos (botón de borrado) situado en la barra de herramientas del DataForm, de forma similar a la descrita en el apartado anterior, podemos escribir un manipulador para el evento DeletingItem del DataForm, en el que pidamos confirmación al usuario antes de que el borrado se haga efectivo.
Para evitar el borrado simplemente debemos asignar true a la propiedad Cancel del objeto CancelEventArgs que recibe el evento como parámetro.
<my1:DataForm x:Name="frmCustomers"
ItemsSource="{Binding ElementName=ddsCustomers, Path=Data}"
Margin="5"
EditEnding="frmCustomers_EditEnding"
DeletingItem="frmCustomers_DeletingItem" />
private void frmCustomers_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e)
{
if (MessageBox.Show("¿Borrar este elemento?", "¡Atención!",
MessageBoxButton.OKCancel) == MessageBoxResult.Cancel)
{
e.Cancel = true;
}
}

Y llegados a este punto concluimos este artículo en el que hemos abordado diversas características del control DataForm relacionadas con la validación de los datos que manipula. Espero que os resulte de interés y ayuda.
Un saludo.
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.