Introducción
El desarrollo modular de aplicaciones web requiere la confección de componentes que permitan encapsular ciertos compartimientos que serán reutilizados, o que necesitan ser tratados de una forma unificada para facilitar la mantenibilidad del desarrollo.
Es por eso que en ciertas ocasiones el uso de User Controls es una buena práctica, aunque un aspecto no menor es la comunicación entre estos componentes encapsulados con el resto de la aplicación, o con otros componentes.
En este texto explicare como lograr la comunicación entre dos User Control, pasando datos de uno a otro de una forma desacoplada.
Diseño
El planteo se basará en la utilización de dos users control muy simples que estarán contenidos en una misma página web.
El primer user control encapsulará una grilla la cual cargara un listado de empleados.
El segundo contiene una serie de Textbox que serán utilizados para visualizar el registro seleccionado del primer user control.
User Control – Listado
Este estará formado por un control GridView.
using System; using System.Collections.Generic; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Data; namespace WebUserControlsCommunicated { public partial class ucGrid : System.Web.UI.UserControl { public delegate void GridSelectorCommandEventHandler(GridSelectorCommandEventArgs e); public event GridSelectorCommandEventHandler GridSelectorChanged; public class GridSelectorCommandEventArgs { public int Id { get; protected set; } public string Nombre { get; protected set; } public string Cargo { get; protected set; } public GridSelectorCommandEventArgs(int id, string nombre, string cargo) { this.Id = id; this.Nombre = nombre; this.Cargo = cargo; } } public DataTable DataSource { get; set; } protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { GridView1.DataSource = this.DataSource; GridView1.DataBind(); } } protected void GridView1_SelectedIndexChanged(object sender, EventArgs e) { int Id = Convert.ToInt32(GridView1.SelectedRow.Cells[1].Text); string nombre = Convert.ToString(GridView1.SelectedRow.Cells[2].Text); string cargo = Convert.ToString(GridView1.SelectedRow.Cells[3].Text); if (GridSelectorChanged != null) GridSelectorChanged(new GridSelectorCommandEventArgs(Id, nombre, cargo)); } } }
Los puntos importantes a destacar del código serian:
- línea 30, define la propiedad que permitirá asignar el origen de datos que se usará para cargar el GridView. Esta propiedad será utilizada en el evento Page_Load del user control, asignando el contenido.
- líneas 12 y 13, en las mismas se define el handler y evento que permitirán que la información de los items seleccionados del GridView viajen desde el interior del user control hacia la página.
- líneas 15-27, definen el argumento del evento que será usado para realizar el pasaje de los datos seleccionados en la grilla. Es por medio de este argumento que se podrá recuperar los valores elegidos, ya que desde fuera del user control no se tendra acceso directo al GridView.
Habría una alternativas a esta opción, la cual consiste en dejar disponible en una propiedad los valores seleccionados, imitando un poco a las propiedades como ser el SelectedRow, o SelectedItems que poseen otro controles.
- líneas 42-53, estas defines el evento local al user control que se ejecutará al utilizar el botón de selección del GridView, básicamente será una especie de conversión de eventos, en donde un evento atrapado localmente, es transformado en un evento exterior, el cual es lanzado en la línea 50.
Como ser observara las primeras acciones son las de recuperar los valores de la columnas de la fila seleccionada, para poderlos usar como parámetros del argumento del evento que se lanzara.
Es muy importante cuando se va a lanzar un evento validar que este tenga algún método adjunto, ya que de no tener ninguno y ejecutarse este producirá un fallo, la validación por distinto de null evita este problema.
User Control – Listado de TextBox
Este user control contendrá una serie de TextBox, destinado a la visualización de la información seleccionada en el user control que contiene el GridView
using System; using System.Collections.Generic; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace WebUserControlsCommunicated { public partial class ucTextView : System.Web.UI.UserControl { public int Id { get; set; } public string Nombre { get; set; } public string Cargo { get; set; } protected void Page_Load(object sender, EventArgs e) { } public void Refresh(int id, string nombre, string cargo) { this.Id = Id; this.Nombre = nombre; this.Cargo = cargo; this.Refresh(); } public void Refresh() { txtId.Text = Convert.ToString(this.Id); txtNombre.Text = this.Nombre; txtCargo.Text = this.Cargo; } } }
Este control tiene bastante menos lógica que implementar ya que su funcionalidad se reduce a recibir los valores y asignarlos a los TextBox que correspondan.
Como se observa cuenta con una serie de propiedades que representan cada atributo de la entidad.
Y un método Refresh() que será el encargado de realizar en concreto la asignación de cada propiedad con su respectivo control.
Integración – Default.aspx
Bien llego el momento de poner todo en conjunto a funcionar.
Para ello se hará uso del formulario web, Default.aspx, es allí donde se arrastrara cada user control en el diseñador de la pagina, haciendo uso de una tabla para dar algo de formato y ubicación.
using System; using System.Collections.Generic; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Data; namespace WebUserControlsCommunicated { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { ucGrid1.GridSelectorChanged += new ucGrid.GridSelectorCommandEventHandler(ucGrid1_GridSelectorChanged); if (!IsPostBack) { ucGrid1.DataSource = CargarTabla(); } } void ucGrid1_GridSelectorChanged(ucGrid.GridSelectorCommandEventArgs e) { ucTextView1.Id = e.Id; ucTextView1.Nombre = e.Nombre; ucTextView1.Cargo = e.Cargo; ucTextView1.Refresh(); } private DataTable CargarTabla() { DataTable dt = new DataTable(); dt.Columns.Add("Id"); dt.Columns.Add("Nombre"); dt.Columns.Add("Cargo"); DataRow row = dt.NewRow(); row["Id"] = 1; row["Nombre"] = "Andres"; row["Cargo"] = "Developer"; dt.Rows.Add(row); row = dt.NewRow(); row["Id"] = 2; row["Nombre"] = "Federico"; row["Cargo"] = "PM"; dt.Rows.Add(row); row = dt.NewRow(); row["Id"] = 3; row["Nombre"] = "Leonardo"; row["Cargo"] = "Developer"; dt.Rows.Add(row); return dt; } } }
En la línea 14 es donde se define la utilización del evento expuesto por el user control que contiene la grilla, la asignación del handler requiere de un método declarado en las líneas 22-29.
Es en este método ucGrid1_GridSelectorChanged donde se hará uso de los parámetro del argumento del evento GridSelectorCommandEventArgs, para especificar los datos al segundo user control.
Como se habrá notado en este caso se asignan las propiedades directamente al segundo user control, para llamar por ultimo el método Refresh() que realizara la asignación de estos valores dentro del control; pero también se podría haber utilizado para tal fin el método Refresh() sobrecargado con los valores de cada propiedad necesaria para desplegar la información.
[C#] |
¡Excelente ejemplo!
ResponderEliminarHabía experimentado antes con la comunicación de usercontrols por evento, pero solo a través de propiedades de la clase así que este ejemplo me viene muy bien.
Lo único que hay que explicar aqui es que este codigo es para la version 3.5 del framework de .net y si se quiere aplicar a 2.0 deben declararse las propiedades completas:
private int codigo;
public int Codigo
{
get
{
return codigo;
}
set
{
codigo = Codigo;
}
}
Si no se declaran sale el error " *propiedad* debe declarar un cuerpo porque no está marcada como abstract o extern"
¡¡Saludos!!
hola vi tu ejemplo pero quiero bajar el proy y me dice que no esta disponible.
ResponderEliminarhola Josengan
ResponderEliminardisculpa la demora, el proyecto de ejemplo ya esta actualizado
ahora deberias pdoer descargarlo
saludos
hola daniel
ResponderEliminarA nivel conceptual puedo decirte que si funciona, por supuesto este ejemplo esta planteado para un desarrollo web por lo tanto el codigo asi como esta no aplica a winforms.
Pero el uso del evento y propiedades es identico en winforms ya que no es un concepto propio de web, sino que es del .net de forma global.
Si en tus user control defines evento y propiedades puede aplicar la misma tecnica que ves en este articulo.
Es mas si los evento y propiedades las defines en tus controles igual que en los ejemplso tambien deberia funcionar.
saludos
Buenas Leandro estoy probando tu ejemplo pero en VB, pero tengo problemas con esta parte en el evento SelectedIndexChanged de gridview :
ResponderEliminarif (GridSelectorChanged != null)
Porque si trato de hacer algo como esto :
If GridSelectorChanged IsNot Nothing Then
Obtengo este error :
'Public Event GridSelectorChanged(e As GridSelectorCommandEventArgs)' es un evento y no se puede llamar directamente. Utilice la instrucción RaiseEvent para provocar un evento.
y por el contenido de esa condicional allí si supongo que debo usar RaiseEvent:
RaiseEvent GridSelectorChanged(New GridSelectorCommandEventArgs(id, nombre, cargo))
Saludos
Sorry :( esta linea tampoco se como convertirla a VB y el resultado que arroja el conversor de C# a VB.NET es el mismo :(
ResponderEliminarucGrid1.GridSelectorChanged += new ucGrid.GridSelectorCommandEventHandler(ucGrid1_GridSelectorChanged);
hola Edalo
ResponderEliminarpero ese error no se produce porque has puesto mal la linea del if, como bien has comentado el
RaiseEvent
es necesario para alnzar un evento en vb.net
pero me quedo la duda aun con este menciona el error ?
prueba de descargar el IDE
SharpDevelop
con este en un click te convierte todo el proyecto de un lenguaje a otro, descarga la version 3.2, y abre la solucion con este para convertir
saludos
con respeto al
ResponderEliminarucGrid1.GridSelectorChanged += new ucGrid.GridSelectorCommandEventHandler(ucGrid1_GridS
podria ser
AddHandler ucGrid1.GridSelectorChanged, AddressOf ucGrid1_GridS
AddHandler (Instrucción)
saludos
wow ! ! Leandro el IDE esta super.
ResponderEliminarResulta que todo esto :
if (GridSelectorChanged != null)
GridSelectorChanged(new GridSelectorCommandEventArgs(Id, nombre, cargo));
es convertido a simplemente:
RaiseEvent GridSelectorChanged(New GridSelectorCommandEventArgs(id, nombre, cargo))
Sin ningún If y la manera de agregar el handler era ésta:
AddHandler ucGrid1.GridSelectorChanged, New ucGrid.GridSelectorCommandEventHandler(AddressOf ucGrid1_GridSelectorChanged)
Otra vez muchísimas gracias Leandro.
Saludos y hasta luego
hola
ResponderEliminarlo del if validalo, porque aunque sea vb.net sino hay nadie adjunto al evento este pdorias falla sino lo verificas
quizas debas hacerlo comparando contra Nothing, porque una cosa es la validacion y otra es el RaiseEvent
lo del evento esta correcto, pero se que con el AddressOf podrias reducir algo el codigo
saludos
Hola Leandro.
ResponderEliminarBueno como lo había mencionado antes, ya había intentado hacer algo así:
If GridSelectorChanged IsNot Nothing Then
RaiseEvent GridSelectorChanged(New GridSelectorCommandEventArgs(id, nombre, cargo))
End If
Pero obtenía este error y ya no supe como poder saber para validarlo:
Error 1 'Public Event GridSelectorChanged(e As GridSelectorCommandEventArgs)' es un evento y no se puede llamar directamente. Utilice la instrucción RaiseEvent para provocar un evento.
y con respecto a lo del evento yo también creo que :
AddHandler ucGrid1.GridSelectorChanged, AddressOf ucGrid1_GridSelectorChanged
Es mas fácil de usar,al menos para mi :( ,ademas que la otra forma (la que puse anteriormente)nunca la termine de entender :(
Saludos
Hola Leandro, Me pareció muy interesante el ejemplo.
ResponderEliminarAl tratar de implementarlo en mi maquina me esta dando null el GridSelectorChanged en las siguientes lineas:
if (GridSelectorChanged != null)
GridSelectorChanged(new GridSelectorCommandEventArgs(Id, nombre, cargo));
¿Tenes idea de porq pasa esto?
hola Patricia
ResponderEliminarHabras visto que en el user control se define un evento
public event GridSelectorCommandEventHandler GridSelectorChanged;
si desde la pagina no se adjuntas al mismo, es que nadie requiere la notificacion, por lo tanto estara en null y el evento no se lanzara
en la pagina Default.aspx veras la linea
ucGrid1.GridSelectorChanged += new ucGrid.GridSelectorCommandEventHandler(ucGrid1_GridSelectorChanged);
alli es donde se adjunta el evento, si tienes esa liena ya no tendras un null cuando se lance el evento desde dentro del control
saludos
Hola Leandro, me ha servido mucho tu artículo. Gracias.
ResponderEliminarUna pregunta. ¿Como usar un UpdatePanel para no refrescar toda página?
Gracias de nuevo
hola
ResponderEliminarbueno la pregunta es un poco general
Información general sobre el control UpdatePanel
quizas si analiza la doc sobre el uso del componente ayude a entender su uso
saludos
Cordial Saludo leandro, le escribo parfa comentarle la siguiente situacion:
ResponderEliminarEstoy desarrollando una ventana de mensajes con un user control todo esto en c#, y lo que ha sido el dolor de cabeza para mi es llamar un metodo que se encuentra en el control desde mi pagina aspx, no puedo hacer en mi pagina web aspx una instancia del user control por lo cual no puedo acceder a este metodo..
Lenadro he leído todo sus comentarios y su blog pero aun no encuentro nada que me ayude.
le agradezco si tienes la respuesta a la mano, que pueda facilitármela.
hola fercho
ResponderEliminaresta ventana de mensajes como la implementas? es un window.open() de javascript, usas jquery, o el modalpopup de ajax toolkit
porque segun sea el caso la implementacion de la llamada cambia
ademas esta pagina es la que contiene el control, o es la pagina padre quien debe comunicarse con la pagina hijo invocando el control
saludos
Hola Leandro
ResponderEliminarQuise agregar el Gridview dentro de un UpdatePanel pero cuando quiero hacer la devolución de la información de User Control al web Form no lo hace y si quito ese UpdatePanel si lo hace
Te ha pasado algo similiar?
Gracias de antemano.
hola Arenales
ResponderEliminarque implicaria esta "devolución" ?
el gridview esta dentro del user control? como comunicas ese user control con la paginas aspx, usas eventos ?
porque deberias crear un eventos custom para pasar datos del user control a la pagina
saludos
Que tal Leandro
ResponderEliminarUtilizó todo tu ejemplo incluido los eventos que nos muestras solo que el gridview (que esta en el usercontrol) lo estoy agregando dentro de un updatepanel.
El usercontrol lo necesito para que muestre un catalogo de materias lo cual quiero que al seleccionar una materia me regrese la clave de esa materia.
Saludos
hola Ray
ResponderEliminarUsing the ASP.NET UpdatePanel Control with User Controls
en principio no veo porque deberia haber problemas en usar un updatepanel dentro de un usercontrol, analiza el link a ver si da uan pista
saludos
Muchas Gracias Leandro... una pregunta no tienes este proyecto en vb 2010
ResponderEliminary que pase de cajas de texto a grilla
Saludos
hola Naaman
ResponderEliminaren este caso no lo publique en vb.net
pero podrias transformarlo si necesitas usando
http://www.icsharpcode.net/opensource/sd/
con este ide open source puede convertir proyecto de un lenguaje a otro con un simple click, por supuesto seguro hay que adaptar, pero la gran mayoria lo tendras en el lenguaje que necesitas
para agregar row al grid solo es cuestion de usar el
datagridview1.Rows.Add(New String() { Textbox1.Text})
DataGridView adding rows and columns in VB.NET
saludos
Hi,Entire user control are not to expensive and by going Web Design Cochin route you almost certainly be guaranteed that your template is unique.Thanks....
ResponderEliminarExelente ejemplo!
ResponderEliminarEn windows forms c# como seria.. yo tengo un texbox fuera del control de usuario quisiera enviar el dato del texbox a un label q esta dentro de un control de usuario como le haria en este caso agradeseria tu respueta gracias !!!
hola Jez
ResponderEliminarla tecnica es la misma, para pasar info al control deberias hacer que este exponga una propiedad o metodo publico el cual puedas invocar desde fuera asignando el valor que luego sera puesto al control label que esta dentro
si quieres notificar al form de alguna accion que se produce dentro del control entonces debes exponer evento
o sea ya sea web o winform las tecnicas son las mismas
saludos
Hola leandro me da mucho gusto saludarte y bueno quería comentarte que tu ejemplo me sirvió muchísimo, solo tengo una duda...
ResponderEliminarsi deseara en vez de utilizar la clase "GridSelectorCommandEventArgs"
dentro del "ucGrid" crear la clase aparte en un
GridSelectorCommandEventArgs.cs
con las propiedades y el constructor... todo exactamente igual.
Crees que se pueda implementar??
saludos y muchas gracias...
hola Jessica
ResponderEliminarclaro, puedes adaptar el codigo de esa forma moviendo la definicion de la clase a un nuevo .cs
como es un class puedes moverlo a un archivo separado
saludos
Leandro gracias por contestar =).
ResponderEliminarTe cuento que ya lo implemente en una clase aparte y funciona de maravilla.
Gracias por este ejemplo tan bueno seguiré mas de cerca información sobre delegados y eventos son realmente interesantes...
Hola Leandro
ResponderEliminarEspero me puedas ayudar, que sucede
si tengo un ControUser en un formulario y desde ese control Invoco a un Formulario que me va retornar informacion.
Como puedo enviar la informacion desde
ese formulario al ControlUser.
Gracias Saludos.
hola Dennis
ResponderEliminarinvocas al form desde el user control de que forma ? lanzas un popup con javascript o realizas un redirect
es que segun como navegas a esa otra pagina cambia como devuelves los datos
si usas javascript podrias usar el window.opener para enviarle informacion a la pagina que lanzo el popup si usas redirect entonces quiezas debas poner los datos en el Session de asp.net para tomarlo cuand vuelvas
saludos
Hola Leandro
ResponderEliminarcreo que no lo mencione pero estoy trabajando con Visual Basic, quisiera una recomendacion para ese caso .
Saludos
hola Dennis
ResponderEliminarque tipo de recomendacion buscas
porque podrias aplicar esta misma tecnica
Comunicar Formularios
la unica diferencia es que mostrarias el form desde un user control, pero el control de la vuelta lo harias desde alli mismo por lo tanto aplica perfectamente
en vb.net la tecnica es la misma, estos ejemplo no lo converti a vb.net pero tampoco es tan complejo usando una tool como ser
http://converter.telerik.com/
saludos
Hola Leandro
ResponderEliminarVoy a resumir lo que quiero hacer, tengo un formulario de Ingresos que se maneja segun el concepto que se elija, tengo un controluser para cada concepto, segun el concepto que se elija el formulario mostrara un control diferente.
Por ejemplo. en el controluser tengo un boton que me abre un formulario con el maestro persona, yo elijo la persona y tendria que retornar ID y Descripcion.
Estoy usando la comunicacion de formularios de forma desacoplada, ahora dime desde donde yo implemento la interfaz desde el formulario ingreso o desde el controluser?, lo hice desde el controluser y me retorna un error, y si lo implemento en el formulario tendria que en donde implemento la interfaz llamar a un metodo publico del controluser en donde muestre los datos, solo asi podria salir.
Dime esa forma esta bien? o ahi alguna otra forma de pasar los datos directamente del form hijo al control user.
Saludos.
hola Dennis
ResponderEliminarpero deberias poder implementarlo desde el user control, que error estas obteniendo?
si el user control implementa la interfaz y esa instancia la pasas por parametro en el constructor la vuelta deberia invocarse al metodos de la interfaz del objeto que hayas pasado en la instancia
saludos
Hola Leandro
ResponderEliminarEsto en el Control :
Dim socio As New frmMaestroSocio
socio.ShowDialog(Me)
En el Form MaestroSocio :
Dim EnviarInformacion As IObtenerSocio = CType(Me.Owner, IObtenerSocio)
If IsDBNull(EnviarInformacion) Then
Else
Dim t As DataGridViewRow
t = DataGridView1.CurrentRow
EnviarInformacion.ObtenerInformacion(t.Cells("descripcion").Value, t.Cells("id").Value)
Me.Close()
End If
Este es el error :
No se puede convertir un objeto de tipo 'PROYECTO.frmIngreso' al tipo 'PROYECTO.IObtenerSocio'.
Saludos
hola Dennis
ResponderEliminarpero el form frmMaestroSocio esta implementando la interfaz IObtenerSocio ?
porque sino la implementa no va a poder convertir
ademas para que haces esto
If IsDBNull(EnviarInformacion) Then
castear de tipo nunca va a es un dbnull, esto se usa para la base de datos
saludos
Hola Leandro
ResponderEliminarPero el frmMaestroSocio es el que manda la informacion, desde el usercontrol abro el frmMaestroSocio.
saludos
hola Dennis
ResponderEliminarpero entonces no te sirve usar un owner porque no es este quien implementa la interfaz
tienes que usar el constructor
Comunicar Formularios
para poder pasarle la instancia del user control que sera quien implemente a itnerfaz
saludos
Hola Leandro
ResponderEliminara cual te refieres:
-Usando Interfaces o
-Usando eventos
saludos
hola Dennis
ResponderEliminares indistinto puedes usar la tecnica que veas mejor se adapte a lo que quieres lograr
quizas para seguir en la misma linea podrias usar el de la interfaz
saludos
Hola Leandro
ResponderEliminarDe maravilla, gracias
saludos
Que tal Leandro.
ResponderEliminarMuchas gracias por compartir tu conocimiento.
Te comento hace algun tiempo integre tu codigo a un proyecto que he hecho.
Te explico lo que hago es mostrar mis controles de usuario en un modal extender (ajaxcontroltoolkit).
Me he dado cuenta que mi pagina se alenta a la hora de mostrar y ocultar mi modal, mi pagina se tarda mucho en cargar los datos y mostrarlos, ya sea en el web form o a la hora de cargar los datos en en control de usuario.
No se a que se deba esto, te agradeceria si me puedes dar alguna sugerencia o como poder identificar en que parte de mi codigo causa que mi pagina responda tan lento.
Saludos y gracias
hola Gerardo
ResponderEliminarrecuerda que el popup en realidad esta dentro de la misma pagina por lo que este tambien se carga cuando la pagina se muestra
si la pagina y los popup contienen mucha informacion o controles pesados como ser el GridView o similar es logico que se demore
quizas debas rediseñar la pagina para mostrar menos informacion
saludos
Tengo una pequena Duda amigo, he visto que algunas web pueden abrir aplicaciones y pasar parametros usando < a href="Appl:Nombre:daniel| Apellido:juan", mi pregunta es como puedo crear un app de windowsform para que coja los parametros enviados atravez del link, uso Visual Studio 2013
ResponderEliminarExcelente, muchas gracias Leandro como siempre de mucho apoyo.
ResponderEliminarSin ánimo de ofender y con intención solo constructiva, en mi humilde opinión creo que sería bueno que corrigiese en su entrada esto:
ResponderEliminarEsto no es correcto:
para facilitar la mantenibilidad del desarrollo
lo correcto sería:
para facilitar el mantenimiento del desarrollo
Hola Leandro, muy buen ejemplo. Gracias por tu aporte. 2 preguntas: puedo tener varias instancias del control en la misma pagina?. El datatable se puede poner dentro del control?
ResponderEliminar
ResponderEliminarEsto esta sobrando en ucTextView.cs:
//public void Refresh(int myId, string nombre, string cargo)
//{
// this.myId = myId;
// this.Nombre = nombre;
// this.Cargo = cargo;
// this.Refresh();
//}