¿Herencia Visual en WebForms y Web User Controls?

Por Elmer M. Carías Contreras

downlanim.gif Descargar ejemplos de este artículo (82 KB).

En este artículo se muestra brevemente cómo podemos heredar Web User Controls en .NET. Fue escrito gracias a la colaboración de un compañero y amigo: Mario Rene Mayén, Consultor en Informática.

Contenido

 Introducción
 Definiendo el Control Padre
 Definiendo el Control Hijo
 Object reference not set to an instance of an object
 Conclusión

Introducción

Si alguna vez te has hecho la siguiente pregunta: "Si .NET es orientado a objetos ¿Por qué no me permite realizar herencia visual de páginas (ASPX) y/o controles de usuario (ASCX) de manera automática, al igual que los Windows Forms?", no puedes dejar de leer este artículo.

Debido a esa inquietud es que surgió la idea de hacer este artículo, ya que existe una forma de habilitar la herencia visual de páginas en .NET, pero se requiere de una serie de pasos para poder lograrlo. Para este artículo se utiliza como ejemplo la creación de un control de usuario que simula una barra de herramientas. Lo que se indica en este artículo y el ejemplo aplican lo mismo para una página Web. El ejemplo que aquí se detalla está hecho con Visual Basic, pero en los archivos de ejemplo de este artículo encontrarás código en C#.

 

Definiendo el Control Padre

Para este ejemplo crearemos una barra de herramientas inicial, que incluya las opciones: "Inicio" y "Ayuda", tal como se muestra en la Figura 1:

Bb972234.art230-img01-354x52(es-es,MSDN.10).jpg
Figura 1. Volver al texto.

Para esto se utiliza una tabla HTML y dos controles ImageButton, a los cuales llamaremos ibtnInicio y ibtnAyuda , respectivamente. Agregamos un nuevo Web User Control con el nombre barraPrincipal.ascx y le damos "Aceptar" (ver Figura 2):

Bb972234.art230-img02-531x392(es-es,MSDN.10).jpg
Figura 2. Volver al texto.

Le agregamos los dos botones que necesitamos. El código Visual Basic respectivo es el siguiente:

Public Class barraPrincipal 
    Inherits System.Web.UI.UserControl 
 
#Region " Web Form Designer Generated Code " 
 
    'This call is required by the Web Form Designer. 
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() 
 
    End Sub 
    Protected WithEvents ibtnInicio As System.Web.UI.WebControls.ImageButton 
    Protected WithEvents ibtnAyuda As System.Web.UI.WebControls.ImageButton 
 
    'NOTE: The following placeholder declaration is required by the Web Form Designer. 
    'Do not delete or move it. 
    Private designerPlaceholderDeclaration As System.Object 
 
    Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init 
        'CODEGEN: This method call is required by the Web Form Designer 
        'Do not modify it using the code editor. 
        InitializeComponent() 
    End Sub 
 
#End Region 
 
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
        'Put user code to initialize the page here 
    End Sub 
End Class 

 

Definiendo el Control Hijo

Para definir el Control Hijo es necesario crear un nuevo Control de Usuario al cual le incluiremos 4 botones adicionales: "Agregar", "Editar/Cancelar", "Almacenar" y "Eliminar", y se verá como se muestra en la Figura 3:

Bb972234.art230-img03-351x52(es-es,MSDN.10).jpg
Figura 3. Volver al texto.

Agregamos el Control de Usuario con el nombre barraMantenimiento.ascx y le damos OK (Ver Figura 4):

Bb972234.art230-img04-396x292(es-es,MSDN.10).jpg
Figura 4. Volver al texto.

Inicialmente debemos decirle al Control de Usuario barraMantenimiento que herede de la clase barraPrincipal, lo que se hace de la siguiente manera:

Public Class barraMantenimiento 
    Inherits barraPrincipal

Se ha cambiado la herencia sobre System.Web.UI.UserControl y se le ha colocado la herencia hacia barraPrincipal, que es la clase del control de usuario que creamos inicialmente.

Posteriormente, para hacer la herencia del HTML no es automático, ya que con el cambio de objeto Padre cambiamos la clase a la cual hace referencia pero no el HTML generado del mismo; esta tarea se debe realizar manualmente copiando todo el HTML que contiene el objeto, para esto abrimos el control barraPrincipal en modo diseño y vemos el HTML, lo copiamos y pegamos en el HTML del objeto que estamos heredando.

El HTML que se verá al copiar será el siguiente:

< TABLE id ="TableBarra" class ="EstiloBarra"> 
      < TR height ="40"> 
            < TD > 
                  < asp:ImageButton id ="ibtnInicio" CssClass ="outButton" runat ="server" ImageUrl
 ="imagenes/Inicio.gif" 
                        ToolTip ="Ir al Inicio"></ asp:ImageButton > 
                  < asp:ImageButton id ="ibtnAyuda" runat ="server" ImageUrl ="imagenes/Ayuda.gif" ToolTip
 ="Ayuda"></ asp:ImageButton > 
            </ TD > 
      </ TR > 
