ASP.NET provee de elementos muy interesantes para construir un sitio web. Sin embargo también hay algunas dificultades que tendremos que sortear tarde o temprano.
Una de estas dificultades consiste en que los controles que ponemos dentro de un control de usuario, no pueden ser accesados tan fácilmente como los que están colocados a nivel de página.
Por ejemplo, si tenemos un control de usuario sencillo como el siguiente:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.cs" Inherits="WSMyLittleStore.WebUserControl1" %> <asp:Label ID="Label1" runat="server" Text="Nombre"></asp:Label> <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> <br /> <asp:Label ID="Label2" runat="server" Text="Apellido"></asp:Label> <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
Y un formulario igualmente sencillo como:
<form id="form1" runat="server"> <div> <uc1:WebUserControl1 ID="WebUserControl11" runat="server" /> <br /> <asp:Label ID="Label1" runat="server" Text="Edad"></asp:Label> <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> </div> </form>
No podremos hacer lo siguiente en el code-behind de la página:
protected void Page_Load(object sender, EventArgs e)
{
TextBox1.Text = "TextBox en el form"; //Correcto!
WebUserControl1.TextBox1.Text = "TextBox en el UserControl"; //Error!
WebUserControl1.TextBox2.Text = "TextBox en el UserControl"; //Error!
}
Esto se debe a que los controles dentro del UserControl están declarados con el atributo protected.
He utilizado tres maneras de sortear este problema, cada una con sus ventajas e inconvenientes:
1) Crear una propiedad pública dentro del UserControl
En el code-behind del UserControl haríamos lo siguiente:
public partial class WebUserControl1 : System.Web.UI.UserControl
{
public TextBox Texto1
{
get { return TextBox1; }
set { TextBox1 = Texto1; }
}
public TextBox Texto2
{
get { return TextBox2; }
set { TextBox1 = Texto2; }
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
Ahora en el code-behind del form podemos utilizar Texto1 y Texto2 para referirnos a las hasta ahora inaccesibles cajas de texto del UserControl:
protected void Page_Load(object sender, EventArgs e)
{
TextBox1.Text = "TextBox en el form"; //Correcto!
WebUserControl1.Texto1.Text = "TextBox en el UserControl"; //Correcto!!
WebUserControl1.Texto2.Text = "TextBox en el UserControl"; //Correcto!!
}
Esta es la opción más recomendada por la mayoría. Para mi es totalmente impráctica cuando el UserControl cuenta con muchos controles que deseamos exponer. Imagínense 40 declaraciones de gets y sets. Simplemente, me parece una pérdida de tiempo y una carga negativa para la mantenibilidad de la aplicación. Sin embargo puede ser útil para utilizarla con unos pocos controles (hasta 3, diría yo).
2) Utilizar FindControl en el code-behind de la página
En este caso nos olvidamos de los gets y sets y trabajamos directamente con el code-behind del form:
protected void Page_Load(object sender, EventArgs e)
{
TextBox Texto1 = (TextBox)WebUserControl1.FindControl("TextBox1");
TextBox Texto2 = (TextBox)WebUserControl1.FindControl("TextBox2");
TextBox1.Text = "TextBox en el form"; //Correcto!
Texto1.Text = "TextBox en el UserControl"; //Correcto!
Texto2.Text = "TextBox en el UserControl"; //Correcto!
}
Sé que esta alternativa va a resultar tentadora para más de uno. Pero hay que tener en cuenta 2 inconvenientes:
Primero, que estamos introduciendo “código duro” en nuestro form, obligándonos a mantener a mano la coincidencia de los nombres de los controles con el string que utilizamos en cada método FindControl.
Segundo y más importante aún, que cualquier error en los nombres no será detectado por el compilador sino que causará errores de ejecución o de lógica en nuestro formulario.
3) Cambiar los atributos a public
En este caso nos fijaremos que nuestro UserControl se compone de 3 ficheros:
- UserControl.ascx
- UserControl.ascx.cs
- UserControl.ascx.designer.cs
Nosotros abriremos el archivo UserControl.ascx.designer.cs y veremos que ahí se encuentran declarados nuestros controles con el atributo protected. Veremos también que sobre cada declaración hay un mensaje que dice:
/// Auto-generated field. /// To modify move field declaration from designer file to code-behind file.
Y bueno, pues podemos hacer caso de eso y cortar la declaración del control para luego pegarla en el code-behind. Una vez en el code-behind, cambiaremos la palabra protected por public y ¡voila!, ya tendremos acceso al control desde el form.
El code-behind del UserControl debería quedar como sigue:
public partial class WebUserControl1 : System.Web.UI.UserControl
{
public global::System.Web.UI.WebControls.TextBox TextBox1;
public global::System.Web.UI.WebControls.TextBox TextBox2;
protected void Page_Load(object sender, EventArgs e)
{
}
}
Ahora en el code-behind de nuestro form podemos escribir:
protected void Page_Load(object sender, EventArgs e)
{
TextBox1.Text = "TextBox en el form"; //Correcto!
WebUserControl1.TextBox1.Text = "TextBox en el UserControl"; //Correcto!
WebUserControl1.TextBox2.Text = "TextBox en el UserControl"; //Correcto!
}
El principal inconveniente de esta solución es que atenta contra una de las leyes de la POO: que los atributos de una clase no deben ser accesados directamente, sino a través de métodos o propiedades. Pero es que las otras soluciones tampoco parecen sacadas de un libro de “buenas prácticas” y me parece que Microsoft nos ha dejado un poco huérfanos con este problema.
Para finalizar, creo que ninguna de las opciones es óptima. Cada una servirá para situaciones particulares y ya depende de cada uno considerar sus ventajas y desventajas. Si alguien conoce otra forma, ¡por favor compartirla con los que leen este blog!
Por lo pronto, yo pienso que Microsoft debería ofrecer una mejor alternativa en las futuras revisiones del UserControl. Por decir algo: que la accesibilidad de cada control pueda ser establecida por medio de una propiedad en tiempo de diseño.
¿Qué opinan ustedes?
¡Hasta pronto!
Josh
abril 30, 2010
Estan muy buenos los consejos de tu blog pero al final dices que seria bueno poder cambiar la accesibilidad y creo que esto tambien va contra las leyes de la POO
Es solo una pequeña observasion. felicidades por el blog
zameb
abril 30, 2010
Hola Josh, gracias por los comentarios! Tienes razon en cuanto la accesibilidad, aunque yo me refería únicamente a la ventana de diseño. Al cambiar una supuesta propiedad de accesibilidad a Public, lo deseable sería que el code-behind implemente los get y sets necesarios. De esta manera se respeta la POO y a la vez se logra que el desarrollador no se preocupe por esos detalles.
Saludos!
Deimer Caldera
julio 29, 2010
Yo visualizo un problema mas complejo de resolver y es el siguiente: Como acceder a los eventos de los controles del user control!?
Por ejemplo un datagridview. la idea es crear el control para reutilizarlo y no solo en una aplicacion sino en todas las que se necesite, y en este caso, la funcionalidad de cada evento varia de aplicacion en aplicacion, y como estan diseñados los user control realmente es complejo poder aplicar este concepto.
zameb
julio 30, 2010
Hola Deimer, gracias por comentar en mi blog. Espero poder resolver tu duda a continuación…
En el caso que tu propones, yo suelo declarar los eventos de la siguiente manera:
public partial class GridCustomControl : UserControl
{
public GridCustomControl()
{
InitializeComponent();
}
//UserControl Events
public event EventHandler ShowSearchUserClicked;
//Demás código del UserControl...
}
De esta manera es posible acceder a los eventos del GridCustomControl desde el formulario en que lo utilices.
Ahora supongamos que tienes un nuevo Form y a este le has agregado tu UserControl y le has puesto MyControl como nombre. En el evento Load del formulario puedes hacer lo siguiente:
MyControl.ShowSearchUserClicked +=
new EventHandler(MyControl_ShowSearchUserClicked);
Y luego puedes definir el código del evento de la siguiente manera:
void MyControl_ShowSearchUserClicked(object sender, EventArgs e)
{
//Código del evento aqui
}
Espero te sea de utilidad, de lo contrario podemos seguir buscándole alguna solución.
Un saludo
zameb
agosto 2, 2010
Algo más… cuando ya tienes los controles expuestos (de alguna de las 3 formas que he señalado en el artículo), también puedes acceder a los métodos dentro de los controles del UserControl.
Para hacerlo
Control.ButtonClick +=
new EventHandler(Control_ButtonClick);
Y ya puedes poner el código del evento de la siguiente manera:
void Control_ButtonClick(object sender, EventArgs e)
{
//Código del evento aqui
}
Ten en cuenta que sólo necesitas escribir:
Control.ButtonClick +=
Luego presionas la tecla tab 2 veces seguidas y el editor te genera el resto de código que necesitas para empezar a trabajar.
Un saludo
Ascarizz
diciembre 26, 2011
Muy Bueno el comentario.
Pero yo tengo lo siquiente.
Quiero acceder a un TabContainer que esta dentro de un master.page y dentro hay un contro que es MenuGestor.asxc como puedo acceder al TabContainer si yo estoy dentro de un aspx???
gracias
zameb
febrero 22, 2012
Hola Ascarizz, disculpa la tardanza en responder.
1) Mira el código behind de tu Master Page y observa cual es el nombre de la clase.
Por ejemplo:
public partial class MainLayout : System.Web.UI.MasterPage
En este caso, la clase de la master page se llama MainLayout.
2) Dentro del aspx tienes una propiedad llamada Master. Tienes que hacer un casting con la clase del paso 1. Por ejemplo:
var MiMaster = (MainLayout )this.Master;
3) Ahora la variable MiMaster tiene todas las propiedades de tu MasterPage. A partir de aqui, puedes utilizar alguna de las formas explicadas en el post para acceder a tu userControl.
4) Una vez que hayas utilizado cualquiera de las formas explicadas para publicar tu control TabContainer, puedes tener acceso de la siguiente forma:
MiMaster.TabContainer
Si tienes alguna duda o problema, me cuentas
uyeapio
febrero 3, 2012
Que tal buen dia, esta bastante interesante tu post, ahora bien si quisieramos acceder a las propiedas de un control, por ejemplo:
webusercontro1.Textbox.Enabled= false;
como hariamos si queremos tener acceso a las propiedades de los mismos, es posible?
zameb
febrero 22, 2012
Si, es totalmente posible con cualquiera de las 3 formas señaladas. Tienes acceso no solo al control, sino tambien a todas sus propiedades
Un saludo