Enlace de Datos con ADO .NET[Parte I]Fecha: 05/Ago/2005 (03/08/2005)
|
Para los curiosos...!!!
Desde mis comienzos en publicar artículos, tanto en el sitio de el Guille como en otros muchos sitios, he tenido el gusto de recibir muchas consultas y comentarios. Gracias por los comentarios y críticas, las cuales en todo momento son bienvenidas, y sobre todo gracias confiar en mi, muchas gracias por darme sus votos y de esta manera ser el autor más leído en Visual Basic .NET en el ranking de Panoramabox, todo esto es gracias a su apoyo... Aprovecho para decirles que siempre trato de atender la mayor cantidad de consultas pero debo confesar que existe una gran cantidad de consultas que no las respondo por motivo de disponibilidad de tiempo, es así que pido mil disculpas a toda aquellos amigos que no he podido ayudarles. Para solucionar esto, he decidido incluir, en artículos que publico, parte de las respuestas a sus consultas. Es más ustedes pueden seguir haciéndome llegar sus consultas y yo las publicaré en mi blog las cuales servirán de historial para mis próximos artículos, y de esta manera publicaré un artículo por cada consulta que me la hagan llegar. Por ahora, seguiré atendiendo todas las consultas en la medida posible, espero su comprensión.
Antes de empezar a relatar este extenso artículo, debo dejar en claro lo siguiente: el único objetivo es detallar aspectos importantes y básicos relacionado al enlace de datos más no otros temas lejanos a éste. Algunas cosillas relacionadas serán explicadas por ser requisito indispensable para el entendimiento básico y la comprensión de este artículo (desde luego hay temas que supongo tú debes saber, pues no puedo explicarte todo, ya que nunca terminaría de escribir este artículo, ... además mis ojos ya cambiaron de color rojo a verde, pues antes eran de color normal, creo que es porque no duermo muy seguido; bueno cada uno con su rollo... je, je,je). En esta primera parte sólo explicaré puntos claves y fundamentales sobre el enlace de datos, más adelante (en otros artículos) detallaré temas de mayor nivel y de esta manera aprenderás poco a poco muchísimas cosas acerca de la vinculación de datos. Ahora sí, allí te van las ideas acerca de "Enlace o Vinculación de Datos - Parte I"
Enlace o Vinculación de Datos - Parte I
El enlace de datos es el mecanismo proporcionado por la plataforma .NET, que en aplicaciones con interfaz windows o en aplicaciones web, enlaza objetos contenedores de datos con los controles de formulario, para poder realizar operaciones de navegación y edición.
Es así que, este artículo explicará cómo colocar un conjunto de datos en un formulario windows y cómo permitir a los usuarios interactuar con el conjunto de datos a través de los controles de formulario Windows. Aprenderá también, cómo vincular datos SQL Server a los controles de formulario windows tales como adjuntar datos a cuadros de texto, lista combinadas y cuadrículas de datos. Cabe resaltar que este artículo se limita a la vinculación de datos en formularios windows y no a formularios web Forms, dado que a la naturaleza de las páginas de formularios Web Forms y a la arquitectura de la programación Web, el enlace de datos en este tipo de páginas difiere ligeramente del efectuado en otros formularios más tradicionales, como los formularios Windows Forms.
NOTA Para la comprensión de este artículo usted debe conocer conceptos básicos acerca de ADO NET. Si usted no tiene conocimientos en ADO .NET, revise los siguientes artículos sobre la Arquitectura y Funcionalidad de ADO .NET, tanto en modo conectado como en modo desconectado.
Arquitectura y Funcionalidad de ADO .NET (Conectado) Arquitectura y Funcionalidad de ADO .NET (Desconectado)
Tipos de Data Binding o enlace a datos
Simple Data Binding Consiste en una asociación entre un control que puede mostrar un único dato y el objeto que actúa como contenedor de datos, como por ejemplo el enlace entre un control TextBox o un label y un objeto DataColumn de una DataTable de un DataSet.
Complex Data Bindings Este tipo de enlace es posible entre un control que puede actuar como interfaz o visualizador de datos debido a que dispone de la capacidad de mostrar varios o todos los datos, normalmente más de un registro, del objeto contenedor de datos. Esto se utiliza normalmente en controles DataGrid ("grilla"), ListBox o ErrorProvider. Un ejemplo clásico viene a ser el enlace entre una grilla y un objeto DataTable de un DataSet.
Elementos del Data Binding
Es posible enlazar datos (Databind) con los controles de formularios Windows, no solamente desde orígenes de datos tradicionales (como DataSets ADO .NET), sino también a casi cualquier estructura que contenga datos como estructuras, colecciones, propiedades y otros controles. Cualquier objeto que deriva de la clase Control tiene una colección de tipo ControlBindingsCollection (expuesta en la propiedad DataBinding) que puede tener objetos Binding (para enlazar). El mecanismo de enlace automático de datos a controles está compuesto por un elevado conjunto de elementos del conjunto de tipos del .NET Framework, entre clases, colecciones, enumeraciones, etc. Entre ellos tenemos:
Binding Clase que permite crear un enlace (Binding) para un control indicando algunos parámetros, donde el primer parámetro es la propiedad de destino, el segundo parámetro es el objeto origen que contiene todos los datos y el tercer parámetro es el campo origen en el objeto origen. Cuando hay una modificación en la propiedad de destino, el cambio también se hace para el objeto Origen. Si el objeto es un Dataset, los datos de la base de datos no son modificados. El Dataset debe ser sincronizado con la base de datos. Por ejemplo, puedes vincular el campo ContactName y un control TextBox1 de la siguiente manera:
Me.SqlDataAdapter1.Fill(Me.DataSet11, "Customers") Dim bind As Binding bind = New Binding("Text", Me.DataSet11, "Customers.Contactname") 'otra manera de configurar nuestro enlace es usando este código. 'bind = New Binding("Text", Me.DataSet11.Tables("Customers"), "Contactname") Me.TextBox1.DataBindings.Add(bind)BindingContext Propiedad de la clase Form que representa el contexto de enlace a datos establecido en los controles del formulario, es decir, toda la información de enlaces establecida entre los controles y objetos proveedores de datos. Devuelve un objeto de tipo BindingManagerBase o administrador base de enlaces de datos.
Cada objeto que herede de la clase Control puede tener un sólo objeto BindingContext. Ese objeto BindingContext administra los objetos BindingManagerBase para ese control y cualquier control que esté incluido. Hay que utilizar BindingContext con el fin de crear o devolver BindingManagerBase para un origen de datos utilizado por los controles con enlace a datos incluidos. Normalmente, se utiliza el objeto BindingContext de la clase Form con el fin de devolver objetos BindingManagerBase para los controles con enlace a datos del formulario. Si se utiliza un control contenedor, como GroupBox, Panel o TabControl, para incluir controles con enlace a datos, se puede crear un objeto BindingContext sólo para ese control contenedor y sus controles. De este modo, cada parte de un formulario puede ser administrada por su propio objeto BindingManagerBase.
BindingMangerBase Objeto que se encarga de administrar un conjunto de objetos de enlace, por ejemplo, los de un formulario obtenidos a través del BindingContext del formulario.
Para devolver un objeto BindingManagerBase concreto, es necesario pasar uno de los siguientes parámetros a la propiedad Item:
Sólo el origen de datos, si el objeto BindingManagerBase que se desea no requiere una ruta de desplazamiento. Por ejemplo, si BindingManagerBase administra un conjunto de objetos Binding que usan ArrayList o DataTable como DataSource, no se requiere ninguna ruta de desplazamiento. Origen de datos y ruta de desplazamiento. La ruta de desplazamiento (establecida en el parámetro DataMember de la propiedad Item) es obligatoria cuando BindingManagerBase administra un conjunto de objetos Binding cuyo origen de datos contiene múltiples objetos. Por ejemplo, DataSet puede contener varios objetos DataTable vinculados mediante objetos DataRelation. En tal caso, se requiere la ruta de desplazamiento para permitir que BindingContext devuelva el objeto BindingManagerBase correcto.Pues yo les aconsejaría que siempre opten por manejar una ruta de desplazamiento, aún cuando estén manejando un origen de datos sencillo como un sólo DataTable, de esta manera, se evitarán algunas complicaciones y podrán ser más "formales".
NOTA Algo a resaltar es que una de las características interesantes en el desarrollo con Visual Basic .NET es la posibilidad de vincular cualquier propiedad de un control visible, como su propiedad BackColor o ForeColor, a una columna de datos. De esta manera, existe la posibilidad de que un origen de datos local controle dinámicamente el formato de un formulario así como los datos que muestra el formulario.
Ejemplo ilustrativo...
A continuación mostraré un ejemplillo donde tras rellenar el conjunto de datos (usando el asistente de configuración de objetos ADO .NET ) asociado a un formulario, haremos referencia al conjunto de datos mediante los controles de formulario. Para vincular los controles de formulario a las columnas del conjunto de datos (una sola tabla) y que los cuadros dependan de la pulsación de nuestros botones navegadores se necesitan unas cuantas líneas de código o también puedes hacerlo en tiempo de diseño (esto te ahorraría bastante tiempo). Esencialmente se trata de navegar los registros cuyos campos son CustomerId, CompanyName, ContactName, ContactTitle de la tabla Customers de la base de datos Northwind, para esto previamente debió vincularse los respectivos controles TextBox a cada una de estas columnas. Como te decía, esta vinculación puede hacerse en tiempo de diseño o en tiempo de ejecución, personalmente, lo hice en tiempo de diseño, pero no te alarmes, que a continuación te enseño a hacerlo de ambas maneras. Para que te vayas haciendo una idea acerca de lo que verás, allí te va la interfaz del formulario.
Esto es fácil. Sigue los siguiente pasos para implementar el ejemplo:
1.Debes generar el conjunto de datos(tan sólo los campos CustomerId, CompanyName, ContactName, ContactTitle de la tabla Customers), mediante el asistente de configuración de ADO .NET.
2.Ahora agrega cuatro controles TextBox sobre tu formulario windows y en la propiedad DataBinding de cada uno de los controles debes establecer el campo a enlazar, como se aprecia en la siguiente imagen.
Opcionalmente puedes hacerlo mediante código. Y esto sería:
Dim AdministradorBaseDeEnlaces As BindingManagerBase Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles MyBase.Load 'Rellenamos nuestro contenedor de datos Me.SqlDataAdapter1.Fill(Me.MisDatos1, "Customers")'Establecemos la vinculación para cada uno de los controles TextBox. Dim Bind As BindingBind = New Binding("Text", Me.MisDatos1, "Customers.CustomerId") Me.TextBox1.DataBindings.Add(Bind) Bind = NothingBind = New Binding("Text", Me.MisDatos1, "Customers.CompanyName") Me.TextBox2.DataBindings.Add(Bind) Bind = NothingBind = New Binding("Text", Me.MisDatos1, "Customers.ContactName") Me.TextBox3.DataBindings.Add(Bind) Bind = NothingBind = New Binding("Text", Me.MisDatos1, "Customers.ContactTitle") Me.TextBox4.DataBindings.Add(Bind) Bind = Nothing'Finalmente obtenemos del contexto de enlace de nuestro 'formulario un objeto adminitrador que nos servirá para navegar 'cada uno de los registros de la tabla customers. AdministradorBaseDeEnlaces = Me.BindingContext(Me.MisDatos1, "Customers")End SubEn base al código anterior, este sería el resto de código que hace posible la navegación de los registros.Private Sub Navegar(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Button1.Click, Button2.Click, Button3.Click, Button4.ClickIf sender Is Me.Button1 Then Me.AdministradorBaseDeEnlaces.Position = _ Me.AdministradorBaseDeEnlaces.Position.MinValue ElseIf sender Is Me.Button2 Then Me.AdministradorBaseDeEnlaces.Position += 1 ElseIf sender Is Me.Button3 Then Me.AdministradorBaseDeEnlaces.Position -= 1 ElseIf sender Is Me.Button4 Then Me.AdministradorBaseDeEnlaces.Position = _ Me.AdministradorBaseDeEnlaces.Position.MaxValue End IfEnd Sub3. Después de realizar la vinculación (en tiempo de diseño) debemos implementar la funcionalidad de nuestros botones navegadores. Y esto sería todo el código:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles MyBase.Load 'Rellenamos nuestro contenedor de datos Me.SqlDataAdapter1.Fill(Me.MisDatos1, "Customers")End SubPrivate Sub Navegar(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Button1.Click, Button2.Click, Button3.Click, Button4.Click If sender Is Me.Button1 Then Me.BindingContext(Me.MisDatos1, "Customers").Position = _ Me.BindingContext(Me.MisDatos1, "Customers").Position.MinValue ElseIf sender Is Me.Button2 Then Me.BindingContext(Me.MisDatos1, "Customers").Position += 1 ElseIf sender Is Me.Button3 Then Me.BindingContext(Me.MisDatos1, "Customers").Position -= 1 ElseIf sender Is Me.Button4 Then Me.BindingContext(Me.MisDatos1, "Customers").Position = _ Me.BindingContext(Me.MisDatos1, "Customers").Position.MaxValue End IfEnd Sub
NOTA Les recomiendo que la vinculación de datos lo hagan, en la medida posible, en tiempo de diseño, ya que es más rápido,fácil y se necesita menos código, de esta manera se evitarán unos pequeños dolores de cabeza durante la depuración de código. 4. Hasta aquí hemos terminado. Ahora tienes que ir navegando cada uno de los registros de la tabla customers. Sencillo verdad ?. Más adelante te explicaré otras cosillas interesantes.
Como habrás notado, es relativamente sencillo realizar este tipo de vinculación. Estás preparado para seguir?, espero que sí... ahora continuemos.
Vínculo de un DataSet con un Control DataGrid
Una vez terminada de explicar el ejemplo de Enlace Simple de Datos o Simple DataBinding, pasaremos a aprender algo más divertido, se trata de cómo implementar la vinculación Compleja de Datos usando controles un poco más complicados como por ejemplo una grilla, un ListBox...pero por esta vez usaré la Grilla.
Para esto agregaremos un control DataGrid en el formulario, este control se va a enlazar con la tabla Customers de la base de datos Northwind. A través de este formulario podemos editar los registros de la tabla y añadir nuevos; al trabajar en desconexión, hasta que no pulsemos el botón Actualizar de este formulario, el objeto DataAdapter no actualizará los datos del DataSet hacia la base de datos física. Observe que he modificado los campos City y Country en el primer registro de la grilla. Luego de actualizar, un mensaje nos avisá que los cambios han sido guardados. Un forma práctica de verificar esto, es volver a cargar la aplicación, la cual contendrá los cambios que hayas realizado.
Ahora dejo a su disposición todo el código que hace posible la ilustración anterior.
Imports System.Data.SqlClient Public Class Form1 Inherits System.Windows.Forms.Form 'CODIGO GENERADO POR EL DISEÑADOR DE WINDOWS FORMS Public DataSet As New DataSet Public Adapter As SqlDataAdapter Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles MyBase.Load Me.StatusBar1.Panels(0).Text = "Percy Reyes:: ¡Los peruanos Sí podemos !" Dim SQL_CONECTION_STRING As String = "Server=localhost;" & _ "DataBase=Northwind;Integrated Security=SSPI;Connect Timeout=5" Dim oConection As New SqlConnection(SQL_CONECTION_STRING) Dim SQL_COMMAND_STRING As String = "Select CustomerID,CompanyName,ContactName," & _ "ContactTitle,Address,City,Country From Customers" Adapter = New SqlDataAdapter(SQL_COMMAND_STRING, oConection) Dim oCommandBuilder As SqlCommandBuilder = New SqlCommandBuilder(Adapter) DataSet = New DataSet Adapter.Fill(DataSet, "Clientes") Me.DataGrid1.DataSource = DataSet Me.DataGrid1.DataMember = "Clientes" End Sub Private Sub BtnActualizar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles BtnActualizar.Click If DataSet.HasChanges Then Adapter.Update(DataSet, "Clientes") Me.StatusBar1.Panels(0).Text = "Los cambios han sido guardados." Me.Timer1.Enabled = True Me.Timer1.Start() End If End SubPublic i As IntegerPrivate Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Timer1.Tick If i = 5 Then i = 0 Timer1.Stop() Timer1.Enabled = False Me.StatusBar1.Panels(0).Text = "Percy Reyes:: ¡Los peruanos Sí podemos !" Else i += 1 Me.StatusBar1.Panels(0).Text = " " Me.StatusBar1.Panels(0).Text = Space(2 * i) & "Los cambios han sido guardados." End If End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Button1.Click End End Sub End ClassRelación Maestro/Detalles en dos DataGridEs posible configurar las propiedades del DataGrid de tal manera que nos permita visualizar más de una tabla, permitiendo al usuario elegir cualquiera de las tablas que se muestran en él, también podemos trabajar con tablas relacionadas, permitiéndonos obtener, de esta manera, las filas hijas, relacionadas con la fila seleccionada de la tabla padre. Este control implementa de forma transparente todos los mecanismos necesarios gracias al DataBinding, por lo que, una vez creada la relación, sólo hemos de asignar a su propiedad DataSource, la tabla pabre del DataSet.
Podemos separar la visualización de las tablas maestro detalle en dos DataGrid independientes. Para sincronizar ambos controles, debemos asignar al que actuará como detalle, una cadena con el nombre de la tabla maestra, junto con el nombre de la relación empleando el siguiente formato: TablaMaestra.Relacion. Más adelante muestro un sencillo ejemplo de cómo lograr esto. No corrás ...!! es muy excitante aprender Visual Basic .NET.
Sigamos. Como te decía, en muchas ocasiones las aplicaciones tienen que trabajar con tablas relacionadas. Aunque un conjunto de datos contiene tablas y columnas al igual que una base de datos, no incluye intrínsecamente la capacidad de las bases de datos para relacionar tablas. No obstante, se pueden crear objetos DataRelation que establezcan una relación entre una tabla primaria (maestra) y una tabla secundaria (de detalle) sobre la base de una clave común.
El objeto DataRelation puede ejecutar dos funciones:
Puede poner a su disposición los registros relacionados con el registro con el que esté trabajando. Proporciona registros secundarios cuando se está en un registro primario y un registro primario si se está trabajando con un registro secundario. Puede exigir restricciones de integridad referencial, como la eliminación de los registros secundarios relacionados cuando se elimina un registro primario.
NOTA Es importante comprender la diferencia entre una combinación auténtica y la función de un objeto DataRelation. En una combinación auténtica, los registros se toman de tablas primarias y secundarias y se ponen en un único conjunto de registros plano. Cuando se utiliza un objeto DataRelation no se crea ningún conjunto de registros nuevo. En su lugar, se hace un seguimiento de la relación entre las tablas y se mantienen sincronizados los registros primarios y secundarios.
Ejemplo Para esta ilustración emplearemos dos tablas (Categories y Products) de la base de datos Northwind. Estableceremos la relación maestro-detalle entre esta dos tablas a través de la columna CategoryId. Luego de hacer esto, escribiremos el código necesario para visualizar la tabla detalle "Productos" en otra DataGrid de acuerdo a la fila seleccionada en la tabla padre "Categorias".
Alli te dejó con el código, revísalo y espero sea de utilidad. No olvides de darme tu alientador voto, vota por mí.
Imports System.Data.SqlClient
Public Class Form1
Inherits System.Windows.Forms.Form
'CODIGO GENERADO POR EL DISEÑADOR DE WINDOWS FORMS
Public DataSet As New DataSet
Public AdapterCategories As SqlDataAdapter
Public AdapterProducts As SqlDataAdapter
Dim SQL_CONECTION_STRING As String = "Server=localhost;" & _
"DataBase=Northwind;Integrated Security=SSPI;Connect Timeout=5"
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.Load
Me.StatusBar1.Panels(0).Text = "Percy Reyes:: ¡Los peruanos Sí podemos !"
Dim oConection As New SqlConnection(SQL_CONECTION_STRING)
Dim SQL_STRING_CATEGORIES As String = "Select CategoryID,CategoryName,Description From Categories"
Dim SQL_STRING_PRODUCTS As String = "Select ProductID,ProductName, SupplierID, " & _
" CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock, UnitsOnOrder From Products"
AdapterCategories = New SqlDataAdapter(SQL_STRING_CATEGORIES, oConection)
Dim oCommandBuilder1 As SqlCommandBuilder = New SqlCommandBuilder(AdapterCategories)
AdapterProducts = New SqlDataAdapter(SQL_STRING_PRODUCTS, oConection)
Dim oCommandBuilder2 As SqlCommandBuilder = New SqlCommandBuilder(AdapterProducts)
DataSet = New DataSet
AdapterCategories.Fill(DataSet, "Categorias")
Me.DataGrid1.DataSource = DataSet
Me.DataGrid1.DataMember = "Categorias"
AdapterProducts.Fill(DataSet, "Productos")
Me.DataGrid2.DataSource = DataSet
DataSet.Relations.Add("Categorias_Productos", DataSet.Tables("Categorias").Columns("CategoryId"), _
DataSet.Tables("Productos").Columns("CategoryId"))
Me.DataGrid2.DataMember = "Categorias.Categorias_Productos"
Me.DataGrid1.CaptionText = "Tabla maestra: " & _
UCase(DataSet.Relations("Categorias_Productos").ParentTable.TableName.ToString())
End Sub
Private Sub BtnActualizar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles BtnActualizar.Click
If DataSet.HasChanges Then
AdapterCategories.Update(DataSet, "Categorias")
AdapterProducts.Update(DataSet, "Productos")
Me.StatusBar1.Panels(0).Text = "Los cambios han sido guardados."
Me.Timer1.Enabled = True
Me.Timer1.Start()
End If
End Sub
Public i As Integer
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Timer1.Tick
If i = 5 Then
i = 0
Timer1.Stop()
Timer1.Enabled = False
Me.StatusBar1.Panels(0).Text = "Percy Reyes:: ¡Los peruanos Sí podemos !"
Else
i += 1
Me.StatusBar1.Panels(0).Text = " "
Me.StatusBar1.Panels(0).Text = Space(2 * i) & "Los cambios han sido guardados."
End If
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Button1.Click
End
End Sub
Private Sub DataGrid1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles DataGrid1.MouseDown
Try
'esto código nos permite capturar el contenido de la celda actual,
' e informar al usuario sobre la lista de productos por categoría.
Dim Hit As DataGrid.HitTestInfo
Hit = Me.DataGrid1.HitTest(e.X, e.Y)
If Hit.Type = DataGrid.HitTestType.Cell Then
Me.DataGrid2.CaptionText = "Tabla detalle: Lista de " & _
UCase(DataSet.Relations("Categorias_Productos").ChildTable.TableName.ToString()) & _
"de " & Mid(UCase(DataSet.Relations("Categorias_Productos").ParentTable.TableName.ToString()), _
1, DataSet.Relations("Categorias_Productos").ParentTable.TableName.Length - 1) _
& Space(1) & Microsoft.VisualBasic.UCase(Me.DataGrid1(Hit.Row, 1))
End If
Catch ex As Exception
Me.DataGrid2.CaptionText = "Tabla detalle: Lista VACÍA "
End Try
End Sub
End ClassNo olvides de darme tu voto en PanoramaBox, pues es una manera de animarme a seguir compartiendo contigo lo que voy aprendiendo.
Saludos desde Trujillo !!!
Espacios de nombres usados en el código de este artículo:
System.Data
System.Data.SqlClient
Fichero con el código de ejemplo: Percynet_SimpleDataBinding.zip - Tamaño 16 KB
Fichero con el código de ejemplo: Percynet_ComplexDataBinding_DataGrid.zip - Tamaño 9 KB
Fichero con el código de ejemplo: Percynet_ComplexDataBinding_MaestroDetalle.zip - Tamaño 10 KB