</ TABLE > 

Ahora bien, el truco es copiar solamente el HTML, no el encabezado del Control, y además guardar la página estando en Vista HTML; más adelante explicare porqué es necesario esto.

Si ingresamos al código del control barraMantenimiento, notaremos que ya tenemos acceso a los botones de la siguiente manera (Ver Figura 5):

Bb972234.art230-img05-474x216(es-es,MSDN.10).jpg
Figura 5. Volver al texto.

Lo que muestra la Figura 5 se puede realizar incluso antes de copiar el HTML, esto sucede porque como hemos heredado todos los controles ya creados en el control barraPrincipal, tenemos acceso a ellos.

Después agregamos los controles que necesitamos e incluso podríamos dar nuevas ubicaciones a los controles que heredamos; en el ejemplo agregaremos 4 controles ImageButton pero los colocaremos entre los dos botones que heredamos. Agregamos 4 botones: "Agregar", "Editar/Cancelar", "Almacenar" y "Eliminar"; con los nombres ibtnAgregar, ibtnEditarCancelar, ibtnAlmacenar, ibtnEliminar, respectivamente.

Al final veremos en la Vista Diseño lo que se muestra en la Figura 6:

Bb972234.art230-img06-214x55(es-es,MSDN.10).jpg
Figura 6. Volver al texto.

Ahora, al guardar los cambios al HTML, si compilamos el proyecto el mismo nos dará dos advertencias:

------ Rebuild All started: Project: herenciaVisualVB, Configuration: Debug .NET ------
Preparing resources...
Updating references...
Performing main compilation...
c:\inetpub\wwwroot\herenciaVisual\barraMantenimiento.ascx.vb(15) : warning BC40004: WithEvents 'ibtnInicio'
 conflicts with WithEvents 'ibtnInicio' in the base class 'barraPrincipal' and so should be declared 'Shadows'.
c:\inetpub\wwwroot\herenciaVisual\barraMantenimiento.ascx.vb(16) : warning BC40004: WithEvents 'ibtnAyuda'
 conflicts with WithEvents 'ibtnAyuda' in the base class 'barraPrincipal' and so should be declared 'Shadows'.
Building satellite assemblies...
---------------------- Done ----------------------
Rebuild All: 1 succeeded, 0 failed, 0 skipped

Esto es porque al crear los nuevos botones en el control de mantenimiento y darle guardar el editor de .NET nos crea en el código una referencia a los botones que copiamos desde HTML, esto crea un conflicto con las definiciones ya existentes en la clase padre.

Pero como son advertencias y corremos la aplicación, obtendremos el siguiente error:

 

Object reference not set to an instance of an object

Esto significa que aunque haya creado el assembly, no podemos ejecutar satisfactoriamente nuestra página; para ello debemos ingresar al código de nuestro control barraMantenimiento y notaremos lo siguiente en la región de código generado por el editor (Ver la Figura 7):

Bb972234.art230-img07-480x334(es-es,MSDN.10).jpg
Figura 7. Volver al texto.

En la Figura 7 observamos que el editor de .NET nos subraya los controles que son de la clase padre, y el error que da es que hay conflicto con objetos de la clase padre.

Esto se resuelve simplemente eliminando la declaración de estos objetos de la clase hijo. Guardamos nuestro proyecto y compilamos. Al compilar .NET no genera ningún error y podemos ejecutar satisfactoriamente nuestra página utilizando el control hijo.

 

Conclusión

Es necesario tener en cuenta las siguientes recomendaciones:

  1. Si un control de usuario se puede abstraer y crear uno que sea mas genérico, eso nos indica que podemos hacer una clase padre.

  2. Si modificamos únicamente el código de la clase padre, ésta se propagará a los hijos.

  3. Si modificamos el HTML de la clase padre agregando objetos, es necesario actualizar manualmente el HTML en todos los controles hijos.

  4. Si queremos acceder a los eventos de los controles de la clase hijo y utilizamos el editor de Visual Studio .NET, es necesario tener cuidado porque al abrir la Vista de Diseño, el editor crea automáticamente una declaración de los objetos de la clase padre. Es recomendable abrir directamente la Vista de Código directamente para no tener este inconveniente.

Elmer M. Carías Contreras es Ingeniero en Sistemas. Trabaja como Arquitecto de Software en el marco de un proyecto del Gobierno de El Salvador, en FISDL (Fondo de Inversión Social para el Desarrollo Local). Posee 2 años de experiencia con aplicaciones en capas con .net y con PowerBuilder-JSP-Jaguar y ha trabajado con las tecnologías de desarrollo Microsoft desde 2002. Ha dado charlas técnicas por parte de Microsoft El Salvador en áreas tales como Office, Exchange, Windows y .net.