Manual Vb Net

196
1 Curso de Titulación en Sistemas con Visual Basic .Net 2003 Marzo 2004 Alejandro Guzmán Zazueta [email protected]

Transcript of Manual Vb Net

1

Curso de Titulación en Sistemas con Visual Basic .Net 2003

Marzo 2004 Alejandro Guzmán Zazueta [email protected]

2

Programación con Visual Basic .NET

El objetivo del curso es proveer a los desarrolladores de software los conocimientos necesarios para crear aplicaciones en Web y Windows, usando la plataforma .Net. Al terminar el curso, el participante estará capacitado para:

o Listar los elementos del Framework y describir los elementos de la nueva versión de Visual Basic.

o Describir y usar las nuevas estructuras y características del lenguaje Visual Basic .Net.

o Explicar y usar los conceptos de programación orientada a objetos en Visual Basic .Net

o Crear aplicaciones usando Microsoft Windows Forms. o Crear aplicaciones que usen Web Forms. o Crear aplicaciones usando ADO.Net. o Crear aplicaciones Multitarea. o Utilización de reportes con CrystalReport para aplicaciones Windows y

Web. o ASP .Net.

Contenido Modulo 1: Introducción a la Plataforma de Microsoft .Net

Objetivos de la infraestructura .Net El Common Language RunTime o CLR Lenguaje Intermedio de Microsoft MSIL Ensamblados Dominios de aplicación Recolector de basura Tipos de Proyectos Disponibles Analizando la Estructura de Proyectos El nuevo entorno Integrado Que son los Ensamblados? Activando Referencias al Proyecto Los Espacios de Nombres (Name Spaces) Activando las Propiedades del Proyecto Usando el Explorador de Soluciones Usando el Examinador de Objetos Usando la Lista de Tareas Ayuda Dinámica Depurando Aplicaciones

3

Módulo 2: Adiciones en la Sintaxis y el Lenguaje

Tipos de Dato como Estructuras de Clase Estructura Común de Tipos Cambios a los Tipos de Dato Existentes Conversiones Estrictas Conversiones de Enlace Tardío Declarando e Inicializando Variables y Arreglos Alcance de Variables Procedimiento o Función Operadores de Asignación Llamada a Funciones y Procedimientos Utilización de Errores y Excepciones

Módulo 3: Programación Orientado a Objetos para Visual Basic .Net

Introducción Módulos de Clase Definiendo Propiedades Especificando la Visibilidad a Variables y Procedimientos Polimorfismo en Propiedades Implementación de Métodos Métodos Compartidos Polimorfismo en Métodos Usando Constructores Constructores Sobrecargados Usando Destructores Colección Garbage Herencia Conceptos Utilizados en la Orientación a Objetos Sobrescribir y Sobrecargado Usando la palabra MyBase Usando la Palabra MyClass Implementando Interfaces Implementación de Eventos

Módulo 4: Uso de Windows Forms

¿Por qué usar Windows Forms? Usando Windows Forms Usando los Métodos de la Forma Creando Formas MDI (Multiple-Document Interface) Creando Formas MDIChild (hijas) Acceder a las Formas Hijas desde la MDI Usando Cajas de Dialogo Estándar Creación de Menús Herencia en Windows Forms

4

Módulo 5: ADO .NET Por que Ado .Net Las Clases de ADO .NET Proveedores de Objetos Conectados Usando el Objeto Command Usando el Objeto DataReader Ejecución de Múltiples Consultas Proveedores de Objetos Desconectados Múltiples Consultas Bajo un DataSet Usando Relaciones dentro de DataSet Acceder Datos Relacionados Usando Restricciones(Constraints) Afectando Datos en un DataSet Actualizando la Información del Dataset al Origen de Datos Enlace de Datos

Módulo 6: Utilización de Reportes en Visual Basic Empleando informes en ambiente Windows Exhibiendo el informe Creando un informe que utilice ADO.NET Código de Barras Informes en Aplicaciones para la Web

Módulo 7. Hilos Threads Crear y usar subprocesos

Prioridades de los subprocesos Subprocesos de primer plano y de fondo Problemas por el Uso de Múltiples Hilos Sincronización mediante Synclock

Módulo 8. ASP .NET

Introducción ASP .NET Estructura Web Form Comparación entre ampliaciones Windows y Web. Eventos en Formularios Web Propiedades, métodos y eventos de paginas Web. Controles en los Formularios Web Web Form con ADO .Net Controles Vinculables a Datos Funciones de ASP .Net Variables en ASP.Net Datos Desconectados (DataSet) Creación de Controles Dinámicamente

5

Modulo 1: Vistazo a la Plataforma de Microsoft .Net Objetivos de la infraestructura .Net Microsoft a tenido que implementar un nuevo conjunto de tecnologías, las cuales remplazan y/o mejoran defectos de su plataforma anterior. A continuación se resumen los objetivos principales que intenta solucionar la nueva infraestructura .Net:

1. Eliminar de raíz los inconvenientes de modelo COM. Para ello, emplea un nuevo estándar con una nueva aproximación basada en ensamblados(se les llama ensamblados a los nuevos componentes y aplicaciones factibles de generar desde cualquier lenguaje de Visual Studio .Net y productos compatibles con la infraestructura) y un almacén o caché global de los mismos.

2. Proveer manejo automático de la memoria empleada por los componentes, sin que el desarrollador tenga que ocuparse de esta tarea. Esto es cierto incluso para aplicaciones implementadas en Visual C++.

3. Ofrecer una mayor sencillez para utilizar componentes desarrollados en otros lenguajes, e incluso residentes en otros sistemas operativos.

4. Hacer más fácil la instalación, a los efectos de que sea posible dejar un componente funcional simplemente copiándolo al directorio de la aplicación.

5. Proveer el mismo modelo para utilizar un componente localmente o a través de una red.

6. Hacer más fácil el desarrollo de aplicaciones distribuidas, y especialmente de soluciones para internet mediante un único entorno de desarrollo.

7. Proveer ejecución segura a través de un nuevo modelo de seguridad para aplicaciones y componentes (semejante a los servlets de Sun Microsystems).

8. Hacer más fácil el desarrollo mediante un conjunto de funcionalidades previamente hechas, las cuales cubran varios aspectos comúnmente utilizados.

9. Proveer características de orientación a objetos para todos los lenguajes de la infraestructura en forma nativa.

Todos estos puntos se resuelven a través de un conjunto de servicios y características que han sido agrupadas bajo el nombre .NET. Ellas involucran nuevas bibliotecas, formatos y conceptos, de los cuales aprenderemos más a lo largo del presente manual.

6

Common Language RunTime o CLR Todos los lenguajes de .NET utilizan un único Runtime para su ejecución. Adicionalmente, se centralizan todas las funciones de forma común para los lenguajes. Por ejemplo, abrir un archivo desde visual se efectúa en forma similar que desde otros lenguajes de la plataforma .NET (por supuesto dependiendo de su sintaxis). A esta característica se le denomina CLR, y es la responsable del cambio de sintaxis de Visual Basic y demás integrantes del Visual Studio .NET. CLR asegura también que todos los lenguajes tendrán soporte para orientación a objetos, como también que no necesitarán preocuparse por el manejo de memoria, ya que ahora se cuenta con un colector de desechos o basura, el cual se encarga de esta tarea por nosotros. Ya no importa si olvidó asignar la palabra nothing , o no se hizo en forma correcta, ya que esta característica se encargará de liberar la referencia cuando no esté siendo utilizada. Por otra parte, CLR emplea un nuevo tipo de bibliotecas y ejecutables llamados Ensamblados, que resuelven los problemas de los DLL. Esta integración aseguran también un sistema común de tipos de dato, por lo que utilizar un componente realizado en otro lenguaje es realmente sencillo, ya que las estructuras son siempre las mismas. Microsoft Intermediate Language MSIL Cuando se compila una aplicación en VB .Net (o cualquier lenguaje compatible con la plataforma .Net), el resultado no es código máquina, sino que es un metalenguaje denominado Lenguaje Intermedio de Microsoft o MSIL (Microsoft Intermediate Language). MSIL es un formato binario que contiene instrucciones de bajo nivel que pueden ser solamente entendidas por el compilador incluido .NET llamado JIT (Just In Time compiler). Cuando se ejecuta una aplicación de este tipo, JIT verifica que el código MSIL sea completamente seguro, esto quiere decir que no realice conversión de punteros de forma ilegal, o todo aquello que pueda ocasionar un error de protección general (fallo de memoria) o acceso a recursos no autorizados, y a continuación lo transforma en código máquina teniendo en cuenta el sistema operativo y procesador y lo ejecuta. De esta forma se asegura un buen rendimiento, ya que el código final seguirá siendo siempre código máquina y no interpretado. Visual Basic .Net sólo puede generar este tipo de compilados, y de hecho se ejecutan en forma similar a otras versiones, aunque en realidad existan todos estos pasos intermedios. A toda aplicación o biblioteca generada en MSIL se le denomina código administrado, debido a que se puede confiar en que el mismo realizará las tareas en forma segura. Base Class Library, BCL La plataforma .Net ofrece aproximadamente 3000 clases ya compiladas como ensamblados, las cuales forman parte de la infraestructura, y pueden ser utilizadas libremente por el desarrollador. Para ello, simplemente basta con crear una instancia de la clase deseada, y posteriormente hacer usos de sus métodos y propiedades, a su vez, gran parte de las funciones del lenguaje emplean

7

directamente las mismas, aunque se muestren dentro de éste como un conjunto de instrucciones estándares. Por otra parte, el sistema común de tipos también se sitúa dentro de la misma, al igual que todos aquellos enumerados o constantes factibles de ser empleadas, lo que hace fácil el compartir conocimiento entre programadores. Ensamblados Todo ensamblado es una biblioteca DLL o EXE que contiene MSIL, y que requiere las características de CLR para ser ejecutadas. Los ensamblados son la nueva unidad de módulo ejecutable de .Net, la cual hace posible resolver los problemas de instalación y coexistencia entre varias versiones de bibliotecas. A simple vista, no existen diferencias entre componente del modelo COM y un ensamblado, y , de hecho, ambos terminan ejecutándose de forma similar desde el punto de vista del usuario. Sin embargo, estos últimos almacenan dentro de sí mismos la descripción de tipos (métodos, propiedades, etc.) y demás datos, lo cual elimina totalmente la necesidad de emplear el archivo de registro como repositorio de información. Decimos entonces que los ensamblados son unidades auto descriptivas o auto contenidas ejecutables, que no dependen del archivo de registro, ni necesitan de éste para su correcta ejecución. Los ensamblados contienen a grandes rasgos dos secciones, una con la implementación y otra con la información que contiene el mismo. A esta ultima se le llama manifiesto, e incluye entre otras el nombre programático del ensamblado, versión, otros ensamblados de los cuales éste depende, sus métodos y propiedades, información de seguridad, etc.

Manifiesto

Meta-datos (Funciones y

ti )

MSIL Lenguaje

Intermedio Microsoft

Biblioteca1.dll

Ensamblado1

8

Los ensamblados ofrecen tres características esenciales, las cuales no están disponibles en código no administrado (el que se construía en versiones anteriores):

1. Facilidad de Instalación.

2. Componentes privados.

3. Nuevo modelo para componentes compartidos.

Dominios de Aplicación Cuando un ensamblado EXE o DLL es ejecutado en la plataforma .NET, el mismo es cargado por CLR dentro de un espacio llamado dominio. Este es similar a un proceso, ya que de hecho brinda la misma seguridad, pero con un costo de recursos infinitamente menor. En realidad CLR utiliza un único proceso para todas las aplicaciones (tanto sean EXE como DLL) y se encarga también de realizar el aislamiento entre ellos, ver la siguiente figura: Código no administrado Código administrado Los dominios de aplicación controlan la visibilidad y la tolerancia contra fallos, sin necesidad de que se requiera un nuevo proceso por caja ejecución. Decimos entonces que un dominio es un proceso por software controlado por CLR, el cual es la nueva unidad de proceso en la infraestructura .NET Recolector de Basura El recolector de basura o desechos es un servicio de la plataforma .NET, y ,más específicamente provisto por CLR para código administrado. El mismo se encarga

CLR

Aplicación Proceso1

Aplicación Proceso3

Aplicación Proceso 2

EXE

EXE

EXE DLL

DLL

DLL

Proceso

Ensamblado Ensamblado

Ensamblado

DLL DLL

DLL

9

de liberar la memoria en forma automática de aquellos objetos que no están siendo utilizados, sin que el desarrollador tenga que ocuparse de esta tarea. En las versiones anteriores de Visual Basic, usted podía indicar y conocer el momento exacto en el cual se deseaba que un objeto fuese liberado, simplemente asignando nothing a la variable. Esto llevaba a que si el contador de referencias alcanzaba el valor 0, la memoria ocupada por el mismo era recobrada por el sistema operativo. A esta función se le denomina finalización determinada, ya que el desarrollador adquiere control total sobre la misma. La perdida de esta característica a causado algunos inconvenientes como que se desconoce el momento exacto en el cual el colector llevará a cabo su tarea (liberar memoria), y por ende, no se debe escribir código partiendo de la base de que el objeto será destruido en el preciso instante en que la referencia es liberada, (se asigna nothing a la variable). Por ejemplo, ahora las clases ofrecen un procedimiento de evento llamado Finalize en vez de Terminate, el cual es ejecutado cuando el objeto es colectado. Este proceso puede diferir sustancialmente en el tiempo en que la referencia ha sido liberada, y de hecho, en el caso de que el proceso involucre a varios objetos, el mismo no asegura que éstos sean liberados en igual orden.

10

Tipos de Proyectos Disponibles Una de las ventajas de la integración de Visual Basic con la plataforma .NET es que ahora se cuenta con una mayor cantidad de plantillas de tipos de proyecto, muchos de los cuales tienen vinculación con las nuevas tecnologías Web. Para crear un proyecto de aplicación, basta con ir a la opción Archivo, luego seleccionar Nuevo, y por ultimo Proyecto. Esto exhibirá la ventana de Nuevo Proyecto, la cual ofrecerá las nuevas opciones disponibles, como a continuación se muestra:

Aplicación para Windows Crear un nuevo proyecto de aplicación para Windows, de forma similar a lo que se hacia en versiones anteriores con un tipo EXE Estándar. Biblioteca de Clases Este es el reemplazo (pero no la migración automática) para todos los proyectos de tipo EXE Activex o DLL Activex construidos en la versión anterior. El mismo contiene un conjunto de clases, las cuales serán ofrecidas por intermedio de un ensamblado. El resultado final de este tipo de proyecto es siempre un archivo (Ensamblado) con extensión DLL, ya que en .NET no se cuenta con biblioteca de clase con extensión EXE. Biblioteca de Controles de Windows Lo que en versiones anteriores se realizaba mediante controles Activex, ahora se debe efectuar a través de este tipo de proyecto. Básicamente, el mismo sirve

11

como contenedor para implementar un control para ser dibujado sobre un formulario de Windows. Aplicación Web ASP.NET Este tipo de aplicación permite trabajar con el modelo de páginas de servidor activo ASP.NET. Las mismas se componen de formularios web, los cuales son gestionados por el entorno de desarrollo en forma similar a los formularios estándar de Windows, pero con la diferencia de que ellos serán posteriormente transformados a ASP.NET, y finalmente a HTML o DHTML. Debido a ello, el resultado final es siempre accesible desde cualquier explorador. Este tipo de proyecto elimina la necesidad de la herramienta Visual Interdev, o los lenguajes de código script del lado servidor. Servicio Web ASP.NET Un servicio Web ASP.NET hace posible el compartir funcionalidades en forma similar a lo que se hace con una biblioteca de clases, pero a través de los protocolos estándar de Internet. Todo esto con igual facilidad de la que llevaba (en versiones anteriores) implementar una biblioteca de funciones estándar de Windows. Biblioteca de Controles Web Similar a una biblioteca de controles para Windows, pero para utilizar en paginas de servidor activo ASP.NET. Esta característica facilita la reutilización de elementos y controles gráficos, sin necesidad de que se tenga que descargar ningún elemento adicional en el explorador. Aplicación de Consola Los proyectos de este tipo permiten crear aplicaciones que no requieran de interfaz gráfica, y que se puedan valer simplemente de texto. El resultado de la misma puede ser exhibido a través de una ventana de consola, en forma similar a lo que hacían las aplicaciones del sistema operativo DOS. Existen también formas de interactuar con la consola, a los efectos de escribir o leer una cadena de texto del teclado o de la pantalla. Servicio Windows Ahora es posible crear un servicio Windows, cosa que era bastante complicada de implementar en versiones anteriores, ya que se debía apelar a un críptico conjunto de API del sistema operativo.

12

Analizando la Estructura de Proyectos Cada proyecto contiene una variedad de archivos de solución para cada tipo de proyecto. Para simplificar el manejo, los archivos del proyecto son almacenados en el directorio del proyecto. Solution Files (.sln, .suo) La extensión .sln es usada por Solution Files que ligan uno o más proyectos a la vez. Y son solo usados para cargar información global. Estos archivos son similar a los grupos de Visual Basic (.vbg) en versiones anteriores. Los archivos con extensión .suo son usadas por los archivos de opciones de los usuarios que acompañan a cualquier solución almacenada y cualquier personalización que usted pueda hacer en su solución. Estos archivos salvan su configuración, como pueden ser puntos de depuración y los elementos del trabajo, de modo que son recuperadas cuando abre la solución. Project Files (.vbproj) El archivo del proyecto es un documento XML (Extensible Markup Language) que contiene referencias a todos los elementos del proyecto, tales como formas y clases, referencias del proyecto y opciones de compilación. Los archivos de Visual Basic .NET tienen la extensión .vbproj para diferenciarse de otros proyectos .NET como pueden ser visual c# con extensión .csproj. Esto permite crear fácilmente proyectos que incluyan diferentes lenguajes en la misma solución. Local Project Items (.vb) En versiones anteriores de Visual Basic se usaban diferentes extensiones para identificar a las clases (.cls), formas (.frm), módulos (.bas), y controles del usuario (.ctl). Visual Basic .NET usa la misma extensión para identificar a estos (.vb). Por ejemplo usted puede crear más de un elemento en el mismo archivo. Usted puede tener una clase y algunos módulos, una forma y una clase, o múltiples clases todas dentro del mismo archivo. Esto nos permite tener elementos fuertemente relacionados en el mismo archivo. Web Project (.aspx, .asmx, .asax) Los proyectos en web solo usan elementos de tipo clases y módulos con extensión .vb. De cualquier modo, los proyectos web incluyen archivos específicos para Internet, como .aspx para Web Forms, asmx para XML Web Services, y .asax para clases globales.

13

El nuevo entorno Integrado

Existen varias diferencias entre los entornos de versiones anteriores y la nueva interfaz de desarrollo. Con el fin de aprovechar mejor la pantalla, se utilizan tres características:

1. Ventanas acoplables. 2. Pestañas.

3. Ocultamiento automático.

La idea principal de las ventanas acoplables, es que se peguen unas con otras, con el fin de reducir la cantidad de espacio utilizado (sector E). También es posible emplear pestañas, con el fin de que sólo una de ellas este visible a la vez (sector D). Con el ocultamiento automático se permite conservar a un más espacio del área de la pantalla (sector B).

A) Barra de Herramientas y Menús

B) Cuadro de Herra- mientas y explorador servidores

C) Opciones referentes a compilación, o lista de tareas pendientes

E) Proyectos abiertos propiedades, ayuda, etc.

D) Área de diseño y edición

14

Que son los Ensamblados? Microsoft a creado un nuevo modelo de bibliotecas y ejecutables para la plataforma .NET a través de los ensamblados, e implementar un nuevo tipo de infraestructura, la cual brinda muchas mejoras. Si bien los nuevos ejecutables y bibliotecas cuentan con la misma extensión (EXE o DLL), en realidad se componen de diferentes estructuras internas. Una de las características sobresalientes radica en que el nuevo modelo no está centrado en el archivo de registro como repositorio de información de aplicaciones. Un ensamblado DLL es una unidad auto contenida de código e información, esto quiere decir que cuenta dentro de si con toda la información requerida, como las diferentes funciones, su versión, otros ensamblados del cual depende, etc., y todo esto a través del Manifiesto y la sección de meta datos. Esto hace que el mismo no requiera ser registrado para poder utilizarse, y que baste con que el mismo sea copiado a la carpeta de la aplicación para que este se haga funcional. Al igual que en versiones previas, se emplean archivos EXE o DLL para ejecutables y bibliotecas, respectivamente (ahora llamados ensamblados EXE y DLL). Todo proyecto creado adiciona un módulo AssemblyInfo.vb, el cual almacena información descriptiva del mismo, como el nombre de la compañía, el producto, etc.

<Assembly: AssemblyTitle("")> <Assembly: AssemblyDescription("")> <Assembly: AssemblyCompany("")> <Assembly: AssemblyProduct("")> <Assembly: AssemblyCopyright("")> <Assembly: AssemblyTrademark("")>

Basta con escribir la información deseada dentro de las comillas, para que la misma sea contenida posteriormente por el compilado y exhibida cuando se haga botón derecho y luego propiedades sobre el ensamblado EXE o DLL desde el explorador de Windows. Se incluye también una carpeta de referencias, la cual almacena todos los nombres de los ensamblados que puede requerir la aplicación para se ejecutada. Esta característica se constituye como una ventaja sustancial con respecto a versiones anteriores, ya que ahora basta con expandir la misma para conocer sus dependencias (ensamblados DLL). Los ensamblados son creados automáticamente cuando se compilan los archivos fuente de Visual Studio .NET.

15

Activando Referencias al Proyecto El modelo .NET provee un nuevo concepto de componentes y controles, los cuales no dependen del archivo de registro, por lo que no necesitan ser registrados. Visual Basic puede crear solamente controles y componentes de este tipo, debido a que produce siempre código administrado (MSIL). Sin embargo, se puede hacer uso también de bibliotecas del modelo COM o controles Activex y para ello la plataforma .NET crea un contenedor en forma transparente para el desarrollador. Esto ultimo puede afectar el rendimiento final de la aplicación, pero asegura la compatibilidad con dicho modelo. Para agregar una referencia a un ensamblado o biblioteca, basta con ir al menú de Proyecto y luego Agregar Referencia, lo que dará como resultado la siguiente ventana:

Cada pestaña hace posible adicionar un tipo diferente de componente o control. La pestaña de .NET no ofrece la lista completa de ensamblados disponible, ya que ahora es posible indicar con exactitud cuáles de todos ellos serán exhibidos. Esto elimina el inconveniente con que cuenta la lista de componentes COM, la que incluye siempre todas las bibliotecas instaladas y registradas en el archivo de registro.

16

Los Espacios de Nombres (Name Spaces) Los espacios de nombres son paquetes que pueden contener clases, interfaces y módulos dentro de una estructura fácil de entender. Se encuentran organizadas en forma jerárquica y no plana, similar a lo que seria una estructura de carpetas del sistema de archivos. Sintaxis

Imports [alias=] <Espacios de nombre separados por punto>

Imports System.IO

Este ejemplo muestra cómo indicar que se utilizarán las clases del subes pació IO, del espacio principal System, las cuales se encargan de la manipulación de entrada y salida a archivos, memoria, etc. Una vez realizada la misma, es posible hacer uso en el lenguaje de las clases allí existentes, por ejemplo de la siguiente forma: Dim Lector as BinaryReader También puede hacerse referencia en forma directa (estática en otros lenguajes), simplemente nombrando cada uno de los espacios de nombres en los cuales la misma está incluida, para el caso en donde no adicione la importación:

Dim Lector as System.IO.BinaryReader Cada espacio de nombres puede contener otros, y cada uno de ellos puede incluir a su vez una o varias clases. El paquete raíz más importante de la jerarquía se llama System, y es aquel que almacena la mayor parte de los espacios de nombres y clases. Mediante la palabra Imports se le advierte a Visual Basic de que deberá proporcionar un acceso rápido para todas las clases existentes en el espacio de nombres. Basta entonces con realizar la importación correspondiente, para que sea posible emplear las clases sin necesidad de tener que adjuntar la ruta a la misma. Las sentencias de importaciones deben de ir al comienzo de cada módulo, y tiene alcance del mismo. De esta forma, cada una de ellas estará asociada con cada uno de éstos . A su vez, muchos de los módulos en Visual Basic agregan en forma automática importaciones, las cuales son utilizadas directamente por el lenguaje, con el fin de facilitar el acceso a las diferentes funcionalidades.

17

Creando Namespaces Un espacio de nombres es un mecanismo por el que las clases pueden ser agrupadas dentro de paquetes lógicos. Esto se hace con el fin de simplificar el acceso, ya sea internamente desde la aplicación o desde elementos externos. De forma similar a lo que sucede con el nombre de la aplicación en versiones anteriores, un espacio de nombres también debe ser nombrado para acceder a la clase. Sin embargo, existen varias diferencias conceptúales con la primera aproximación. Cuando se esta trabajando con un proyecto de biblioteca de clases, es posible establecer el espacio de nombres en la cual alojaran sus integrantes mediante dos alternativas:

- la ventana de propiedades del proyecto. - la utilización de la instrucción NameSpace.

La primera es la más sencilla, ya que basta con asignar a la opción Espacio de Nombres de la raíz de la ventana de propiedades del proyecto el espacio de nombres que contendrá los elementos de la misma. La segunda implica algo más de conocimiento, ya que se realiza a través de la instrucción NameSpace, la cual debe ser incluida dentro del módulo de clase. NameSpace AccesoADatos Public Class Cliente ‘ Implementación End Class Public Class Socio ‘ Implementación End Class End NameSpace El espacio de nombres se define antes de la declaración de la clase, y tiene el alcance de todos los elementos englobados en el bloque. En este caso, tanto Cliente como Socio estarán agrupados dentro del espacio de nombres AccesoADatos. Posteriormente para acceder a alguna de las clases se deberá nombrar el o los mismos, separados por punto y seguido del nombre de la estructura. Dim MisClientes as New AccesoADatos.Cliente() MisClientes.AgregarCliente(“Abraham”,”Nomeolvides 455”) A simple vista el resultado es similar a la solución ofrecida por versiones anteriores mediante la inclusión del nombre del proyecto, pero, sin embargo, existen algunas ventajas como por ejemplo, que pueden existir varios espacios de nombres dentro de una misma aplicación o que éstos pueden estar agrupados jerárquicamente.

18

Namespace AccesoADatos Public Class Cliente ‘Implementación End Class End NameSpace Namespace Impuesto Public Class ClacTasa ‘ Implementación End Class End Namespace Si va a usar esta técnica es recomendable que ponga en blanco el valor propuesto por la ventana de propiedades del proyecto, o que lo tenga en cuenta posteriormente. Para utilizar alguna de las clases se debe nombrar siempre el espacio respectivo, aun que es posible también hacer uso de la palabra Imports, la cual permite establecer un acceso rápido a la misma. Dim MisClientes as New AccesoADatos.Cliente() Dim MiTasa as New Impuesto.CalcTasa() o Imports AccesoADatos Imports Impuesto Public Class Class7 Sub Ejemplo() Dim MisClientes as New Cliente() Dim MiTasa as New CalcTasa() End Sub End Class Por otra parte, los espacios de nombres pueden ser anidados, lo que facilita la organización jerárquica de las estructuras. Namespace AccesoADatos Namespace Oracle Public Class Cliente ‘Implementación End Class

End Namespace

19

Namespace SQLServer Public Class Socio ‘Implementación End Class End Namespace

End Namespace Para acceder a las mismas se deberá mencionar cada uno de los espacios referidos y por ultimo el nombre de la clase. Dim X as New AccesoADatos.Oracle.Cliente() Dim Y as New AccesoADatos.SQLServer.Socio()

20

Importando Namespaces El espacio de nombres facilita la visualización y el acceso a una clase, en algunas ocasiones pueden resultar ser no tan cómodas. Por ejemplo: Namespace AccesoADatos Namespace Lectura Namespace SeccAtenCliente Namespace SQLServer ‘ Implementación de clases End Namespace Namespace SQLServer ‘ Implementación de Clases End Namespace End Namespace End Namespace End Namespace Evidentemente, cada vez que desee definir una variable del tipo de la clase, se deberá referir a cada uno de los espacios involucrados. Si bien puede resultar muy mnemónico y estructurado, normalmente no deseara escribir esto en más de una ocasión. Afortunadamente, se cuenta con una solución y lo suficientemente flexible para adecuarse a la mayoría de las casos, la cual consiste en hacer uso de la instrucción imports, que debe ser incluida en la zona de declaraciones del módulo, con el fin de crear un acceso rápido a las clases contenidas por el mismo.

Imports AccesoADatos.Lectura.SeccAtenCliente Posteriormente, podrá referirse a la clase deseada utilizando solamente el resto de la jerarquía. El empleo de alias es también un punto importante, ya que, como se muestra a continuación es posible definir un sobrenombre o alias para un conjunto de espacios.

Imports AccesoBD = AccesoADatos.Lectura.SeccAtenCliente. Dim X as AccesoBD.SQLServer.MiClase

21

Activando las Propiedades del Proyecto Cuando se está implementando un nuevo proyecto en Visual Basic .NET (ya sea para Windows o para la Web), existen varias opciones que pueden condicionar la forma en que el mismo será tratado posteriormente por el diseñador de formularios, compilador o depurador. Haciendo clic derecho sobre el proyecto y seleccionando propiedades, o marcando la opción de Propiedades en el menú de Proyecto, es posible acceder y modificar las mismas. La ventana de propiedades involucra más elementos que los que tenía la versión anterior y, a su vez, dispuestos de diferente forma. La expansión de las mismas se debe principalmente a dos factores, el primero es la integración del producto con la infraestructura .NET, mientras que el segundo es debido a los nuevos tipos de plantillas de proyecto con que ahora se cuenta. Las opciones se almacenan debajo de las siguientes carpetas:

- Propiedades comunes. Incluye las características de configuración vinculadas específicamente al proyecto y compilador.

- Propiedades de configuración. Hace posible establecer opciones más

avanzadas de depuración y optimización.

22

Propiedades Comunes Descripción

General Permite especificar el nombre del ensamblado EXE o DLL, el tipo de aplicación, el objeto de inicio y el espacio de nombres al cual pertenecerá el mismo.

Generar Hace posible establecer los valores por defecto a utilizar durante el proceso de compilación, así como también el icono de la aplicación. Las opciones de Option Explicit y Option Compare son ya habituales para un desarrollador Visual Basic.

Importaciones Permite indicar los espacios de nombres que deberán ser importados para el proyecto.

Ruta de acceso de referencia Indica las carpetas en donde estarán situados los ensamblados adicionados al proyecto mediante la ventana de referencias.

Valores predeterminados del diseñador

Contiene varias características que afectan únicamente a las aplicaciones para el web (ASP.NET). las opciones superiores indican las pautas a seguir por el diseñador de formularios, mientras que la inferior especifica el lenguaje que será utilizado por el Código Script para ser ejecutado del cliente.

Propiedades de Configuración Descripción

Depuración Permite establecer varias acciones a tomar cuando el proyecto entre en tiempo de ejecución.

Optimizaciones Hace posible especificar varias optimizaciones para ser realizadas en el momento de la compilación, con el fin de mejorar el rendimiento o tamaño final del ensamblado.

Generar Permite establecer varios atributos referentes al ensamblado resultante del proyecto. Es aquí donde se debe modificar la carpeta donde el mismo tendrá que ser depositado, cada vez que sea compilado.

Implementación Hace posible contener el nombre del documento de configuración a utilizar por un proyecto Web o servicio Web basado en XML

23

Usando el Explorador de Soluciones Un proyecto es un conjunto lógico de elementos, los cuales se corresponden con archivos físicos. Esto es valido para soluciones de Windows como Web. Se emplea el termino lógico, ya que en muchas ocasiones la relación entre ambos no es de uno a uno. Los diferentes integrantes son exhibidos en el entorno de desarrollo mediante la ventana de Explorador de Soluciones, que remplaza a la Ventana de Proyecto utilizada anteriormente. El rol que cumple la misma es similar, ya que muestra todos aquellos módulos que integran el proyecto; sin embargo, algunas capacidades han sido expandidas. Es posible agregar, renombrar, o eliminar cada uno de ellos, simplemente haciendo clic derecho sobre el mismo y seleccionando la opción deseada. Por otra parte, varios proyectos pueden estar también incluidos bajo la misma solución. Existen algunos archivos más que son creados por la misma, los cuales son gestionados internamente por Visual Basic. Para visualizar estos últimos, basta con hacer clic sobre el icono de Mostrar Todos los Archivos, situado en la ventana de Explorador de Soluciones.

24

Usando el Explorador de Servidores Las aplicaciones creadas con Visual Studio pueden consistir en uno o varios componentes, los cuales pueden estar situados en diferentes equipos comunicados a través de una red. La ventana de Explorador de Servidores permite centralizar el monitorizado y gestión de las distintas características de los mismos, sin necesidad de salir del entorno de desarrollo. Dicha ventana esta localizada encima del cuadro de herramientas como elemento ocultable automáticamente. Cada nodo representa los diferentes servidores, y los elementos hijos a sus características. Veamos entonces qué funcionalidades son provistas por las mismas:

o Colas de mensajes. o Contadores de rendimiento. o Servicios. o Servicios de Crystal. o Lista de procesos (es posible iniciar o detener cualquiera de ellos). o Registro de eventos. Esto incluye: Aplicación, Seguridad Y sistema

(similar a los ofrecidos por NT) o Bases de datos SQL Server.

Es posible agregar un nuevo servidor, simplemente haciendo clic derecho sobre el nodo Servidores y luego seleccionando Agregar Servidor. El Mismo exhibirá una caja de diálogo, la cual solicitara la dirección IP o nombre del equipo. Adicionalmente a los servidores, esta ventana puede también mantener la lista de conexiones de las diferentes bases de datos utilizadas por la aplicación. Si se emplea SQL Server o Oracle, es posible agregar o modificar la estructura de las tablas en forma grafica utilizando el diseñador de datos, sin necesidad de tener que recurrir a las herramientas provistas por las mismas. Para agregar una nueva conexión, basta hacer clic sobre el nodo Conexiones de Datos, y luego seleccionar Agregar Conexión. Sin duda, esta característica es de gran utilidad, ya que centraliza las diferentes funcionalidades de uno o más servidores bajo la misma ventana, lo que es de vital importancia cuando se está trabajando con aplicaciones distribuidas.

25

Usando el Examinador de Objetos El examinador de objetos permite consultar las distintas estructuras, clases y sus propiedades, métodos, eventos, etc. Los mismos pueden ser proyectos incluidos por la solución ensamblados .NET, o componentes del modelo anterior(COM). Para acceder a éste debe hacer Ctrl-Alt-J o seleccionar Ver y luego Otras Ventanas, y por ultimo Examinador de Objetos. Cuando se explora un componente, sus integrantes aparecerán en el panel de la izquierda mientras que sus miembros y descripción a la derecha. Es posible también realizar una búsqueda, simplemente haciendo clic sobre el icono de Buscar Símbolo. Visual Basic cuenta con una infinidad de ensamblados previamente hechos, los cuales se brindan a través de la llamada biblioteca de clases base BCL (Base Class Library), y contienen cientos de funcionalidades. Debido a ello, esta característica es de fundamental importancia para poder investigar las mismas. La siguiente ventana nos muestra la librería de Microsoft Visual Basic .NET Runtime y varios de sus Namespaces. Esta ventana resalta el namespace de Microsoft. Visual Basic y muestra las clases que este contiene, incluyendo la clase DateAndTime, la cual hereda las características de la clase System.Object.

Clas Herencia Namespace Métod

Libreria

26

Usando Lista de Tareas Usted puede usar esta característica para mantener una lista de tareas con las que trabajara, o les dará seguimiento, podrá eliminar las tareas al ser completadas. Las tareas son conservadas dentro del archivo del proyecto .suo, para que estas no se pierdan cuando cierra su sesión de Visual Studio .Net. Cualquier tarea cargada será disponible para todos los desarrolladores que usen el mismo archivo del proyecto .suo. Para abrir la ventana Lista de Tareas, selecciónela en la opción de Otras Ventanas en el menú de Ver. Para adicionar tareas a la lista:

o Usted puede adicionar una tarea manualmente tecleándola en la columna de descripción en la venta de Lista de Tareas.

o Visual Basic .Net adiciona los errores, comentarios para actualizar, etc.

o Usted puede adicionar tareas creando comentarios en el código usando una palabra clave definida en la caja de dialogo Options, la cual es accesible en el menú de Tools. Las palabras claves TODO, HACK y UNDONE están creadas para usted, pero usted puede definir las suyas.

27

Ayuda Dinámica La ayuda dinámica despliega automáticamente el archivo de ayuda que más se ajuste a la ayuda requerida, dependiendo donde se encuentre el cursor y si el texto esta seleccionado. Por ejemplo, el resultado que la ayuda dinámica despliega varia dependiendo de donde se encuentre el cursor posicionado: Dim x as Integer

o Si el cursor se encuentra posicionado dentro de la palabra Dim, la ayuda dinámica despliega la información relevante a la palabra Dim.

o Si el cursor se encuentra dentro de la palabra integer , la ayuda dinámica despliega la información relevante a tipos de dato.

Usted puede usar la caja de dialogo Options, que se encuentra dentro del menú Tools, para configurar los elementos que la ventana de ayuda desplegará. La siguiente ventana muestra la caja de dialogo de Options:

28

Depurando Aplicaciones La depuración de un error supone mucho más que ejecutar el código para determinar si éste hace las cosas en forma correcta. En Muchas ocasiones, los errores se darán solamente bajo algunas circunstancias, por lo que las herramientas provistas para esta tarea serán, realmente esenciales cuando se desee localizar un error. Desde las primeras versiones de Visual Basic, las mismas han sido un punto a favor para elegir el producto como lenguaje de desarrollo principal. Sin embargo, ahora se cuenta con un nuevo entorno de desarrollo, el cual ofrece varios beneficios, y por supuesto también algunas desventajas, en comparación con los propuestos anteriormente. En esta versión se incluyen varias utilidades adicionales, las cuales hacen posible detectar un error en forma más precisa y eficiente, pero, como punto negativo, no es posible modificar el código cuando se está depurando el mismo, y ahora es necesario volver a tiempo de diseño e iniciar nuevamente la aplicación para realizar esta tarea. La restricción se debe a la integración del producto con el modelo Web, en la cual se hace uso de las mismas herramientas. Puntos de Interrupción y Condicionar un Punto de Interrupción Para que se detenga la ejecución en un lugar especifico, es necesario marcar un punto de interrupción. Para ello, basta con hacer clic sobre el margen Izquierdo o proporcionar la tecla F9, lo que marcará como resultado la línea, indicando que se detendrá la ejecución de la aplicación en dicho punto. Es posible quitar todos mediante la opción Borrar Todos los Puntos de Interrupción del menú Depurar, o hacer F9 de nuevo sobre el mismo para quitarlos. También es posible habilitar o deshabilitar los mismos sin necesidad de eliminarlos, mediante la ventana de Puntos de Interrupción. Para abrir la ventana de Puntos de Interrupción selecciónela en la opción de Ventanas en el menú Depurar.

Mediante esta ventana es posible también introducir condiciones para la interrupción, haciendo clic derecho sobre el renglón del punto de interrupción.

29

La siguiente ventana muestra un punto de interrupción condicionado, cuando el valor de la caja de textos valor.text sea igual a 100.

Como beneficio adicional, estos son guardados en forma conjunta con el proyecto, por lo que la próxima vez que abra el mismo, encontrará las marcas previamente configuradas. La Ventana de Comandos La ventana de comandos permite evaluar expresiones principalmente durante el proceso de depuración. Tiene cierto parecido con lo que se conocía como ventana de Inmediato en versiones anteriores. Sin embargo, brinda algunas opciones más avanzadas, las cuales describiremos a continuación. Para acceder a la misma basta con ir al menú Ver, luego Otras Ventanas, y por último Ventana de Comandos, o presionar CTRL-ALT-A (aunque en tiempo de ejecución es mostrada por defecto). Si desea evaluar una variable, propiedad o expresión, durante la depuración, basta con escribir >Debug.Print x o >? x. espacio Si desea blanquear el contenido de la ventana de comandos debe escribir la instrucción cls.

30

Esta ventana también permite ejecutar elementos del menú en forma de comandos de texto. Por ejemplo, la siguiente línea busca debajo de la carpeta y subcarpetas del proyecto aquellos archivos que contengan el texto AlexLora, y posteriormente muestra los resultados.

>Edit.FindInFiles “AlexLora” Asimismo es posible asignarles un seudónimo, a los efectos de que sea más cómoda su invocación. Para conocer más al respecto, introduzca la palabra alias dentro de la misma y presione <enter>.

La Ventana de Inspección La ventana de inspección es normalmente exhibida en la parte inferior derecha de la pantalla, y hace posible observar el contenido de una variable, (véase la siguiente figura). Basta con escribir dentro de ella el nombre de la misma para conocer su valor y tipo. La misma permite también modificar el contenido del elemento que está siendo evaluado, simplemente haciendo clic sobre la columna que contiene su valor y escribiendo uno nuevo.

Esta ventana solo la puede ver y abrir estando en ejecución el proyecto. Para abrirla basta seleccionar Inspección en la opción de Ventanas en el menú Depurar.

31

La ventana de Automático La ventana de automático ofrece funcionamiento similar a la de inspección, pero incluye por defecto todas las variables empleadas dentro del procedimiento, con sus respectivos valores y tipos. Podemos acceder a ella en el menú Depurar en la opción Ventanas y Ventana de Automático.

La ventana de Locales La ventana de locales es similar a la inspección y automático, pero incluye todas las variables y propiedades de componentes, que son visibles por el procedimiento en el cual se localiza el punto de ejecución. Podemos acceder a ella en el menú Depurar en la opción Ventanas y Ventana de Locales.

La ventana de Pila de Llamadas En muchas ocasiones es necesario conocer las llamadas que originaron que el flujo del programa se sitúe en la línea en la cual está parado. Para ello se cuenta con la ventana Pila de Llamadas, la cual hace posible conocer dicha información haciendo clic sobre cada una de las entradas.

32

Módulo 2: Adiciones en la Sintaxis y el Lenguaje Tipos de Dato como Estructuras de Clase Los tipos de dato definidos en Visual Basic tales como enteros, enteros largos, cadenas de texto, etc., correspondían en versiones anteriores a elementos estándares definidos en el Runtime del lenguaje, pero en esta nueva versión todos los tipos de dato han sido convertidos en estructuras de clase. Por ejemplo en la versión anterior: Dim iContador as Long IContador = 10 La primera línea define una variable de tipo entero largo, y posteriormente se le asigna un valor a la misma. En este caso, el lenguaje define un tipo de dato preestablecido en el Runtime, y posteriormente se encarga de la manipulación del mismo. En la nueva versión la sintaxis es similar, pero, sin embargo, el lenguaje crea una variable de objeto de tipo entero largo. Dim iContador as long IContador = 10 Aquí también se asigna un valor, pero el mismo es guardado por la variable de objeto iContador de tipo entero largo. La respuesta a este cambio es debida a la utilización de estructuras de clase como elemento base de la infraestructura .NET. Adicionalmente, los tipos de dato no son considerados parte del lenguaje, ni son manipulados por este último, sino que están definidos en forma común para todos los integrantes de Visual Studio mediante el sistema común o universal de tipos. Esto ofrece dos grandes beneficios:

- Funcionalidades asociadas a cada tipo. - Compatibilidad de tipos entre lenguajes.

Debido a que todos los tipos de dato son clases, los mismos pueden incluir funcionalidades asociadas a ellos en forma de métodos y propiedades (ver la siguiente figura). Por otra parte, las conversiones que requerían el empleo de funciones independientes son ahora realizadas mediante métodos asociados al tipo de dato.

33

Al existir un único repositorio común con la definición de los mismos, se facilita la utilización de ensamblados implementados en otros lenguajes de la plataforma .NET, ya que un tipo de una precisión determinada será el mismo tipo para todos los integrantes que hagan uso de la infraestructura. Sin embargo, este cambio afecta a la forma en que los mismos deberán ser utilizados. La asignación de versiones anteriores entre tipos y objetos no estaba permitida, pero ahora es posible debido a que todas las variables son de este tipo. A su vez, se utilizaba la palabra Set para asignar una variable de objeto, la cual ha sido eliminada totalmente del lenguaje, por lo que la siguiente línea: Set iContador = iVal Se ve modificada a esta otra:

iContador = iVal Este ejemplo muestra cómo se copia una variable de objeto a otra, pero no debe confundirse esto último con que ambos apunten a la misma instancia de memoria. El siguiente ejemplo muestra cómo transformar una variable de tipo entero largo a cadena de texto, empleando uno de sus métodos intrínsecos. Dim Telefono as long Dim Lada as string Telefono = 7825845 Lada = “(461)” Lada = Lada & Telefono.ToString Msgbox(Lada) ‘ El resultado es (461) 7825845 La asignación y lectura de valores de variables en Visual Basic .NET se hace en forma similar a versiones anteriores, aunque se trate en realidad de objetos. Es importante recordar nuevamente que la liberación de recursos utilizados por un objeto no debe ser realizado por el desarrollador mediante la palabra Nothing,

34

debido a que el recolector de basura detecta cuándo el mismo no está siendo utilizado y libera la memoria ocupada por éste. Sin embargo, gran parte de las clases ofrecen un método llamado dispose, el cual obliga al colector a liberar la misma tan pronto como éste sea invocado. Sin embargo, esto puede redundar en una caída momentánea del rendimiento de la aplicación. Estructura Común de Tipos Todos los tipos de dato ahora se corresponden con estructuras de clase definidas en la infraestructura .NET (en la clase System.Object), por lo que el lenguaje se remite estrictamente a emplear éstos. En realidad, los tipos de dato originales tienen nombres <<extraños>> para Visual Basic, por lo que este último le brinda un seudónimo más acorde y amigable, con el fin de lograr una apariencia similar a la que se tenía en versiones anteriores. A continuación se muestra la estructura tipos definidos dentro del espacio de nombres System.Object:

Nombre Real Tipo en Visual Basic Descripción Boolean Bolean Verdadero o falso Byte Byte 0–255 sin signo Char Char Un carácter en formato Unicode (2 Bytes)System.DateTime Date

DateTime Fecha y Hora

Decimal Decimal +/–79,228,162,514,264,337,593,543,950,335sin punto decimal; +/–7.9228162514264337593543950335 con 28 posiciones a la derecha del punto decimal; el numero no cero más pequeño es +/–0.0000000000000000000000000001

Double Double –1.79769313486231E308 a –4.94065645841247E-324 para valores negativos; 4.94065645841247E-324 a 1.79769313486232E308 para valores positivos.

Int16 Short –32,768 to 32,767 Int32 Integer –2,147,483,648 a 2,147,483,647 Int64 Long –9,223,372,036,854,775,808 a

9,223,372,036,854,775,807 Object Object Dirección (32bit) que referencia a una

instancia de una clase. Single Single –3.402823E38 a –1.401298E-45 para

números negativos; 1.401298E-45 a 3.402823E38 para números positivos.

String String Aproximadamente 2 billones de caracteres en formato Unicode

35

Cambios a los Tipos de Dato Existentes Currency Un Cambio Importante a tener en cuenta radica en que el tipo de dato Currency no está soportado, por lo que en su lugar se debe hacer uso de Decimal. El mismo cuenta con características similares, ya que provee soporte para valores muy grandes. Integer Hay cambios en la capacidad de los tipos de dato entero y entero largo. Donde en versiones anteriores se utilizaba un entero (Integer) ahora se debe emplear el tipo de dato Short. De forma similar, lo que representaba un entero largo (Long), ahora se debe definir como un entero (Integer). String Un tipo de datos String en la plataforma .NET es inmutable, y esto quiere decir que una vez asignado un valor, el contenido del mismo no será modificado. De esta forma cualquier intento de modificación de un elemento de la cadena o todos derivará en un nuevo objeto de tipo String, mientras que el objeto original será destruido. Por supuesto que este proceso es trasparente para el desarrollador, pero puede influir seriamente en el rendimiento final de la aplicación. Imagine una aplicación que realice 52 modificaciones sobre una cadena de 400 KB. Cada vez que ésta sea modificada, será creada una nueva instancia del objeto conteniendo la nueva cadena. Lo mismo pasa si se concatenan dos de ellas, lo que derivará en una nueva cadena conteniendo el resultado, como muestra la siguiente figura: Basta con que la aplicación tenga que realizar esta tarea en reiteradas ocasiones para que se obtenga como consecuencia un consumo excesivo de tiempos y recursos. Con el fin de gestionar este tipo de situaciones, la biblioteca de clases, BCL, ofrece una alternativa que permite la modificación de los elementos, sin que ello derive en la creación de una nueva instancia del objeto, y para dicho fin se cuenta con una clase llamada StringBuilder. La misma representa un espacio de memoria (Buffer), y contiene métodos para gestionar su contenido, sin que ello derive en la creación de una nueva instancia de objeto.

160Kb 140Kb

300Kb

36

Dim NombreAlumno as New System.Text.StringBuilder o Imports System.Text . . Dim NombreAlumno as New StringBuilder Si bien StringBuilder provee en forma automática un tamaño de memoria inicial para almacenar la cadena, es recomendable que se especifique uno en su inicialización. Dim NombreAlumno as System.Text.StringBuilder NombreAlumno = new System.Text.Stringbuilder(60) En este caso, el tamaño inicial del texto será de 60 caracteres, aunque si la misma es sobrepasada, ésta será expandida automáticamente. Una vez creado el objeto, el mismo quedará apto para ser utilizado. La manipulación de la información que debe ser guardada se logra mediante el conjunto de métodos asociados a la clase. Dim NombreAlumno as System.Text.StringBuilder Dim Cadena as String NombreAlumno = new System.Text.StringBuilder() NombreAlumno.Append(“Hola Abraham”) Cadena = NombreAlumno.ToString El método Append permite que una cadena de texto sea copiada dentro del objeto StringBuilder. Para asignar el contenido de un objeto StringBuilder a uno String, el mismo debe ser siempre convertido. Por otra parte, la inserción, eliminación y reemplazo se logra mediante los métodos Insert, Remove y Replace, respectivamente. Dim NombreAlumno as System.Text.StringBuilder NombreAlumno = new System.Text.StringBuilder() NombreAlumno.Append(“Hola Abraham”) NombreAlumno.Replace(“Abraham”,”Juan Pablo”) NombreAlumno.Insert(NombreAlumno.Length, “!!”) Msgbox(NombreAlumno.ToString)

37

Sin duda, la clase StringBuilder ofrece una característica importante a la hora de obtener un rendimiento más optimo en lo que a manejo de cadenas de refiere. Hacer el siguiente ejemplo para contabilizar los tiempos requeridos de un proceso de concatenación efectuado con String y otro con StringBuilder:

1. Agregar los siguientes controles, de acuerdo a la siguiente tabla:

Objeto Propiedad Valor Label Nombre

Text Label1 TiempoString:

TextBox Nombre Text

TiempoString

Label Nombre Text

Label2 Tiempo StringBuilder:

TextBox Nombre Text

TiempoStringBuilder

Button Nombre Text

PruebaString Prueba String

Button Nombre Text

PruebaStringBuilder Prueba StringBuilder

En el evento click del botón PruebaString agregaremos el siguiente código: Private Sub PruebaString_Click(… Dim Cadena As String Dim i As Integer Dim Inicio As Date = Now For i = 1 To 5000 Cadena = Cadena & "Prueba de Concatenación" Next i TiempoString.Text = Now.Subtract(Inicio).TotalSeconds & "Segundos" End Sub

38

A continuación agregue el siguiente código al botón de PruebaStringBuilder Private Sub PruebaStringBuilder_Click(… Dim i As Integer Dim Cadena As System.Text.StringBuilder Dim Inicio As Date = Now Cadena = New System.Text.StringBuilder(150000) For i = 1 To 5000 Cadena.Append("Prueba de Concatenación") Next i TiempoStringBuilder.Text = Now.Subtract(Inicio).TotalSeconds & _

"Segundos" End Sub Dependiendo del sistema operativo y del procesador, los resultados ofrecidos serán diferentes. En un Duron a 950 Mhz con Windows 2000 Server, la ejecución de StringBuilder es 426 veces más rápida (Tiempo del String = 4.2661344 Segundos y el Tiempo del StringBuilder = 0.0100144 Segundos). Date Algunos desarrolladores utilizaban en versiones anteriores el tipo de dato doble para almacenar fechas. En efecto, las fechas eran en realidad información de tipo doble bajo el tipo Date. Visual Basic no ponía ninguna restricción, y de hecho ambos podían ser intercambiados en cualquier momento sin ningún efecto secundario. En esta versión se utiliza un formato propio de fecha y fecha/hora, el cual no guarda relación con el doble. Dim Fecha as Date Dim Doble as Double Fecha = now ‘Produce error de compilación Doble = Fecha Debido a ello, no es posible almacenar directamente una fecha en una variable de este tipo, aunque pueden emplearse algunas técnicas de conversión con el fin de realizar dicha tarea, como forma alternativa a tener que modificar toda la aplicación. Veamos entonces los métodos disponibles -ToA2Date -FromOADate

39

Los mismos permiten convertir una fecha y hora a double o viceversa. La utilización es sencilla, ya que simplemente se debe invocar a uno u otro método sin necesidad de pasos previos, o información adicional. Dim FechaHora As Date Dim dblFechaHora As Double FechaHora = Now 'Convierte el tipo de dato Fecha y Hora a Doble dblFechaHora = FechaHora.ToOAdate 'Convierte el tipo de dato doble a Fecha y Hora FechaHora = Date.FromOADate(dblFechaHora) MsgBox("La Fecha y Hora es: " & FechaHora.ToString)

40

Conversiones Estrictas Las conversiones implícitas permitidas en versiones anteriores de Visual Basic ya no están permitidas En Visual Basic .NET las cosas han cambiado, ya que ésta es la primera versión que implementa una nueva modalidad, la cual permite especificar en forma explicita si se desean conversiones implícitas entre tipos de dato o no. Para ello, se agrego una nueva instrucción llamada Option Strict On, la cual fuerza a que la conversión entre tipos de dato tenga que efectuarse explícitamente mediante la función respectiva. La misma se debe definir como primera línea del módulo y solamente tiene alcance para el mismo. Una vez escrito éste, cualquier intento de compilación que incluya una conversión de un tipo a otro sin utilizar la función adecuada dará lugar a un error. Por ejemplo: Option Strict On Dim Texto as String Dim Numero as long Texto = “52” ‘Esta Línea produce error Numero = Texto MsgBox(Numero) La ventaja obtenida es que las conversiones implícitas son deshabilitadas, generándose así un error en tiempo de compilación en todas aquellas líneas que requieran a las mismas. Para solucionar el problema de conversión se debe recurrir a las funciones de transformación ofrecidas.

Texto = Cstr(Numero) A su vez, la conversión de cadena de texto a un valor numérico puede realizarse también mediante el método asociado.

Texto = Numero.ToString Visual Basic cuenta además con varias palabras Option que pueden especificar controles adicionales a realizar por el compilador:

Opción Valor Descripción Option explicit On Fuerza a que todas las variables sean declaradas Off Las variables pueden ser utilizadas sin ser declaradas Option compare Binary Especifica que la comparación debe hacerse como binario Text Especifica que la comparación debe hacerse como texto Option strict On Fuerza a que se deba hacer en forma explicita la

conversiones mediante las funciones adecuadas. Off Conversión automática de tipos, similar a la versión anterior

(esta es la opción por defecto)

41

Conversiones de Tipos de Dato con Enlace Tardío Para utilizar enlace tardío con la opción estricta, se debe apelar a la función Ctype, la cual permite convertir un tipo en otro en forma explícita, corriendo por parte del desarrollador el riesgo de que en tiempo de ejecución el tipo sea el que se indica. Sintaxis: Ctype(<Variable>,<Tipo destino> as Type) as Type Ejemplo con tipos de dato:

Dim Texto as String Dim Objeto as Object ‘Almacena una cadena de texto Objeto = “Hola Abraham” Texto = Ctype(Objeto, String).ToLower MsgBox(Texto)

Enlace tardío es una característica que le permite a Visual Basic utilizar funcionalidades que no están disponibles en el momento del desarrollo o de la compilación de la aplicación, pero sí durante la ejecución.

42

Declarando e Inicializando Variables y Arreglos La sintaxis empleada en la declaración de variables en Visual Basic .Net permanece inmutada. Sin embargo, muchas de las nuevas características que pasan inadvertidas pueden tener un impacto más que importante en la migración de sus aplicaciones. Si bien todas las variables numéricas son inicializadas en 0 y los tipos String como vacíos, ahora es posible especificar directamente un valor en el momento de la declaración de la misma. Dim Numero as Integer = 20 Dim Numeo2 as Integer = 10 Dim Texto as String = “Hola Abraham” La inicialización puede también incluir una constante, así como llevarse a cabo de la forma habitual: Const NumClientes as Long = 10 Dim Clientes as Long = NumClientes Dim Numero as Long Numero = 20 Una técnica similar de inicialización puede emplearse para las matrices, pero dicha técnica es utilizada para precargar en tiempo de diseño los diferentes valores de la misma. Dim miArreglo() as Integer = {1,5,6,6,7} El ejemplo anterior declara una matriz de 5 elementos con sus respectivos valores, utilizando a su comienzo y final llaves. También es posible cargar valores para una matriz bidimensional, pero se debe agregar una coma en medio de los paréntesis: Dim miMatriz(,) as Integer = {54,25,78,14},{487,69,52,45,78} Los cambios en las matrices vienen dados por las funcionalidades que éstas adquieren en esta nueva versión. En muchas aplicaciones es común agrupar un conjunto de variables sin hacer uso de una clase. Para dicho fin se cuenta con los tipos de dato definidos por el usuario (UDT User Defined Type), los que permiten ofrecer un conjunto de elementos bajo una misma entidad. En versiones anteriores de Visual Basic, una estructura definida por el usuario se veía de la siguiente forma: Public Type Cliente Nombre as String Telefono as Integer End Type

43

En Visual Basic .NET la sintaxis sufre un pequeño cambio, que además ofrece la posibilidad de modificar la visibilidad de cada uno de sus miembros. Public Structure Cliente Public Nombre as String Private Telefono as String End Structure Otra característica muy comúnmente empleada eran las variables de texto con longitud fija. Para ello se debía incluir en la declaración el número de caracteres antecedido por un asterisco. Dim Cliente as String * 50 Visual Basic generaba un texto con longitud especifica, rellenando las posiciones sobrantes con el caracter ASCII 0. en esta versión dicha característica no existe en forma directa. Dim Cliente as New String(CChar(“ ”),50) Esto puede ser de utilidad cuando se espera una variable de dicho contenido, pero si se asigna un nuevo valor, ésta adquirirá automáticamente el nuevo largo. Para declarar Múltiples Variables En Visual Basic 6 usted podía declarar en una sola línea múltiples variables, pero usted podría obtener resultados inesperados. Considere el siguiente ejemplo: Dim I,J, X as Integer En Visual Basic 6 I y J eran creadas de tipo Variant, y X de tipo entero. En Visual Basic .NET las tres variables son creadas de tipo entero. Esto es consistente con la mayoría de los lenguajes de programación y más intuitivo.

44

Alcance de Variables El alcance de una variable establece quiénes podrán acceder a la misma, así como también el tiempo de vida de ésta. En Visual Basic .NET el mismo viene determinado por el lugar donde ella sea declarada. Ahora existen cuatro posibles lugares donde la misma puede ser definida, lo cual dictamina la forma en la que estará accesible para los demás miembros del proyecto. Módulo Cuando una variable se define en la zona de declaraciones de un módulo, ésta se hace visible a todos los integrantes de la aplicación. Para agregar un módulo, basta con hacer botón derecho sobre el proyecto en el Explorador de Soluciones y luego Agregar y posteriormente Agregar Módulo. Module Module1 End Module Por la nueva sintaxis que se le agrego a los módulos, ahora se podría definir más de un módulo dentro de un solo archivo. Module Module1 Public Var1 As Long End Module Cualquier otro módulo de la aplicación podrá acceder a ella en forma directa: Private Sub Form_Load(… Var1 = 10 End Sub Es posible también incluir la palabra Private en la definición, a los efectos de que la misma sea visible tan solo por el módulo que la define. Clase o Formulario El segundo tipo de de visibilidad es aquella que involucra los módulos de clases o formularios. Puede accederse a toda variable definida como pública en la zona de declaraciones de los mismos, desde los demás integrantes del proyecto, pero éstos deberán incluir el nombre del objeto donde ella reside. Los módulos de clase en Visual Basic .NET incluyen un indicador de comienzo llamado Class y a continuación el nombre programático, y finalizan con End Class. De hecho, se podría definir más de una clase dentro de un mismo archivo. La zona general se encuentra entre ambos indicadores, y fuera de cualquier procedimiento. Veamos un ejemplo

45

Public Class Negocio Public Interes as Long Sub CalcularMontoInteres() End Sub End Class

Para acceder a la misma, basta con definir un objeto de este tipo y asignar o leer el valor de igual forma que si se tratara de una propiedad.

Dim x as New Negocio() x.Interes = 2

También se puede agregar la palabra Shared a continuación de Public o Private, a los efectos de que se requiera el definir un nuevo objeto para acceder a la misma.

Public Class Negocio Public Shared Interes as Integer End Class

Desde otro sitio de la aplicación, bastaría con hacer referencia al nombre de la clase, seguida de la variable. Negocio.Interes = 10 En este caso, todos los objetos utilizarán la misma instancia de memoria. Procedimiento o Función El tercer tipo de variable corresponde a la definición en un procedimiento o funciones. Toda variable declarada dentro del mismo procedimiento será visible exclusivamente por éste (debe usar la palabra DIM en vez de Public o Private cuando se defina una Variable de Procedimiento o función). Sub Proc1 Dim Var1 as Long Var1 = 10 End Sub Bloque Existe un cuarto tipo de visibilidad, el cual viene dado por la utilización de subbloques dentro de un procedimiento o función. Todas las variables definidas

46

dentro de ellos serán visibles exclusivamente por el mismo, y su vida estará condicionada a la finalización de éste. Dim i as Integer For i = 1 to 10 Dim Y as Boolean = False Y = Not Y MsgBox(Y) Next I Y = True De esta forma Y será visible solamente por el bloque For, y cualquier intento de acceder desde fuera del mismo producirá un error de compilación. Cuando el punto de ejecución llega al bloque, las variables allí definidas serán creadas, y destruidas una vez que finalizo el mismo. La visibilidad de este tipo de variables se hace extensible también para aquellos bloques anidados. Como excepción, no pueden existir dos variables con igual nombre dentro del procedimiento, aunque pertenezcan a diferentes bloques.

47

Operadores de Asignación En Visual Basic .NET se incluyen nuevos operadores, los cuales ponen el lenguaje a la altura de los demás. Operador de Concatenación &= Concatena una cadena u objeto de tipo string a la variable, y asigna el resultado. Sintaxis

Variable &= expresión Ejemplo:

Dim Cadena as String Cadena = “Hola ” Cadena &= “Abraham” MsgBox(Cadena)

Operadores *=, +=, -=, /=, \= y ^= Realiza una operación entre la expresión y la variable, y asigna el resultado. Ejemplo: Dim Num1 as Integer Dim Num2 as Integer Num1 = 10 Num2 = 100 Num1 *= Num2 Resultado Num1 = 1000 Num2 = 100 El operador \= divide el valor de la variable por el de la expresión y asigna el resultado entero a la variable. El Operador ^= eleva el valor de la variable a la potencia de la expresión y asigna el resultado a la variable.

48

Llamada a Funciones y Procedimientos Usted debe usar paréntesis para encerrar a los parámetros en cualquier función o subrutina. Si usted llama a un procedimiento que no tiene parámetros, usted debe incluir los paréntesis vacíos. Paso de parámetros por Referencia (ByRef) y por Valor (ByVal) El paso de parámetros por default es ByVal, el cual se agrega automáticamente cuando este no se especifica. Si usted selecciona ByRef Visual Basic pasa la dirección de memoria del parámetro al procedimiento, y el procedimiento puede modificar el valor de la variable directamente. Cuando regresa de la ejecución del procedimiento, la variable contiene el valor modificado. Si usted selecciona ByVal, Visual Basic pasa una copia de la variable al procedimiento. Y si el procedimiento modifica la copia, el valor original de la variable permanece intacto. Cuando la ejecución de procedimiento termina, la variable contiene el mismo valor que antes de que fuera pasada como parámetro. Parámetros Opcionales Ahora en esta nueva versión los parámetros opcionales deberán incluir su valor por default. El siguiente ejemplo muestra la declaración de los parámetros opcionales: Function ADD(ByVal val1 As Integer, ByVal val2 As Integer, Optional ByVal val3 As Integer = 3) As Integer … End Function Regresando Valores desde una Función Visual Basic .Net ofrece facilidades para retornar valores desde una función. Usted puede usar el nombre de la función para retornar el valor de una función. Function Fecha () as String … Fecha = “Fecha ejemplo” End Function

49

Usted puede usar sentencia Return para regresar el valor de una función. Esto evita ligar el regreso con el nombre de la función. Permitiendo facilitar el regreso de la función Function Fecha () as String … Return “Fecha Ejemplo” End Function

50

Utilización de Errores y Excepciones Las excepciones reemplazan la necesidad de emplear las técnicas de captura de error utilizadas en versiones anteriores. Cuando un procedimiento o función inicia una excepción, ésta es capturada automáticamente por CLR, sin importar el lenguaje de la plataforma .NET en el que el módulo fue escrito, y posteriormente se le pasa el control al manejador de excepciones para que intente discriminar la misma. Hasta aquí la diferencia entre ambos modelos es mínima y comparable. Sin embargo, la principal diferencia radica en que la información del fallo es almacenada y trasladada en una estructura de clase, en vez de ser gestionada a través de un objeto implícito del lenguaje como era Err. Para proteger un bloque de código en esta nueva versión se deben utilizar las palabras Try, Catch, Finally y End Try.

Sub Proc1 Try ‘ Incluir el código que va a ser probado Catch ‘ Define el tipo de excepción y la acción a ser tomada Finally ‘ Bloque opcional

‘ Todo código escrito en esta sección será siempre ejecutado ‘ después de la finalización de cualquiera de los bloques ‘ contenidos por Try.

End Try End Sub

En este caso el Catch capturará todos los errores producidos dentro de Try, pero no discriminará de qué tipo se trata. A su vez, los bloques Try pueden ser anidados, por lo que un bloque de captura de excepción puede a su vez contener otro. Si se produce un error dentro del bloque Try, automáticamente CLR dará paso al código incluido por el bloque Catch. Después de discriminar la excepción y ejecutar el código alternativo, se llevará adelante la ejecución del contenido de la sección Finally. En el caso de que no se produzca una excepción, la sección de Finally será ejecutada una vez finalizado con el contenido Try. En Visual Basic existen cientos de errores de excepción, por lo que se cuenta también con cientos de clases para representar los mismos. El preguntar sobre un tipo de clase especifica es más natural que hacer referencia a un determinado número. Por ejemplo cuando se produce un error de división por cero, CLR genera automáticamente un objeto del tipo de la clase DivideByZeroException. Veamos el código del siguiente ejemplo:

51

Dim i1, i2, iResult As Decimal i1 = 22 i2 = 0 Try iResult = i1 / i2 MsgBox(iResult) Catch eexception As DivideByZeroException MsgBox(eexception.Message) Finally Beep() End Try A continuación de la palabra Catch se debe definir la variable de objeto que contendrá la información de la excepción, y posteriormente el tipo que se desea capturar. A continuación se muestran algunas excepciones comúnmente utilizadas:

Nombre Descripción DivideByZeroException Se inicia al intentar una división por cero InvalidCastException Se inicia cuando un tipo de dato no puede ser

convertido. IOException Se inicia cuando existe algún error al intentar

acceder a un archivo. IndexoutOfRangeException Se inicia cuando se intenta acceder a un

elemento de una matriz, empleando un índice fuera de los límites.

También es posible capturar una excepción sin importar el tipo involucrado. Para ello basta hacer uso de la clase base, la cual se denomina Exception. En el siguiente ejemplo el catch atrapa el error de división por cero, haciendo uso de la clase base. Dim i1, i2, iResult As Decimal i1 = 22 i2 = 0 Try iResult = i1 / i2 MsgBox(iResult) Catch eexception As Exception MsgBox(eexception.Message) Finally Beep() End Try

52

La Clase System.Exception Cuando se inicia o se captura un error, es en general indispensable recolectar el mayor número de información posible, y para ello las clases de excepción cuentan con un conjunto de propiedades factibles de ser leídas o escritas para obtener o indicar sus diferentes datos. En la siguiente tabla se muestran algunas de las propiedades y métodos más usados de la clase System.Exception:

Propiedades Nombre Tipo Descripción

HelpLink Lectura / Escritura Hipervínculo relacionado a la misma. InnerException Lectura Referencia a una excepción interna. Message Lectura Descripción del error. StackTrace Lectura Información de la pila de llamados. Source Lectura / Escritura Nombre de la aplicación, o el objeto

que causó el error. TargetSite Lectura Referencia al método que genero la

excepción.

Métodos Nombre Tipo Descripción

GetBaseException Lectura Obtiene la excepción inicial en la cadena de excepciones.

ToString Lectura Convierte el contenido de la excepción en un texto (nombre de la excepción, mensaje de error, nombre de las excepciones contenidas, valor de la pila, etc.)

La clase Exception y sus derivadas cuentan con varias propiedades comunes, a las cuales se accede de forma similar a otros objetos, y permiten recabar mucha más información que en versiones anteriores. Por ejemplo la propiedad Message contiene el texto con la descripción de la excepción. El siguiente código muestra el uso de esta propiedad:

Catch eException As DivideByZeroException MsgBox(“Ocurrio un error: ” & eException.Message )

53

Las excepciones cuentan también con una propiedad llamada StackTrace, la cual hace posible obtener toda la información respectiva a los métodos, archivos y líneas relacionados a la misma.

Catch eException As DivideByZeroException MsgBox(“Ocurrio un error: ” & eException.Message )

MsgBox(“La información de la pila es: ” & eException.StackTrace) Para obtener una cadena de texto que contenga absolutamente toda la información de la excepción, basta invocar al método ToString ofrecido por la misma.

Catch eException As DivideByZeroException MsgBox(“Ocurrio un error: ” & eException.Message )

MsgBox(“La información de la pila es: ” & eException.StackTrace) MsgBox(“La Información es: ” & eException.ToString)

54

Excepciones Anidadas Las estructuras de manejo de error, al igual que las de excepción, hacen posible que las mismas puedan ser iniciadas y posteriormente capturadas, ya sea por el procedimiento que genero la misma como por cualquiera de los involucrados en la pila. Debido a ello, varios procedimientos podrían estar envueltos en la captura y posterior gestión de la misma. En el siguiente ejemplo contamos con un procedimiento llamado Principal, el cual invoca a otro denominado RealizaOperación. Este último llama a Operación, que generará una excepción de división por cero. Esto derivará en que flujo de la aplicación retorne al bloque Catch de RealizarOperación, el cual intentará resolver el problema. Si por algún motivo se produce un error en este bloque, entonces el mismo será automáticamente derivado hacia el procedimiento principal. Sub Principal() Try Call RealizaOperación() Catch eExceptionPrincipal As Exception Dim Excepción As Exception Do Excepción = eExceptionPrincipal.InnerException MsgBox(Excepción.Message) Loop Until Excepción Is eExceptionPrincipal.GetBaseException End Try End Sub Private Sub RealizaOperación() Try Call Operación() Catch eGenerica As Exception Throw New Exception("Mensaje XXX", eGenerica) End Try End Sub Private Sub Operación() Dim x, y, z As Long z = x / z ' Genera la excepción de división entre cero End Sub La clase Exception cuenta con un constructor personalizado, que hace posible iniciar una nueva excepción, pero adjuntando la recibida por el bloque. Esto con el objetivo de mandar el error hacia la rutina Principal: Catch eGenerica As Exception Throw New Exception("Mensaje XXX", eGenerica)

55

En este caso se generará una excepción empleando el texto especificado, pero adjuntando a ésta la original, la cual a su vez podría contener otras excepciones. Esto produce que se envíe un conjunto de excepciones anidadas en vez de un único elemento. Para extraer las excepciones contenidas se cuenta con un método llamado InnerException, el cual retorna la referencia a la misma.

Excepción = eExceptionPrincipal.InnerException También es posible acceder a la primera excepción de la jerarquía mediante el método GetBaseException:

eExceptionPrincipal.GetBaseException En ambos casos se retornará una referencia nula si no se cuenta con la información solicitada. Utilizando estos miembros es posible implementar un ciclo que obtenga cada una de ellas, y posteriormente muestre su contenido: Catch eExceptionPrincipal As Exception Dim Excepción As Exception Do Excepción = eExceptionPrincipal.InnerException MsgBox(Excepción.Message) Loop Until Excepción Is eExceptionPrincipal.GetBaseException Cada iteración del ciclo obtendrá una excepción anidada y la exhibirá. El mismo finalizará cuando el valor sea igual a la excepción base.

56

Módulo 3: Programación Orientado a Objetos para Visual Basic .Net IntroducciónLa programación orientada a objetos es un paradigma increíblemente poderoso y natural para crear programas que sobrevivan a los cambios inevitables que acompañan al crecimiento y mantenimiento de cualquier aplicación. Debido a que cada clase es auto contenida y mantiene interfaces (métodos y propiedades) para su acceso, es posible llevar a cabo sistemas que consten de cientos de ellas interactuando entre sí con mucha facilidad. La orientación a objetos u OOP(Object Oriented Programming) es un conjunto de características adicionales que un lenguaje puede ofrecer, y más concretamente relacionadas a las estructuras de clase. A continuación se resumen algunas estas características: Abstracción Esta característica permite concentrarse en lo que hace, pero no en como lo hace. Mediante esta, es posible usar un objeto conociendo solamente las características que nos interesan del mismo, y manteniendo oculta la forma en la cual lo hace. Lógicamente, esto nos brinda el gran beneficio de la facilidad. Encapsulación Brinda la posibilidad de esconder la implementación, y proveer el acceso a través de una apariencia estándar llamada interfaz. Una interfaz es una colección de métodos y propiedades, aunque en algunos lenguajes se incluye también la definición de eventos de la misma. De esta forma, la clase puede ofrecer una o varias de éstas para interactuar con sus funcionalidades, sin necesidad de exponer directamente su implementación. Esta característica mejora la integración entre aplicaciones, ya que se emplea una forma estándar de utilización. Agregación Hace posible que una propiedad de una clase puede contener otras clases. En general las capacidades de encapsulación y agregación trabajan en conjunto, ya que varias clases podrían estar incluidas dentro de una misma estructura. Herencia Una clase puede heredar las funcionalidades de otra ya existente, con el fin de ganar sus cualidades, y posteriormente adicionar, eliminar y hasta sobrescribir los miembros originales. En algunos lenguajes se permite que una clase pueda heredar las características de varias de ellas, y a esto se le domina herencia múltiple. Polimorfismo El termino significa <<un objeto y muchas formas>>, y hace posible que un método de una clase (función o procedimiento) pueda tener diferentes comportamientos teniendo en cuenta el o los tipos de dato utilizados en su invocación. Una clase llamada Operaciones que contenga un método Suma y que

57

acepte dos argumentos podría ejecutar implementaciones diferentes si es invocada con tipos de datos distintos. Módulos de Clase En Visual Basic .NET la mayoría de los módulos que se utilizan son de clase y, de hecho, cada uno de ellos incluye explícitamente el tipo de archivo al cual corresponde, en vez de efectuarse la vinculación por su extensión. Public Class Cliente End Class Public Class Negocio End Class Esta nueva característica hace posible migrar de un tipo de archivo a otro en forma mucho más sencilla, así como también el utilizar un editor de código externo para implementar las mismas. La nueva sintaxis incluye en el lenguaje la palabra Class, la cual define explícitamente un módulo de este tipo. Se debe también hacer uso de End Class a los efectos de finalizar con el bloque. Asimismo, los módulos estándares de código se crean bajo una estructura parecida, la cual emplea la palabra Module en vez de Class. Module Cliente End Module Así mismo, es posible incluir dentro de un mismo módulo (archivo físico) varias clases, simplemente encerrando la implementación de cada una de ellas dentro de los identificadores de comienzo y fin de bloque vistos anteriormente. A su vez, una clase puede estar partida a través de varios archivos físicos, aunque se vea posteriormente desde el punto de vista programático como una única entidad. Esto último simplifica la manipulación de código cuando varios desarrolladores requieren trabajar con la misma estructura al mismo tiempo. Mediante esta facilidad, cada módulo podría contener un conjunto de métodos de la clase, factibles de ser fusionados posteriormente en el momento de la compilación.

Cliente.vb

Public Class Cliente End Class

Public Class Cliente End Class

Cliente

58

Una vez incluidas las clases al proyecto, es necesario crear una instancia de ella a los efectos de acceder a sus miembros: Dim oCliente as EspaciodeNombres.Cliente() OCliente = New EspaciodeNombres.Cliente() o podría ser también Dim oCliente as New EspaciodeNombres.Cliente() La perdida de la palabra Set trae también algunos cambios en la estructura de los procedimientos de propiedad.

59

Definiendo Propiedades Los miembros de una clase (métodos y propiedades) son la forma en la cual la misma exhibe sus funcionalidades a los demás integrantes. Visual Basic .NET amplía y modifica la sintaxis utilizada en versiones anteriores, con el fin de simplificar la codificación y emplear una aproximación más consistente. Propiedades de Sólo Lectura o Escritura Es una tarea común la de requerir que algunas propiedades sean de sólo lectura o de solo escritura. Para lograr esto en versiones anteriores, se debía implementar únicamente un procedimiento Get o Let, aunque era posible también hacer uso de ambos con diferentes opciones de visibilidad. En Visual Basic .NET no alcanza con ello, ya que se debe indicar en forma obligatoria si se desea tener uno u otro comportamiento. Para ello se cuenta con las palabras ReadOnly o WriteOnly, las cuales deben ser incluidas en el encabezado del bloque, siempre antes de la especificación de visibilidad del mismo. El siguiente ejemplo muestra como declarar una propiedad de nombre MiDato de tipo entero. El bloque Get regresa la variable local (invisible) llamada intMiDato usando la sentencia Return. El bloque Set usa el parámetro que recibe la propiedad y lo asigna a la variable local intMiDato.

Public Class Class2 Private intMiDato As Integer Public Property miDato() As Integer Get Return intMiDato End Get Set(ByVal Value As Integer) intMiDato = Value End Set End Property End Class

Propiedades de sólo Lectura Usted puede crear propiedades de solo lectura usando la palabra ReadOnly cuando declara la propiedad. La propiedad de solo lectura no puede ser usada en una sentencia de asignación. El siguiente ejemplo muestra una propiedad de solo lectura:

Public ReadOnly Property miDato() As Integer Get Return intMiDato End Get End Property

60

Usted no puede usar el bloque Set cuando define una propiedad de solo lectura porque la propiedad no puede ser actualizable. El compilador genera un error si usted coloca esta. Propiedad de sólo Escritura Usted puede crear propiedades de solo escritura usando la propiedad WriteOnly cuando declara la propiedad. La propiedad de solo escritura no puede ser usada para recuperar el valor de la propiedad. El siguiente ejemplo muestra como crear una propiedad de solo escritura: Public WriteOnly Property miDato() Set(ByVal Value) intMiDato = Value End Set End Property

Usted no puede usar el bloque Get cuando define una propiedad de solo escritura, por que la propiedad no puede ser leída. El compilador genera un error si se incluye esta. Usando Propiedades Default Usted puede crear propiedades por default en las clases, usando la palabra default cuando declara la propiedad. El código de la propiedad deberá recibir por lo menos un argumento, y usted deberá especificar si el acceso es de tipo Public, Protected o Friend.

Public Class Class2 Private miArreglo(10) As Integer Default Public Property Item(ByVal i As Integer) As Boolean Get Return miArreglo(i) End Get Set(ByVal Value As Boolean) miarreglo(i) = Value End Set End Property End Class

61

Especificando la Visibilidad a Variables y Procedimientos El tipo de visibilidad de un miembro de una clase establece quién o quiénes podrán, acceder a ella. Generalmente, los niveles de acceso más utilizados son los públicos y privados. Básicamente la técnica consiste en agregar la palabra Public o Private al comienzo de la definición del procedimiento, asegurando o denegando de esta forma el acceso de elementos externos a la misma.

Palabra Definición Public Accesible desde cualquier lugar. Private Accesible solo dentro de ella misma. Friend Accesible para los demás integrantes del proyecto, pero no

fuera del mismo. Protected Asegura que los miembros pertenecientes a la misma podrán

ser visibles solamente por todas aquellas que deriven de ésta, pero no por los demás integrantes.

Protected Friend Es la unión de Protected y Friend. Esto indica que la visibilidad será exclusivamente para todos los elementos que la hereden, así como también para los integrantes del proyecto.

Por ejemplo: Protected Friend Property Contador() As Long Get ' Código para Implementar End Get Set(ByVal Value As Long) ' Código para Implementar End Set End Property A su vez, las mismas pueden ser marcadas a nivel de toda la clase, lográndose así un resultado más modular. Friend Class Class2 End Class

62

Polimorfismo en Propiedades Las características de polimorfismo están también disponibles para ser utilizadas en propiedades. Como vimos anteriormente, cuando se invoca o lee una propiedad, Visual Basic ejecuta en forma automática el bloque Get o Set, teniendo en cuenta la acción solicitada. Esto hace que los mismos puedan ser vistos como un tipo especial de método. Debido a ello, las características de sobrecarga pueden ser también utilizadas en propiedades. Public Class Class2 Private miArreglo(10) As Long Private miArreglo2(10, 10) As Long Public Overloads Property Elemento(ByVal i As Integer) As Long Get Return miArreglo(i) End Get Set(ByVal Value As Long) miArreglo(i) = Value End Set End Property Public Overloads Property Elemento(ByVal i As Integer, ByVal j As Integer) As Long Get Return miArreglo2(i, j) End Get Set(ByVal Value As Long) miArreglo2(i, j) = Value End Set End Property End Class Como podemos apreciar, es posible emplear diferentes comportamientos de acuerdo con los tipos de datos especificados, tanto en métodos como propiedades.

63

Implementación de Métodos Un método es un procedimiento o función definido dentro de una estructura de clase. Estos también han sufrido cambios en sus sintaxis, aunque mucho menor en relación a las propiedades. El encabezado de una función o procedimiento se ve similar a versiones anteriores:

Public Class ClaseDePrueba Public Sub TestIt(ByVal x As Integer) End Sub Public Function GetIt() As Integer End Function End Class

Las funciones pueden emplear la palabra Return para retornar un valor, de forma similar a lo que se hacía en las propiedades. Las características de visibilidad analizadas anteriormente también pueden ser aplicadas a un método, logrando así similares resultados.

64

Métodos Compartidos Los métodos compartidos de una clase también pueden ser indicados como compartidos (Shared), a los efectos de que los mismos puedan ser invocados sin la necesidad de crear una variable objeto. Para ello basta con adicionar la palabra Shared después de la definición de visibilidad del procedimiento o función.

Public Class Conectate Public Shared Function UsrConectado() as string ‘Retorna el usuario conectado End Function End Class

De esta forma el procedimiento estará disponible para ser llamado, sin necesidad de tener que emplear una variable de objeto que contenga a la misma. MsgBox(Conectate.UsrConectado) En una misma clase pueden existir algunos métodos que estén marcados como compartidos y otros no. Adicionalmente, los mismos pueden utilizar similares reglas que aquellos estándares, lo cual simplifica su entendimiento. Ejemplo: Hacer un procedimiento de tipo compartido que incremente una variable privada de la clase cada vez que el mismo sea invocado.

Public Class Cuenta Private Shared Cuenta As Long Public Shared Function Contador() As Long Cuenta += 1 Return Cuenta End Function End Class

Evidentemente, esta solución requiere un poco de conocimiento al respecto. Los métodos compartidos que necesitan acceder a variables de la clase podrán hacerlo exclusivamente si las mismas fueron definidas también como compartidas. Si no se respeta esta regla, Visual Basic generará un error en el momento de la compilación. .

65

Polimorfismo en Métodos El polimorfismo significa que un miembro de una clase (método o propiedad) podrá comportarse de diferentes formas, de acuerdo al o a los tipos de dato utilizados en la invocación del mismo. Para demostrar esta característica crearemos el método Buscar en Visual Basic que busque un registro en una base de datos empleando una condición dada, y que posteriormente retorne el resultado de la misma como una cadena de texto. Lo primero que se debería hacer es conocer los tipos que la función tendría que aceptar:

Nombre de la Función Tipo de Dato de Entrada Tipo de Dato de Salida Buscar String String Integer String Una vez conocidos los mismos, se deberían implementar las funciones necesarias atendiendo a cada una de las situaciones:

Public Class Cuenta Public Function Buscar(ByVal Arg As String) As String End Function Public Function Buscar(ByVal Arg As Integer) As String End Function End Class

Así como se especifico en este ejemplo, es más que suficiente para realizar la invocación de uno u otro método dependiendo de los parámetros utilizados. Existe la opción OverLoads, la cual no es obligatoria y hace posible marcar aquellos procedimientos que ofrecerán diferentes opciones de invocación. Public Class Cuenta Public Overloads Function Buscar(ByVal Arg As String) As String End Function Public Overloads Function Buscar(ByVal Arg As String, ByVal Arg2 As Integer) As String End Function End Class El mismo debe ser posicionado a continuación de la definición de visibilidad del método, y se emplea más que nada con el fin de diferenciar aquellos miembros que brindarán más de una alternativa de uso. A esta característica se le llama Sobrecarga, y está también disponible cuando se requiere la utilización de una cantidad diferente de argumentos de entrada o de salida. Para invocar al Método:

MsgBox(oCuenta.Buscar(“1”,1))

66

Usando Constructores Mediante los métodos y propiedades de una clase es posible interactuar con la implementación de la misma. Sin embargo, en algunas ocasiones se necesita configurar algunos valores por defecto tan pronto como el objeto es creado, y para ello se hace uso de un tipo especial de procedimiento denominado Constructor de Clase. Un constructor de clase es un procedimiento similar a otros, pero que es ejecutado automáticamente cuando se crea una nueva instancia de una clase. Generalmente, se emplea para realizar tareas de inicialización requeridas por el objeto, aunque esto último no es excluyente. Cada constructor de clase se localiza en un procedimiento de evento llamado New definido dentro de la misma.

Public Class Class2 Private iValor As Integer Public Sub New() iValor = 10 End Sub End Class

Constructores Sobrecargados Los constructores sobrecargados permiten que una clase pueda ofrecer distintas formas de inicialización, involucrando diferentes parámetros para realizar dicho proceso. Para ello se emplean las características de sobrecarga de funciones (Polimorfismo) que vimos anteriormente, con el fin de brindar diferentes alternativas a la hora de crear un objeto.

Public Class Class2 Private iValor As Integer Private strValor As String Public Sub New() iValor = 10 End Sub Public Sub New(ByVal i As Integer) iValor = i End Sub Public Sub New(ByVal i As Integer, ByVal sNom As String) iValor = i strValor = sNom End Sub End Class

Emplea el constructor por defecto: Dim cClase as New Class2()

67

Para emplear el constructor sobrecargado o parametrizado: instancia e inicializa un objeto Dim cClase as New Class2(52) Esta técnica es de mucha utilidad para lograr que un objeto se comporte de formas diferentes dependiendo de los valores empleados en su inicialización. Es posible escribir tantos constructores como requiera su clase. Otro punto importante es que los constructores de una clase no pueden ser invocados como métodos estándares (cClase.New() ). Usando Destructores Así como se cuenta con un procedimiento Constructor que se ejecuta tan pronto como un objeto es creado, también se ofrece un tipo especial de procedimiento denominado Destructor. El mismo se ejecuta tan pronto como el recolector de basura libera la memoria ocupada por el objeto y, a diferencia del primero, no es posible sobrecargar el mismo. El siguiente ejemplo cierra la conexión y otros recursos: Protected Overrides Sub Finalize() Con.Close

MyBase.Finalize() End Sub Colección Garbage Dentro de Visual Basic .NET, cuando se activa la referencia de un objeto a Nothing o permitiendo que este salga de su alcance y se remuevan todas las ligas a el objeto, la colección Garbage recuperar el espacio ocupado por estas. Este proceso corre trasfondo y sigue la pista de las referencias de los objetos y destruye los que no van a ser ejecutados, incluyendo los que no son referenciados. Al mismo tiempo que la colección Garbage corre, se ejecuta el destructor (método Finalize). Usted puede forzar que se colecten los objetos que no se usan, usando el método Collect de la clase GC (Garbage Collector) del sistema. Gc.Collect() No obstante, el método Collect no se recomienda usarlo, por que puede redundar en una caída momentánea del rendimiento de la aplicación cuando no exista basura que colectar. Ocasionalmente es apropiado el uso del método Collect cuando usted sabe que ha generado basura, pero esto deberá hacerlo con precaución.

68

Herencia Dentro de Visual Basic .NET, usted puede usar herencia para obtener una clase derivada de una clase base. La clase derivada puede heredar todas las propiedades, métodos miembros de datos, eventos de la clase base con la finalidad de reutilizar el código de la clase base a través de una aplicación. A medida que las aplicaciones han crecido en tamaño y complejidad, la tarea de reutilizar sus partes se hace cada vez más difícil. Indudablemente, la reutilización de código implica que las funcionalidades empleadas por una aplicación puedan ser utilizadas por otra en forma sencilla. El primer punto radica siempre en identificar aquellos módulos o características a ser compartidas, a los efectos de aislarlas, y así definir quién o quiénes formarán parte de la infraestructura común. Sin duda la orientación objetos brinda parte de la solución mediante el manejo de clases, ya que varias implementaciones podrían ser agrupadas y posteriormente tratadas como entidad. Sin embargo, es importante poder contar con alguna característica que permita ganar cualidades de las clases ya existentes, con el fin de modificar o mejorar las mismas, y así ofrecer un nuevo conjunto de características basadas en la primera. Para ello el paradigma de orientación a objetos ofrece una funcionalidad denominada herencia, la cual permite que una clase pueda adquirir las cualidades de otra ya existente, pudiendo –posteriormente- agregar nuevas implementaciones bajo la misma apariencia, o lo que es igual, extenderla. Clase Base Una clase base es aquella que se utiliza como plantilla para definir otras, o dicho de otra forma, aquella de la que derivan otras clases ganando así sus cualidades. En Visual Basic .NET se basa en la utilización de la palabra Inherits (heredada), la cual se debe situar en la zona de declaraciones, y debe incluir el nombre de la clase de quien se desean ganar las características. Inherits <clase o interfaz> Inherits Caja La palabra Inherits permite que una clase derivada solamente incluya aquellos nuevos miembros, o aquellos cuya reimplementación se desea. ‘ Clase Caja

Option Explicit On Public Class Caja Public Function volumen() As Long 'Implementación End Function Public Function Peso() As Long 'Implementación End Function End Class

69

‘Clase Caja de Chocolate

Option Explicit On Public Class CajaDeChocolates Inherits Caja Public Sub Color() 'Implementación End Sub End Class

Al crearse un objeto de tipo CajaDeChocolates, automáticamente se contará con los miembros de la clase Caja, más los que implemente. De hecho, si la clase Caja agregara posteriormente nuevos miembros, ellos se incluirían en CajaDeChocolates automáticamente. Conceptos Utilizados en la Orientación a Objetos La mayor parte de los desarrolladores en Visual Basic desconocen los conceptos que se usan en la programación orientada a objetos, y ello es sencillamente por que el lenguaje no preveía dichas características. Ahora todas ellas están disponibles, por lo que es importante que puedan hacer uso de la misma con precisión. Una clase que se toma como base para una herencia puede ser encontrada de las siguientes formas:

o Clase base. o Superclase. o Clase padre.

A su vez, todas las clases que heredan o ganan las características de una base pueden ser encontradas de las siguientes formas:

o Clase derivada. o Subclase. o Clase Hija.

En términos de notación de modelado de objetos (UML – Universal Modeling Language) la herencia puede ser también encontrada como generalización. Cuando se refiere a propiedades, métodos y eventos en su conjunto, es posible utilizar el término miembros de la clase o miembros, en vez de tener que hacer referencia a ellos en cada caso. Cuando una clase hereda métodos y propiedades de otra que solamente define la estructura pero no tiene su implementación. Se le denomina a la clase base con el nombre de clase abstracta.

70

Palabra NotInheritable Por defecto todas las clases en Visual Basic .NET son factibles de ser heredadas. Sin embargo, en algunas ocasiones puede ser necesario evitar que se realice este proceso. Para ello se cuenta con la palabra NotInHeritable, la cual evita que una clase pueda ganar las características de la misma. <Visibilidad> NotInHeritable Class <Nombre> Option Explicit On Public NotInheritable Class ClasePrueba ‘Implementación End Class Public Class ClaseDerivada Inherits ClasePrueba ‘ Genera un error el compilador

‘Implementación End Class Cuando intente hacer uso de la herencia con una clase que indique esta palabra, Visual Basic generará un error en tiempo de compilación tal como lo demuestra el ejemplo anterior. Palabra MustInherit Es posible construir una variable de objeto basada en casi todas las clases de la infraestructura .NET. Sin embargo, en algunos casos puede requerirse el restringir esta funcionalidad, a los efectos de que solamente sea utilizada si se hace a través de una clase derivada. Para ello se ofrece la palabra MustInherit, la cual obliga a que la misma deba ser heredada por otra para que pueda hacerse uso de sus miembros. <Visibilidad> MustInHerit Class <Nombre>

Option Explicit On Public MustInherit Class Caja Public Function volumen() As Long 'Implementación End Function Public Function Peso() As Long 'Implementación End Function End Class

‘ Esta Línea Genera un error

Dim C As New Caja() El error ocurre, por que esta clase no se puede usar directamente.

71

Visibilidad en la Herencia Las clases pueden heredar las propiedades o métodos de otra dependiendo de la visibilidad que los miembros ofrezcan. Existen cinco tipos de visibilidad diferentes, dos de los cuales son aplicables exclusivamente a la herencia: Protected ProtectedFriend Private Public Friend Protected Tiene utilidad cuando se hace uso de la herencia. La misma permite que un miembro se haga visible para aquellas clases derivadas que ganen sus características, pero no puedan ser invocadas directamente por otros. Protected sub Volumen() Si intenta llamar a un método que utiliza este tipo de visibilidad, obtendrá un error al momento de la compilación. Friend En algunas ocasiones es necesario que un conjunto de características sean ofrecidas para otros miembros del proyecto, pero no así para elementos externos al mismo. Esto es muy útil cuando se busca que una estructura contenida en un ensamblado sea brindada al mismo, pero no así a aquellas entidades externas. Para ello se debe hacer uso de esta operación de visibilidad. Friend Sub Volumen() No es posible usar Friend en conjunto con Private o Public.

X

Y Z

H E R E N C I A

72

Protected Friend Esta forma ofrece menores restricciones, ya que justamente conjuga las características de Protected y Friend. El mismo permite que un miembro pueda ser visible por todas las clases que integran al proyecto, pero también por las que la hereden. La visibilidad también puede ser especificada a nivel de módulo de clase, a los efectos de que se aplique a todos los miembros del mismo. Para ello, basta con indicar éste al comienzo del bloque de la clase

Option Explicit Public Class Caja Function Volumen() as Long ‘Implementación End Function Function Peso() as Long

‘Implementación End Function End Class

73

Sobrescribir y Sobrecargado

o Una clase derivada puede sobrescribir una propiedad o método Overridable. Se puede sobrescribir MustOverride. Se debe sobrescribir dentro de una clase derivada Overrides. Remplaza al método que fue heredado NotOverridable. No se puede sobrescribir (predeterminada)

o Use la palabra Overload para sobrecargar una propiedad o método heredado.

Sobrescribiendo Cuando una clase derivada es heredada de una clase base, esta hereda todos las funciones, subrutinas, y propiedades de la clase base, incluyendo cualquier implementación dentro de los métodos. Ocasionalmente usted puede implementar código para su clase derivada en vez de usar el método heredado. Overridable Para crear una implementación especial en la clase derivada, especifique la palabra Overridable en la definición del método de la clase base, como se muestra en el siguiente ejemplo: Public Overridable Sub metodoSobreEscri() MsgBox("Método Base de Bobrescritura") End Sub MustOverride Para crear un método en una clase base que se deba sobrescribir dentro de todas las clases derivadas, define el método con la palabra MustOverride. Solo el prototipo del método puede ser creado dentro de la clase base, pero no el código de su implementación. Usted solo puede usar esta palabra en la clase base que esta marcada como MustInherit. El siguiente ejemplo muestra como definir esto:

Public MustOverride Sub AccionPorDefinir() Overrides Para especificar que la clase derivada va a sobrescribir la implementación de un método de la clase base, use la palabra Overrides. Para que esto se pueda hacer la clase base deberá tener definido dicho método como Overridable. El siguiente ejemplo muestra como se debe definir el método en la clase derivada: Public Overrides Sub metodoSobreEscri() MsgBox("Método derivado de uno de Sobrescritura") End Sub

74

Ejemplo:

Option Explicit On Public MustInherit Class ClaseBase Public Overridable Sub metodoSobreEscri() MsgBox("Método Base de Sobrescritura") End Sub Public Sub Otro() MsgBox("Otro Método de la Clase Base de No Sobrescritura") End Sub End Class Public Class ClaseDerivada1 Inherits ClaseBase Public Overrides Sub metodoSobreEscri() MsgBox("Método derivado de uno de Sobrescritura") End Sub End Class

NotOverridable En algunos casos puede ser necesario que un elemento que rescribe otro anule la posibilidad a futuros miembros de que implementen una nueva versión del mismo. Para ello debe utilizar la palabra NotOverridable en conjunto con Overrides.

Option Explicit On Public MustInherit Class ClaseBase Public MustOverride Sub ProCalcula() Public Overridable Sub metodoSobreEscri() MsgBox("Método Base de Sobrescritura") End Sub Public Sub Otro() MsgBox("Otro Método de la Clase Base de No Sobrescritura") End Sub End Class Public Class ClaseDerivada Inherits ClaseBase Public NotOverridable Overrides Sub procalcula() MsgBox("Implementación derivado") End Sub Public Overrides Sub metodoSobreEscri() MsgBox("Método derivado de uno de Sobrescritura") End Sub Public Overloads Sub Otro(ByVal i As Integer) MsgBox("Método Otro Sobrecargado") End Sub End Class

75

Código para invocar a los métodos de la clase derivada: Dim x As New ClaseDerivada() x.Otro() 'Despliega <Otro Método de la Clase Base de No Sobrescritura> x.Otro(20) 'Despliega <Método Otro Sobrecargado> x.metodoSobreEscri()'Despliega <Método derivado de uno de Sobrescritura> x.ProCalcula() 'Despliega <Implementación derivado>

76

Shadowing Una clase derivada, puede sobrescribir o ocultar un método de la clase base. Shadowing efectivamente oculta el método de la clase base, basándose solo en el nombre del método.

Public Class aBase Public Sub M1() ' El método no se puede sobrescribir x default '... End Sub End Class Class aEjemploShadow Inherits aBase Public Shadows Sub M1(ByVal i As Integer) ' Solo se puede ver este método End Sub End Class

Invocación: Dim x As New aEjemploShadow() x.M1() ' Genera un Error x.M1(20) ' Sin Error

77

Usando la palabra MyBase Muchas veces la implementación de un método requiere la llamada a la funcionalidad implementada por la clase base. Usted puede usar la palabra MyBase para acceder de inmediato a la clase base desde una clase derivada que fue heredada de esta. Cuando usa Mybase, usted deberá ser consiente de algunas limitaciones:

o Esta solo referencia directamente a la clase base de la jerarquía. o Esta permite acceder a todos los miembros de la clase derivada que sean:

Public, Protected, o Friend. o Esta no es un objeto real, usted no podrá asignar MyBase a una variable.

El siguiente ejemplo muestra como usar la palabra MyBase para ejecutar un método implementado en la clase base:

Public Class ClaseDerivada Inherits ClaseBase Public Sub New() MyBase.New() ' Llama al constructor de la clase base End Sub Public Overrides Sub metodoSobreEscri() MsgBox("Método derivado de uno de Sobrescritura") MyBase.metodoSobreEscri() ' Llama al método original End Sub End Class

78

Usando la Palabra MyClass Usted puede usar la palabra MyClass para ejecutar la implementación de un método localizado dentro de la misma clase. Cuando usa MyClass, usted deberá ser consiente de algunas limitaciones:

o Esta permite acceder a todos los miembros de la clase derivada que sean: Public, Protected, o Friend.

o Esta no es un objeto real, usted no podrá asignar MyClass a una variable. El siguiente ejemplo muestra como llamar a un método de la clase base desde una clase derivada, usando MyClass:

Option Explicit On Public Class ClaseBase Public Overridable Sub metodoSobreEscri() MsgBox("Método Base de Sobrescritura") End Sub Public Sub Otro() MyClass.metodoSobreEscri() End Sub End Class Public Class ClaseDerivada Inherits ClaseBase Public Overrides Sub metodoSobreEscri() MsgBox("Método derivado de uno de Sobrescritura") End Sub End Class

Invocación: Dim x As New ClaseDerivada() x.Otro()' Despliega “Método Base de Sobrescritura”

79

Implementando Interfaces Una interfaz es una colección de prototipos y propiedades. Como ya definimos es posible crear una clase que solamente definiera el conjunto de miembros, sin implementar cada uno de ellos. A este tipo de clase se le llama Clase Abstracta, y era utilizado como una plantilla para crear nuevas clases que tuviesen que seguir ciertos patrones predefinidos. Una vez creada la clase abstracta, bastaba con heredar la misma para adquirir su estructura. Visual Basic .NET cuenta con una nueva aproximación, que hace posible definir un conjunto de miembros (métodos, propiedades y eventos) los cuales posteriormente serán utilizados por las demás clases a modo de plantilla. La metodología ofrecida es bastante sencilla, ya que basta con conocer las básicas de esta aproximación para poder utilizarla. La definición de una interfaz es similar a una clase estándar, pero con la diferencia de que ninguno de los miembros puede contener implementación, así como tampoco puede contener operadores de visibilidad, los cuales deberá, ser posteriormente provistos por la clase. El siguiente ejemplo muestra como se define una interfase que incluye tres métodos, dos de los cuales están sobrecargados.

Interface iMiInterfase Function Metodo1(ByRef s As String) As Boolean Sub Metodo2() Sub Metodo2(ByVal I As Integer) End Interface

Una vez realizada la declaración, la interfaz quedará disponible para ser empleada por otras clases. Para hacer uso de la misma, basta con definir en la primera línea de la clase la palabra Implements, seguida del nombre de la interfaz. Posteriormente, cada miembro de ésta deberá implementar necesariamente cada procedimiento, función o evento definido en la primera. A su vez, cada elemento debe adicionar al final de su declaración el miembro con el cual mantiene vinculación a través de la palabra Implements. En el caso de que no se implemente uno de los mismos, Visual Basic generará un error en el momento de la compilación. Otra opción es que la interfaz incluya declaraciones de propiedades y eventos, así como las diferentes opciones de métodos: El siguiente ejemplo muestra como implementar el polimorfismo en Visual Basic .Net. Al examinar el código, note lo siguiente:

o La interfase iPerson define dos miembros: UltimoNombre y Despliega o La clase Empleado implementa la interfase y sus dos miembros. o Usando la palabra implements para cada miembro individual, usted puede

especificar el nombre propietario para el método y cual será ejecutado si la aplicación cliente invoca al nombre original de la interfase.

80

Interface IPerson Property UltimoNombre() As String Sub Despliega() End Interface Public Class Empleado Implements IPerson Private strNombre As String Private strCompañia As String Private Property UltimoNombre() As String Implements IPerson.UltimoNombre Get Return strNombre End Get Set(ByVal Value As String) strNombre = Value End Set End Property Public Property Compañia() As String Get Return (strCompañia) End Get Set(ByVal Value As String) strCompañia = Value End Set End Property Public Sub Despliega() Implements IPerson.Despliega MsgBox(UltimoNombre & " " & Compañia, , "Empleado") End Sub End Class Ejemplo de código del cliente: El siguiente código muestra como la clase Empleado y la interfase IPerson podrán ser usadas dentro de la aplicación del cliente. Dim Person As IPerson, Emp As New Empleado() Emp.Compañia = "Microsoft" Person = Emp Person.UltimoNombre = "Jones" Person.Despliega() ' Llama al método despliega sobre la interfase Emp.Despliega() ' Método Despliega esta definido como public En el caso de que uno o varios procedimientos no necesiten ser implementados, es recomendable que genere un error cuando éste sea invocado, a los efectos de indicarle al usuario que el mismo no se encuentra disponible.

81

Implementación de Eventos Un evento es la forma que utiliza un objeto para comunicarse con quien lo está manipulando. Una clase puede iniciar tantos eventos como desee, pero antes debe definirlos. La definición de los mismos se realiza en la zona general de una clase, y tiene cierta similitud con la declaración de un procedimiento, salvo que la misma no incluye la finalización del bloque ni su implementación. Sintaxis <visibilidad> MustInHerit Class <Nombre> <visibilidad> Event <Nombre>(Args,...) as <Tipo>

Public Class Caja Public Event SeAbreCaja(ByVal TipoApertura As Long) Public Function Metodo1() As Long 'Implementación End Function Public Function metodo2() As Long 'Implementación End Function Public Sub AbrirCaja() ' Ese método es utilizado para abrir la caja End Sub End Class

Al igual que en versiones anteriores, para definir un evento se utiliza la palabra Event seguida por el nombre del mismo y sus argumentos. Generalmente, los mismos son declarados como públicos, con el fin de que ellos puedan ser visibles por otras clases residentes dentro de la misma aplicación u otro ensamblado. Posteriormente la implementación debe iniciar el evento, lo cual se efectúa mediante la palabra RaiseEvent seguida del nombre del mismo. El siguiente método, por ejemplo, iniciará el evento SeAbreCaja

Public Sub AbrirCaja() RaiseEvent SeAbreCaja(10) End Sub

En Visual Basic existen dos formas mediante las cuales es posible capturar los eventos generados por una clase desde el exterior:

- Utilizando la palabra WithEvents (con eventos). - Haciendo uso de delegados.

Como la primer técnica se contaba en versiones previas, mientras que la segunda es una nueva característica de esta versión.

82

Cuando se utiliza la palabra WithEvents en la declaración de una variable, Visual Basic inspecciona la clase en el momento de la definición para ver si contiene eventos, y en caso afirmativo los incluye en la ventana de código en forma similar a otros objetos. Esto hace que pueda hacer uso de las características estándares para realizar la captura del mismo, pudiéndose en forma sencilla escribir código para el evento.

Public WithEvents miCaja As New Caja() Como un evento debe ser visible desde los miembros del módulo, la variable debe ser definida a nivel modular, no pudiéndose utilizar este operador con elementos locales. Public Sub miCaja_seAbrecaja(ByVal TipoApertura As System.Int64) _ Handles miCaja.SeAbreCaja MsgBox("Usted ha abierto la caja ") End Sub La segunda forma es aún más flexible, ya que permite asociar un procedimiento a un evento en tiempo de ejecución mediante la utilización de delegados. Los delegados ofrecen la posibilidad a Visual Basic de asociar un procedimiento a uno o más eventos. Existen varias formas de realizar esta tarea, pero la más fácil es la de hacer uso de la palabra AddHandler. El primer paso es escribir el procedimiento que se desea que se ejecute cuando se inicia un evento: Public Sub miCaja_seAbrecaja(ByVal TipoApertura As System.Int64) MsgBox("Usted ha abierto la caja ") End Sub A continuación se debe establecer la relación entre éste y el evento de la siguiente forma: AddHandler MiCaja.SeAbreCaja, AddressOf Me.SeAbrioCaja

El Evento se asociará a este Procedimiento Esta última hace posible a Visual Basic obtener una referencia al mismo. Cuando el evento es enlazado, automáticamente se ejecutará las implementaciones asociadas. Veamos como quedaría: Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim micaja As New Caja() AddHandler micaja.SeAbreCaja, AddressOf Me.miCaja_seAbrecaja End Sub

83

En resumen los pasos a seguir deben ser los siguientes:

1. Definir e instanciar una variable de objeto de la clase. 2. Asociar el evento del objeto a un procedimiento mediante la palabra

AddHandler. Recuerde que los argumentos del procedimiento asociado deben ser iguales a los del evento declarado en la clase. Por otra parte, es posible asociar varios procedimientos a un evento con tan solo agregar más de una línea AddHandler Código de la clase Caja: Public Class Caja Public Event SeAbreCaja() Public Function Metodo1() As Long 'Implementación End Function Public Function metodo2() As Long 'Implementación End Function Public Sub AbrirCaja() RaiseEvent SeAbreCaja() End Sub End Class Código de la forma para ligar un procedimiento al evento: Public Class Form1 Inherits System.Windows.Forms.Form

Public Sub Abriendo() MsgBox("Usted ha abierto la caja ") End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim C As New caja() AddHandler C.SeAbreCaja, AddressOf Me.Abriendo C.AbrirCaja() End Sub End Class

84

Módulo 4: Uso de Windows Forms ¿Por qué usar Windows Forms? El auge de las aplicaciones de Internet ha llevado a que muchas empresas realicen sus aplicaciones para funcionar exclusivamente bajo este paradigma, y más específicamente utilizando http y un explorador como forma de comunicación y presentación de las diferentes pantallas. Las mismas ofrecen un bajo costo de actualización, ya que la lógica principal se ejecuta en el servidor, y solamente las pantallas y algo de código Script viaja al cliente. Esto ofrece una flexibilidad importante, visto que cualquier sistema operativo capaz de utilizar un explorador y conectarse a Internet es un potencial consumidor de la aplicación. Lamentablemente, muchas empresas evalúan el costo que este tiene para el usuario, debido a que las interfaces graficas bajo este modelo ofrecen menor potencia y control que la misma aplicación utilizando formularios de Windows. Así una aplicación para Windows gana todas las funcionalidades nativas ofrecidas por el sistema operativo. Desde el punto de vista del usuario, una aplicación de este tipo brinda un conjunto de facilidades mayores, lo que hace más confortable y agradable la apariencia. Como desventaja, la empresa debe asumir los costos de distribución y actualización del producto. Usando Windows Forms El uso de formularios es similar al de Visual Basic 6, pero se le agregaron nuevas propiedades, métodos y eventos. Como consecuencia de la utilización de la nueva biblioteca para gestionar la interfaz gráfica, los formularios han ganado algunas características, así como también han perdido o modificado el funcionamiento o la forma de acceso a otras tantas. A simple vista, un formulario tiene apariencia similar a versiones anteriores, pero, en esencia, encierra cambios estructurales importantes que pueden impactar sobre los desarrolladores existentes. Por ejemplo esta versión utiliza la propiedad Text en características de sólo lectura o lectura/escritura en todos los objetos, ya sean formularios, cajas de texto o etiquetas. Label1.Text = “Nombre del Cliente: ” Form1.Text = “Catálogo de Clientes” Text1.Text = “Administrador del Sistema” Tab Order Cuando se dibujan los diferentes controles sobre un formulario en tiempo de diseño, el flujo a seguir por el tabulador en tiempo de ejecución viene determinado por el valor de la propiedad TabIndex. Esta tarea era tediosa, pero afortunadamente ahora existe una nueva herramienta que facilita la misma. Para

85

ello, después de dibujar los controles sobre el formulario basta con activar la opción Orden de Tabulación (Tab Order) del menú Ver, el cual permite hacer esta tarea en forma grafica. DialogResult Cuando se invoca al método show de un formulario una nueva ventana es exhibida, brindando así la posibilidad de que el usuario interactúe con la misma. Existe un caso particular, y es cuando la ventana que exhibe el formulario espera una respuesta de esta ultima. Para tal fin, la primera debe bloquear el acceso a las demás ventanas de la aplicación hasta que la exhibida sea cerrada. De esta forma, el formulario invocador quedará a la espera, y luego continuará ejecutando su código o evaluando la respuesta del mismo. Para lograr dicho efecto, los formularios cuentan con una propiedad denominada ShowDialog.

MiSegundaForma.ShowDialog() Show Abre una ventana y se continua ejecutando el código de la misma. ShowDialog Abre una ventana, bloqueando la aplicación hasta que esta ultima

sea cerrada. Código de la forma que abre la forma con ShowDialog: Dim miForma As New Form3() miForma.ShowDialog() If miForma.DialogResult = DialogResult.Yes Then MsgBox("Yes") End If miForma.Dispose() Código de la forma para regresar la acción efectuada por la forma: Me.DialogResult = DialogResult.Yes Me.Close() Font La propiedad Font de las formas de Windows varia ligeramente de las de la versión anterior. Los controles de la forma heredan Font.BackColor y Font.ForeColor del control padre. Si el font no es activado para el control, entonces hereda el font del padre. Esto permite que usted pueda cambiar el font de la forma, y todos los controles sobre ella cambiaran al nuevo font en forma automática.

86

Opacity La gestión de trasparencias en Visual Basic se realiza en forma fácil, ya que se brinda el soporte necesario para esta característica mediante propiedades. Un formulario puede ser tan trasparente u opaco como usted desee. Para implementar este tipo de característica, Visual Basic cuenta con la propiedad de tipo doble llamada Opacity, la cual hace posible modificar la trasparencia de un formulario. Ésta indica que tan trasparente u opaca será la ventana.

Me.Opacity = 0.5 Las características de trasparencia de la plataforma .NET son aun más flexibles, ya que brindan la posibilidad de definir zonas totalmente trasparentes, sin importar la opacidad del resto del formulario. Para lograr esta característica debemos especificar en la propiedad TrasparencyKey el color a tomar como trasparente, el color puede ser cualquiera de la gama, aunque se recomienda emplear aquellos no utilizados frecuentemente por controles o imágenes. Cuando se especifica el mismo como color trasparente, todo elemento dentro del área del formulario que contenga a éste adquirirá de forma automática trasparencia total. Sólo puede haber un color de este tipo por formulario.

Me.TransparencyKey = Color.Black MaximumSize y MinimumSize Estas dos propiedades permiten que usted defina el tamaño máximo y el tamaño mínimo de la forma en tiempo de ejecución. Los tipos de datos de tamaño, son la propiedad Height y la propiedad Width para definir el tamaño total de la forma. El siguiente ejemplo muestra como usar estas propiedades: Dim MaximoTamano As New Size() Dim MinimoTamano As New Size() MaximoTamano.Height = 500 MaximoTamano.Width = 500 MinimoTamano.Height = 200 MinimoTamano.Width = 200 Me.MaximumSize = MaximoTamano Me.MinimumSize = MinimoTamano

87

TopMost Los formularios flotantes son de mucha utilidad cuando se desea que una ventana prevalezca sobre cualquier otra, o incluso sobre otras aplicaciones. A este tipo de formularios se les denomina Flotantes. Una aplicación puede tener más de una ventana flotante. Para indicar que un formulario debe ser de este tipo se debe hacer uso de la propiedad TopMost. La misma puede ser modificada tanto en tiempo de diseño como en tiempo de ejecución. Private Sub Button1_Click(… Me.TopMost = Not Me.TopMost If Me.TopMost = True Then Me.Text = "Forma Flotante" Else Me.Text = "Forma Estándar" End If End Sub La característica de ventana flotante se utiliza cuando se desea representar un cuadro de herramientas disponible para todas las ventanas de la aplicación. AcceptButton y CancelButton Las propiedades AcceptButton y CancelButton de las formas de Windows permiten que usted defina cual botón será activado la tecla de Enter y Esc sean presionadas. El siguiente ejemplo muestra como especificar su botón de Ok y de Cancel usando las propiedades AcceptButton y CancelButton: Me.AcceptButton = Button1 Me.CancelButton = Button2 Usando los Métodos de la Forma Close Este método es similar al de Visual Basic 6 Unload. Usted puede usar este método para cerrar la forma actual y liberar los recursos retenidos.

Me.Close Show y ShowDialog Usted puede usar estos métodos para desplegar una forma sobre la pantalla. El método Show despliega la forma activando la propiedad Visible en true. El método ShowDialog muestra la forma como caja de dialogo modal.

88

El siguiente ejemplo muestra como desplegar una caja de dialogo en forma modal y como usar la propiedad DialogResult para determinar la acción que debe tomar: Dim miForma As New Form3() miForma.ShowDialog() If miForma.DialogResult = DialogResult.OK Then MessageBox.Show("Procesando") ElseIf miForma.DialogResult = DialogResult.Cancel Then MessageBox.Show("Cancelando") End If miForma.Dispose() Usando Eventos de la Forma Activated y Deactivate El evento Activated es invocado cuando la forma es activada por el código o por la interacción del usuario, y el evento Deactivate es invocado cuando la forma pierde el foco. El siguiente ejemplo muestra como usar el evento Activated, seleccionando el contenido de un textBox: Private Sub Form3_Activated(ByVal sender… TextBox1.Focus() TextBox1.SelectAll() End Sub Closing Este evento es similar al evento unload de Visual Basic 6, este evento se dispara cuando la forma empieza a cerrarse y permite que usted cancele el cerrado a través del argumento CancelEventArgs. El siguiente ejemplo muestra como usar el evento Closing, consultando si el usuario quiere realmente cerrar la forma: Private Sub Form3_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing If MessageBox.Show("Quiere realmente que se cierre la forma", "Cerrando", MessageBoxButtons.YesNo, MessageBoxIcon.Information) = DialogResult.Yes Then e.Cancel = False Else e.Cancel = True End If End Sub

89

Closed El evento Closed ocurre después del evento Closing, pero antes del método Dispose de la forma. Usted puede usarlo para preguntar si la información de la forma desea salvarla. El siguiente ejemplo muestra como usar el evento Closed para cargar información en una variable global: Private Sub Form3_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed strNombre = "Alex Lora" MessageBox.Show("Despues del closing") End Sub MenuStart y MenuComplete Estos dos eventos son invocados cuando el menú recibe y pierde el foco. Usted puede usar este evento para activar las propiedades de los elementos del menú, por ejemplo la propiedad Checked o Enabled. El siguiente ejemplo muestra como habilitar y deshabilitar los elementos de menú, basándose en el tipo de control que tiene el foco en la forma: If TypeOf (ActiveControl) Is TextBox Then mnuCut.Enabled = True mnuCopy.Enabled = True Else mnuCut.Enabled = False mnuCopy.Enabled = False End If Creando Formas MDI (Multiple-Document Interface) Una ventana MDI es aquella capaz de contener otras. Las ventanas hijas son aquellas albergadas y manipuladas obligatoriamente dentro del espacio grafico de la ventana principal. Adicionalmente, cualquier acción sobre la ventana principal puede afectar a las contenidas o hijas. IsMdiContainer() as Boolean Usted puede usar la propiedad IsMDIContainer de la forma para crear una forma MDI padre. Esta propiedad acepta un valor booleano. Ella puede ser configurada en tiempo de diseño o en tiempo de ejecución.

90

El siguiente ejemplo muestra como especificar que una forma será de tipo MDI y maximice su tamaño para facilitar su uso. Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.IsMdiContainer = True Me.WindowState = FormWindowState.Maximized End Sub Creando Formas MDIChild (hijas) La existencia de una ventana principal no predestina a que los demás formularios de la aplicación sean considerados hijos de forma automática. Ello debe efectuarse explícitamente, y para esto debe informar quién es su padre. Usted puede crear una forma hija, activando la propiedad MDIParent de la forma, con el nombre de la forma MDI padre. El siguiente ejemplo como crear una forma MDI Child. Dim Doc As Form4 = New Form4() contador += 1 Doc.MdiParent = Me Doc.Text = "Forma Hija " & contador Doc.Show() Acceder a las Formas Hijas desde la MDI Es común el usos de menús en una forma MDI padre, para manipular partes de una forma MDI Child. Para hacer esto usted necesita determinar que forma hija es la que se encuentra activa en un instante determinado. La propiedad ActiveMDIchild de la forma padre identifica esto. El siguiente código muestra como cerrar una ventana hija desde la Forma MDI padre: Private Sub MenuItem5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItem5.Click Me.ActiveMdiChild.Close() End Sub

91

Arreglo de las Formas Child Usted puede usar el método LayOutMdi de la forma padre para cambiar el arreglo de las formas hijas dentro de la ventana principal. Este método toma un parámetro como se puede ver en el siguiente código: Me.LayoutMdi(MdiLayout.ArrangeIcons) Me.LayoutMdi(MdiLayout.Cascade) Me.LayoutMdi(MdiLayout.TileHorizontal) Me.LayoutMdi(MdiLayout.TileVertical) Habilitando Lista de Ventanas En versiones previas de Visual Basic, usted podía activar la propiedad Window List del menú para crear la lista formas hijas desplegadas en la ventana principal. En Visual Basic .NET, usted puede acceder a la misma funcionalidad activando la propiedad MdiList del encabezado del menú que desee. Usando Cajas de Dialogo Estándar

♦ Msgbox ♦ MessageBox ♦ InputBox

Msgbox La tradicional función MsgBox usada por los desarrolladores, se encuentra incluida dentro de .NET FrameWork. Usted puede usar la misma sintaxis que usaba en versiones anteriores, excepto que usted define el estilo de la caja, usando la enumeración MsgBoxStyle y el resultado de la decisión con la enumeración MsgBoxResult. El siguiente ejemplo muestra como usar la función MsgBox: If MsgBox("¿Continuamos?", _ MsgBoxStyle.YesNo + MsgBoxStyle.Question, _ "Pregunta") = MsgBoxResult.Yes Then ' Implementación End If Clase MessageBox Dentro de .NET Framework, usted puede usar la clase MessageBox para desplegar un simple mensaje en una caja de dialogo. Esta provee un método show y constantes enteras, para controlar el estilo de desplegado de la caja de

92

dialogo. Usted puede comparar el resultado de una decisión del usuario usando la enumeración System.Windows.Forms.DialogResult, como a continuación se muestra: If MessageBox.Show("¿Continuamos?", "Pregunta", _ MessageBoxButtons.YesNo, _ MessageBoxIcon.Question) = DialogResult.Yes Then ' Implementación End If InputBox La función InputBox se encuentra soportada en Visual Basic .NET y no tiene cambios con respecto a la versión anterior.

93

Creación de Menús En Visual Basic .NET el proceso de creación de menús es muy diferente al de Visual Basic 6.0. Usted puede crear más de un menú por forma, el cual crea menús dinámicos de una manera sencilla y puede crear ContextMenus directamente. Existen tres clases principales que deberá usar en la creación de menús: MainMenu Use la clase MainMenu para crear una barra de menú estándar para Windows, en la parte superior de la forma. ContextMenu Use la clase ContextMenu para definir un menú Pop-Up asociado con un control particular. MenuItem Use la clase MenuItem para definir los elementos del menú para un MainMenu o para un ContextMenu Creando Menús a Tiempo de Diseño El control MainMenu es del grupo de controles invisibles en tiempo de ejecución, por lo que será agregado a la ventana en forma separada del mismo, pero manteniendo en todo momento su relación con éste. Una vez que soltamos el objeto sobre el formulario, Visual Basic nos sugerirá la primera descripción para el primer elemento del menú, bajo el texto <<Escriba aquí>>. A su vez, podremos crear un elemento hijo (submenú) del menú principal, o uno hermano del mismo. Basta con que hagamos clic en la posición deseada para que el nuevo elemento sea creado.

94

En forma automática, el control de menú asocia cada elemento a un procedimiento clic, el cual será ejecutado al seleccionar el mismo. Para acceder al procedimiento en tiempo de diseño, basta con hacer doble clic sobre el elemento deseado y escribir el código. Private Sub MenuItem2_Click (ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItem2.Click End Sub El nombre a utilizar programáticamente por el mismo puede ser modificado mediante la propiedad Name, ya que por defecto se asigna el texto MenuItem seguido de un número correlativo. De esta forma, cada elemento que se agregue al menú será considerado un objeto independiente, pero relacionado con la estructura jerárquica. Veamos algunas de las propiedades disponibles en los elementos del menú:

Propiedad Descripción Checked Indica si el elemento estará marcado con un tick. DefaultItem Cuando se indica que un elemento es el por defecto, el texto del

menú será marcado en negrita, y cuando el usuario haga doble clic sobre un submenú que contenga elementos, el mismo será ejecutado por defecto.

Enabled Indica si el elemento estará habilitado o deshabilitado. MDIList Esta propiedad es sólo aplicable cuando se utiliza el modelo de

interfaces de documento múltiple (MDI), e indica que se deberán listar los nombres de las ventanas abiertas.

MergeOrder Indica el orden con que los elementos fusionados serán mostrados.

MergeType Cuando un menú es fusionado con otro, esta propiedad establece cómo debe ser tratado el primero.

RadioCheck Esta propiedad se utiliza en conjunto con Checked, e indica si en vez de utilizar un tick se utilizará un círculo.

ShowShortCut Indica si se deberán mostrar las teclas de acceso rápido. ShortCut Especifica la combinación a utilizar de acceso rápido (por

ejemplo F3, Ctrl.+G). Text Contiene el caption del menú. Visible Indica si el elemento estará visible o no visible. Ahora los formularios hijos también pueden contener sus propios menús, e integrarse en forma automática al de la ventana principal en el elemento en que el usuario abra la primera. De esta forma, el formulario MDI puede ganar elementos del menú hijo mientras dicha ventana esté activa.

95

Creación de un Menú a Tiempo de Ejecución Usted puede adicionar o editar menús a tiempo de ejecución asiendo uso de las clases MainMenu, ContextMenu y MenuItem. Cada una de estas clases contiene la colección MenuItem que cuenta con los métodos Add y Remove. El siguiente ejemplo muestra como crear menús en forma dinámica: Dim mnuMain As New MainMenu() Dim mnuItem1 As New MenuItem() Dim mnuItem2 As New MenuItem() mnuItem1.Text = "Catalogos" mnuMain.MenuItems.Add(mnuItem1) mnuItem2.Text = "Salir X" mnuMain.MenuItems(0).MenuItems.Add(mnuItem2) AddHandler mnuItem2.Click, AddressOf Cerrar Menu = mnuMain La vinculación dinámica entre un evento y un procedimiento se realiza mediante una nueva funcionalidad brindada por la plataforma .NET denominada delegados. El primer paso consiste en escribir una rutina que acepte como argumentos de entrada los parámetros estándares de un procedimiento de evento Visual Basic (el cual consta de dos argumentos), y luego agregar su implementación: Public Sub Cerrar(ByVal sender As System.Object, ByVal e As System.EventArgs) Me.Close() End Sub El nombre del mismo puede ser cualquiera, pero los argumentos de entrada deben ser del tipo que aquí se exhibe. A continuación se debe escribir el código necesario para enlazar el evento clic del elemento con el procedimiento previamente escrito. AddHandler(<Evento a relacionar> as event, AddressOf <nombre del procedimiento>) La palabra AddHandler hace posible relacionar un procedimiento con un evento de un objeto. El primer argumento indica el nombre y evento del mismo, y luego se debe especificar el nombre del procedimiento que se ejecutará cuando este sea lanzado. La palabra AddressOf es obligatoria, y le permite a la función obtener una referencia a este último. Esto sirve para que cuando el usuario haga clic sobre el elemento creado en forma dinámica se ejecute el procedimiento Cerrar.

96

Menús de Contexto Otro tipo de menús comúnmente empleados en interfaces de Windows son aquellos sensibles al contexto. Generalmente, se accede a ellos mediante clic derecho sobre un control o zona de la ventana, y habitualmente representan acciones específicas del objeto. El proceso para crear un menú contextual es parecido al de crear un menú estándar, salvo que se utiliza un control llamado ContextMenu en vez de MainMenu. El mismo debe ser arrastrado y soltado sobre el formulario como se vio anteriormente, o creado a tiempo de ejecución como a continuación se muestra: Dim mnuMainC As New ContextMenu() Dim mnuItemC1 As New MenuItem() Dim mnuItemC2 As New MenuItem() mnuItemC1.Text = "Salvar" mnuMainC.MenuItems.Add(mnuItemC1) mnuItemC2.Text = "Cerrar" mnuMainC.MenuItems.Add(mnuItemC2) AddHandler mnuItemC2.Click, AddressOf Cerrar Me.ContextMenu = mnuMainC En este caso al hacer clic derecho sobre la forma, se desplegara el menú contextual con las opciones Salvar y Cerrar.

97

Herencia de Ventanas Las ventanas son clases que heredan sus características de la biblioteca de formularios de Windows (Windows Forms), por lo que es posible aplicar todas aquellas características que aprendimos a lo largo de este capitulo (incluso la herencia). Un ejemplo de reutilización podría ser el de una ventana que solicitase al usuario su nombre y clave, y que posteriormente hiciese uso de distintas implementaciones de acuerdo con el sitio donde fuese empleada. Por supuesto que para demostrar esta característica vamos a crear un nuevo proyecto de Biblioteca de clases, al cual llamaremos LibClases, y posteriormente agregaremos un formulario con los siguientes controles, en forma similar a la siguiente figura:

Debido a que no haremos uso de la clase creada por defecto por la plantilla, la eliminaremos del proyecto, haciendo clic derecho sobre ésta y seleccionando Excluir del proyecto. Ahora vamos a agregar código al botón Cancelar, el cual simplemente cerrará la ventana. Private Sub Cancelar_Clic (... Me.Close() End Sub Por último, vamos a compilar la aplicación, lo que dará como resultado un ensamblado llamado LibClases.dll. Vamos ahora a agregar un nuevo proyecto para Windows a la solución denominado Herencia. Una vez hecho esto, haremos clic derecho sobre el proyecto, y luego seleccionaremos Agregar – Agregar formulario heredado.

98

A continuación, haremos clic en Aceptar. Lo que desplegará la ventana Selector de herencia, como muestra la siguiente figura:

Debido a que el formulario a heredar se encuentra dentro dela solución, Visual Basic puede detectar el mismo automáticamente, y así mostrar aquellos elementos disponibles. En el caso de que sólo se cuente con más de un ensamblado, basta con hacer clic en el botón Explorar y luego seleccionar la biblioteca deseada. Vamos ahora a hacer clic en el componente a heredar y luego

99

Aceptar, lo que dará como resultado un nuevo formulario que contiene los controles del original dentro de ese proyecto. Como podemos apreciar, las características son similares al formulario implementado por el proyecto de biblioteca de clases base, pero no es posible modificar las propiedades de los controles, aunque sí es posible agregar nuevos objetos:

Echaremos un vistazo al código generado, a los efectos de ver cómo se implementa la herencia visual: Inherits ClassLibrary1.Form1 Como podemos apreciar, la forma de heredar un formulario es sencilla y consistente con el modelo de programación orientada a objetos, lo que facilita ampliamente la reutilización de código. También es posible emplear todas aquellas características de clases aprendidas anteriormente, como la sobrecarga de miembros, la sobrescritura, etc. Vamos ahora a ir a la ventana de propiedades del proyecto para configurar el formulario como por defecto al iniciar la aplicación. Una vez hecho esto, haremos clic derecho sobre la ventana de aplicación para Windows y seleccionaremos Establecer como proyecto de inicio, a los efectos de que se inicie la misma en vez del ensamblado creado inicialmente.

100

Por último, ejecutaremos la aplicación, lo que dará como resultado nuevamente la ventana de control de acceso. Sin embargo, si intenta escribir código para los diferentes eventos, notará que ello no es posible. Ello es debido a que los objetos dentro del código generado por el diseñador de formularios de Windows en la clase base han sido definidos utilizando el operador de visibilidad Friend, lo que restringe la posibilidad de implementar sus diferentes eventos. -El Diseñador de Windows Forms generó código .. Friend WithEvents Button1 As System.Windows.Forms.Button Para enmendar este inconveniente, basta con que modifique la visibilidad de aquellos que desea utilizar a Public o Protected, lo que hará posible escribir el código de los diferentes eventos de los controles del a ventana en la clase derivada. Sin lugar a duda, la utilización de estas técnicas de herencia visual y demás características de orientación a objetos brindan un espectro de posibilidades de trabajo mucho más flexible y consistente a la hora de implementar una solución. La realidad es que en muchas ocasiones los desarrolladores Visual Basic contábamos con fuertes restricciones como las que vimos al comienzo de este apartado, las cuales se ven enmendadas por las nuevas funcionalidades provistas por el lenguaje. Cada programador podía realizar un conjunto de ensamblados que contenían una parte de la aplicación, la cual posteriormente podía ser montada para conformar la solución final.

101

Módulo 5: ADO .NET Por que ADO .NET ADO .NET plantea una evolución natural, ya que se basa en la manipulación de información obtenida de orígenes de datos en forma desconectada. La idea principal de ADO .NET es la de emplear las conexiones únicamente bajo demanda; esto quiere decir que la aplicación estará por defecto desconectada del origen de datos, y solamente en el momento de requerir los servicios del mismo se establecerá una comunicación a éste. Esta aproximación permite al motor de datos resguardar un gran número de recursos. Existen dos tipos de ejecución factibles de ser realizadas sobre un origen de datos. Veamos entonces cómo resuelve el modelo desconectado propuesto por ADO .NET cada una de ellas.

♦ Acciones Unidireccionales ♦ Acciones Bidireccionales

El primer paso involucra aquellas acciones que no retornan filas, como una actualización, modificación o eliminación de uno o varios registros. Para este caso la solución en forma desconectada es relativamente sencilla, ya que en el momento de requerir la acción, el mismo podrá establecer una conexión con el origen, enviar la sentencia SQL y posteriormente desconectarse del mismo. El segundo caso involucra aquellas acciones que dependen de ambos lados, como aquellas que retornan filas al ordenador. Evidentemente, la solución de esto último bajo el modelo desconectado requiere de una mayor complejidad. Al igual que en el caso anterior, se abre la conexión al motor de datos para ejecutar la sentencia SQL, la cual retornará un conjunto de filas, que serán trasladadas y almacenadas en forma local, y posteriormente se cerrará la conexión con el origen. Para lograr esto ADO .NET cuenta con un conjunto de objetos que sirven de buffer para guardar en el cliente las filas obtenidas de la consulta al servidor. Como consecuencia, las acciones realizadas sobre las filas (modificaciones, eliminaciones, etc.) serán gestionadas en forma local, o lo que es igual, utilizando un juego privado de datos. En ADO ya se contaba con una característica similar denominada “Cursores Desconectados”, pero el proceso de conexión y desconexión debía realizarse en forma explicita. En ADO .NET los objetos están naturalmente desconectados del origen, y son estos últimos los que permiten en forma local filtrar, ordenar y hasta agregar, eliminar o modificar una o varias filas. Por supuesto que las acciones serán realizadas sobre la copia privada, sin que ello afecte a la información guardada en el motor de datos. ADO .NET cuenta con métodos que permiten posteriormente sincronizar la información del cursor local con el origen, y de esta forma hacer efectivos los cambios.

102

Adicionalmente, toda la información gestionada por ADO .NET puede ser almacenada en formato de documentos XML, ya sean cursores, reglas, y hasta los tipos de dato de cada columna (campos). Esto beneficia al modelo, ya que este tipo de documento es aceptado ampliamente para transferencia de datos a través de diferentes redes. A su vez, el mismo es intensamente utilizado para intercomunicar información entre aplicaciones o sistemas operativos, dado que el mismo está conformado exclusivamente por caracteres ASCII. Gracias a esto, los documentos pueden ser enviados a través del Web sin los inconvenientes que provocan los formatos binarios como los que ofrecía el modelo COM. Las Clases de ADO.NET ADO .NET incluye varias clases para la manipulación de datos, las cuales están incluidas en varios espacios de nombres: Imports System.Data Imports System.Data.OleDb Imports System.Data.SqlClient Las clases en ADO .NET se separan en dos grandes grupos, teniendo en cuenta la tarea que las mismas desempeñan:

1. Consumidores de Datos. 2. Gestores de Información.

Dentro del primer grupo se encuentran las clases que se encargan exclusivamente de realizar una conexión y obtener los datos. Como ejemplo de estas tenemos, las localizadas dentro de OleDb y SQLClient. Dentro del segundo grupo se hallan aquellas que gestionan la información una vez obtenida. Esto permite a ADO .NET emplear las mismas clases para gestionar los datos, sin importar si éstos fueron obtenidos a través de un proveedor específico (SQLClient) o genérico (OleDb). De acuerdo con ello, quienes obtienen la

System

Data

OleDb

SqlClient (Proveedor especifico)

103

información podrán estar relacionados con un motor de datos especifico, pero quienes lo gestionen posteriormente serán comunes a todas ellas. ¿Cuándo se debe utilizar un proveedor específico? Los proveedores de datos en .NET permiten acceder a orígenes de datos específicos. Usted puede usar el espacio de nombres System.Data.SQLClient para acceder a SQL Server 2000 y versiones anteriores, y el espacio de nombres System.Data.OLEDB para acceder a cualquier origen de datos expuesto a través de OLEDB.

104

Objetos Conectados Como mencionamos anteriormente el nuevo modelo utiliza cursores desconectados, aunque también mantiene algunas estructuras para gestión de cursores conectados, en forma similar a lo que se hacia en versiones anteriores. En esta sección veremos la forma de establecer una conexión permanente a un origen de datos. Para ello debemos hacer uso de la clase OleDbConnection localizada dentro del espacio OleDb. Veremos a continuación dos cadenas de conexión, una para un motor de datos de tipo SQL Server, y otra para una base de datos de Microsoft Access: Public Cn As New OleDb.OleDbConnection Dim x As New Form1

Sub Main() Cn.ConnectionString = "Provider=SQLOLEDB;User Id=sa;Password=;Initial Catalog=Ferreteria;Data Source=CASA;" Cn.Open() x.ShowDialog() End Sub

Public Cn As New OleDb.OleDbConnection() Dim x As New Form2() Sub Main() Cn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;User

Id=Admin;Password=;Data Source=D:\BD.mdb;" Cn.Open()

x.ShowDialog() End Sub

Si desea emplear el proveedor específico de SQL Server provisto por ADO .NET, se debe hacer uso de la estructura localizada dentro del espacio SQLClient:

Public Cn As New SqlClient.SqlConnection() Dim x As New Form2() Sub main()

Cn.ConnectionString = "User Id=sa;Password=;Initial Catalog=Ferreteria;Data Source=CASA;"

Cn.Open() x.ShowDialog()

End Sub Dado que sólo se podrá emplear con este tipo de motor de datos, no se hace necesario la especificación del manejador (driver) a utilizar. Para cerrar la conexión en forma inmediata o retornarla al pool de conexiones, basta con que se invoque el método Close como se muestra a continuación:

Cn.Close

105

Usando el Objeto Command La conexión es establecida para ejecutar un procedimiento almacenado o consulta. Para dicho fin contamos con una clase llamada Command, la cual viene precedida por OleDb o SQL, dependiendo de la clase que se esté utilizando. La misma representa una consulta o procedimiento almacenado para ser enviado a un origen de datos. El objeto de comando acepta como argumento del constructor personalizado la cadena SQL o nombre del procedimiento almacenado, así como también la variable de conexión previamente abierta. A diferencia de versiones anteriores de Visual Basic en las cuales un objeto de comando podía prescindir de uno de conexión, en ADO .NET un comando depende en su totalidad de una variable de este tipo. El próximo paso consiste entonces en asignar los argumentos que contienen la instrucción de comando y la conexión al objeto en el momento de la creación del mismo. Sintaxis OleDbCommand(<Inst. SQL, Nombre Tabla, etc.> As string, <conexión> as OleDb Connection ) El objeto Command provee cuatro métodos para ejecutar un comando: ExecuteReader Use este método cuando espere que un query regrese un conjunto de registros. Este método regresa los registros dentro de un objeto SQLDataReader o un OleDbDataReader. ExecuteScalar Use este método cuando el query regrese un solo valor. Por ejemplo el resultado de un select que solo contenga una función de agregado en sus columnas. Select Max(PrecioUni) From Producto ExecuteNonQuery Use este método cuando el query no regrese resultado, por ejemplo un Insert, Update, Delete o Procedimiento almacenado. ExecuteXML Use este método solo cuando el query incluya la cláusula de validación FOR XML. Este método solo es valido cuando usa el objeto SQLCommand. El siguiente ejemplo muestra como usar el objeto Command, para mandar un query a la base de datos y recuperar algun dato: Dim commSQL As New OleDb.OleDbCommand commSQL.Connection = Cn 'asigna la conexión al comando

106

commSQL.CommandText = "Select Count(*) From Cliente" MessageBox.Show(commSQL.ExecuteScalar().ToString) Usando el Objeto Command con un Procedimiento Almacenado Usted puede usar el objeto Command para ejecutar un procedimiento almacenado de una base de datos: El siguiente ejemplo muestra como ejecutar un procedimiento almacenado usando ADO .NET: -- Procedimiento almacenado en la Base de Datos Ferreteria /* **************************************************************************************** */ /* AGZ 09 Mayo 2003 */ /* Drop Procedure spInsDetalle */ /* **************************************************************************************** */ Create Procedure spInsDetalle @Emp int, @ConsecPk Int, @ProductoPk Int, @cantidad Numeric(18,2), @Importe Numeric(18,2), @ImpIva Numeric(18,2), @PorDescuento tinyint,@ImpDescuento Numeric (18,2) as Begin Transaction Insert into DetalleFactura (ConsecPk, EmpresaPK, ProductoPk, Cantidad, Importe, ImpIVA, PorDescuento, ImpDescuento) Values ( @ConsecPk, @Emp, @ProductoPk, @cantidad,@Importe, @ImpIva, @PorDescuento,@ImpDescuento) If @@Error <> 0 Begin RAISERROR ('Error al Insertar el Detalle', 16, 1) Rollback Transaction Return 1 end Update Producto Set Existencia = Existencia - @Cantidad Where EmpresaPk = @Emp and ProductoPk = @ProductoPk If @@Error <> 0 Begin RAISERROR ('Error al Actualizar las Existencias', 16, 1) Rollback Transaction Return 1 end Commit Transaction Return 0

107

Código Visual Basic para ejecutar el procedimiento almacenado Dim commSQL As New OleDb.OleDbCommand Dim paramSQL As New System.Data.OleDb.OleDbParameter Dim paramSQL1 As New System.Data.OleDb.OleDbParameter Dim paramSQL2 As New System.Data.OleDb.OleDbParameter Dim paramSQL3 As New System.Data.OleDb.OleDbParameter Dim paramSQL4 As New System.Data.OleDb.OleDbParameter Dim paramSQL5 As New System.Data.OleDb.OleDbParameter Dim paramSQL6 As New System.Data.OleDb.OleDbParameter Dim paramSQL7 As New System.Data.OleDb.OleDbParameter Try commSQL = New OleDb.OleDbCommand("spInsDetalle", Cn) commSQL.CommandType = CommandType.StoredProcedure paramSQL = commSQL.Parameters.Add("@Emp", OleDb.OleDbType.Integer) paramSQL1 = commSQL.Parameters.Add("@ConsecPk", OleDb.OleDbType.Integer) paramSQL2 = commSQL.Parameters.Add("@ProductoPk", OleDb.OleDbType.Integer) paramSQL3 = commSQL.Parameters.Add("@Cantidad", OleDb.OleDbType.Numeric) paramSQL4 = commSQL.Parameters.Add("@Importe", OleDb.OleDbType.Numeric) paramSQL5 = commSQL.Parameters.Add("@ImpIva", OleDb.OleDbType.Numeric) paramSQL6 = commSQL.Parameters.Add("@PorDescuento", OleDb.OleDbType.TinyInt) paramSQL7 = commSQL.Parameters.Add("@ImpDescuento", OleDb.OleDbType.Numeric) paramSQL.Direction = ParameterDirection.Input paramSQL1.Direction = ParameterDirection.Input paramSQL2.Direction = ParameterDirection.Input paramSQL3.Direction = ParameterDirection.Input paramSQL4.Direction = ParameterDirection.Input paramSQL5.Direction = ParameterDirection.Input paramSQL6.Direction = ParameterDirection.Input paramSQL7.Direction = ParameterDirection.Input paramSQL.Value = "1" paramSQL1.Value = "5" paramSQL2.Value = "9" paramSQL3.Value = "8" paramSQL4.Value = "110.0" paramSQL5.Value = "72.40" paramSQL6.Value = "0" paramSQL7.Value = "0.0" commSQL.ExecuteNonQuery() Catch ex As Exception MessageBox.Show(ex.ToString) End Try

108

Usando el Objeto DataReader En versiones anteriores de Visual Basic se contaba con un objeto llamado RecordSet, el cual permitía alojar el resultado de la ejecución de un comando. En ADO .NET el mismo no existe, por lo que debemos pensar entonces en un objeto capaz de realizar tareas similares. El nuevo modelo de clases para acceso a datos provee un objeto llamado DataReader, el cual permite ejecutar una consulta almacenada por un comando, y posteriormente acceder a su resultado (este es ReadOnly - ForwardOnly). DataReader Ofrece la forma más eficiente de acceso a un conjunto de filas, y es el único provisto por ADO .NET para consumir una conexión durante todo el tiempo. El primer paso es crear un objeto de este tipo: Dim datRead As OleDb.OleDbDataReader El enlace con el objeto de comando se hace: datRead = commSQL.ExecuteReader Dado que la variable de tipo DataReader puede leer un solo registro por vez, se brinda el método Read para realizar esta acción. Cada invocación al mismo incrementará el puntero al próximo registro, y es posible conocer si se encuentra al final, si el método regresa el valor de falso: While datRead.Read MessageBox.Show(datRead(0).ToString) End While Para consultar el contenido de cada registro, existen dos formas. Ambos modalidades utilizan la propiedad Item para obtener el valor de la columna, y lo hacen en el formato original definido por la columna de la base de datos. La primera consiste en especificar el nombre del campo mediante una cadena de caracteres: MessageBox.Show(datRead.Item("ClientePk").ToString) La segunda consiste en acceder al mismo mediante su ordinal, siendo la primera posición siempre 0: MessageBox.Show(datRead.Item(0).ToString)

109

Si quisiéramos validar que una columna del dataReader contenga un valor nulo: If datRead.IsDBNull(0) Then MessageBox.Show("Valor Nulo") Else MessageBox.Show(datRead.Item(0).ToString) End If Para obtener el nombre de una de las columnas de la tabla que trae cargada el DataReader: MessageBox.Show(datRead.GetName(0)) Ejecución de Múltiples Consultas Además de poder contener una única sentencia SQL, un objeto DataReader puede almacenar múltiples consultas, a los efectos de que las mismas sean ejecutadas en el origen de datos. Para ello, debemos escribir cada una de las mismas separada por un punto y coma, dentro de la cadena que contendrá las mismas. Una vez ejecutado el comando, se ofrecerá por defecto el resultado de la primera consulta. Para avanzar a la próxima se debe utilizar el método NextResult, el cual retornará un valor de tipo booleano si existen más cadenas disponibles. Dim commSQL As New OleDb.OleDbCommand Dim datRead As OleDb.OleDbDataReader commSQL = New OleDb.OleDbCommand("Select * From Cliente; Select * From Producto", Cn) datRead = commSQL.ExecuteReader Do While datRead.Read() MessageBox.Show(datRead.Item(3).ToString) End While Loop While datRead.NextResult datRead.Close() Ejecución de Sentencias que No Tengan Datos de Retorno Se cuenta también con otro tipo de consultas, las cuales no retornan filas después de la ejecución de la misma. Este tipo de sentencias se utiliza muy comúnmente para crear objetos en la base de datos (tablas, permisos), así como también para modificar la información de una o varias filas (Insert, Update o Delete). Para dicho fin, los objetos de comando cuentan con un método denominado ExecuteNonQuery, que permite al mismo optimizar el proceso, partiendo de la base de que la instrucción no retornará filas.

110

El siguiente ejemplo modifica el campo NomCliente de la tabla Cliente de la base de datos Ferreteria (conectada al ejemplo en el punto anterior): Dim commSQL As OleDb.OleDbCommand Dim strCommand As String strCommand = "Update Cliente set NomCliente = 'Alex Lora' Where ClientePk = 52" commSQL = New OleDb.OleDbCommand(strCommand, Cn) commSQL.ExecuteNonQuery()

111

Proveedores de Objetos Desconectados En ADO .NET el objeto RecordSet no existe, pero en su reemplazo se ofrece uno llamado DataSet. El mismo es el elegido para gestionar los cursores en ADO .Net, así como también para el intercambio de datos entre aplicaciones o componentes. El DataSet permite almacenar filas de uno o varios resultados, pero, a diferencia del primero, en éste es posible establecer las relaciones entre sus integrantes. Sin embargo, la diferencia más importante entre un RecordSet y un DataSet es que este último representa una vista local o en memoria de las filas obtenidas de un origen de datos. Esencialmente, un objeto DataSet obtiene un conjunto de filas de una o más tablas, y posteriormente las aloja en memoria. Una vez realizado este proceso, el mismo procede a desconectarse del origen de datos. El DataSet se encarga posteriormente de la gestión local de las filas obtenidas, así como también de las posibles acciones a ser realizadas sobre las mismas (modificaciones, eliminaciones, etc.) todas las acciones serán aplicadas sobre la copia privada o local de datos, sin que ello interfiera en el origen. Posteriormente, se necesitará sincronizar la información con este último, a los efectos que los cambios puedan hacerse efectivos. Para dicha tarea, se involucra un segundo objeto llamado DataAdapter, el cual funciona de intermediario o puente sincronizador entre la copia privada y el origen de datos. Esencialmente, un DataAdapter establece una conexión, y luego envía al origen de datos la acción SQL por cada registro modificado, agregado o eliminando en la copia local, y posteriormente se desconecta. Esta tarea no necesariamente debe realizarse al finalizar cada una de las acciones, sino que la misma puede ser llevada a cabo durante períodos determinados de tiempo, el DataSet puede también desechar la copia local y obtener un nuevo juego de datos más actualizado si así lo desea.

DataSet

DataAdapter

Origen de

Datos

OleDbDataAdapter o SQLDataAdapter

112

Otro punto importante a tener en cuenta es el relacionado con las estructuras y restricciones vinculadas a cada tabla. Como vimos anteriormente, cuando un conjunto de datos obtenido de un origen de datos, el mismo es gestionado por un objeto de tipo DataSet en modalidad local y desconectado. Las columnas de una tabla generalmente definen relaciones con otras, así como también contienen reglas y restricciones que aseguran la consistencia de la información almacenada. La única forma de cerciorarse sobre la integridad de los datos locales es asegurándose de que las mismas reglas estén también disponibles en la copia privada. Para dicho fin, el DataSet cuenta con una clase llamada DataRelation , la cual permite especificar relaciones y restricciones a mantener entre las tablas pero en forma local, con el fin de emular los comportamientos ofrecidos por el origen de datos (o similar a la modalidad conectada). De esta forma es posible asegurar que la copia local no generará conflictos en el momento de la sincronización, ya que las pautas con las cuales será tratada la información local serán similares a las ofrecidas por el origen de datos. El dataset también beneficia en forma directa a las aplicaciones Web, las cuales son desconectadas por naturaleza. En general, no se hace necesario el conocer dónde está localizado el origen de datos hasta el momento en que la información debe ser sincronizada. En muchas ocasiones, el proceso involucra solamente lectura de datos o acciones que nunca serán enviadas al motor, por lo que muy comúnmente el DataSet será utilizado simplemente como estructura de intercambio entre aplicaciones. El siguiente ejemplo carga un Listbox con la información de la tabla de Cliente de la base de datos Ferreteria. Para ello emplearemos la conexión vista en puntos anteriores y un DataAdapter, el cual funciona en forma similar a un objeto comando, debido a que este es el responsable de obtener las filas del origen de datos, a continuación en un DataSet se depositaran las filas del DataAdapter. El DataAdapter cierra la conexión una vez que las filas han sido obtenidas y depositadas en el DataSet. A continuación se muestra la ventana y el código:

113

Declaración del DataSet en la sección de Declaraciones a nivel forma: Dim Ds As New System.Data.DataSet() Botón para cargar el DataSet con la conexión del ejemplo anterior:

Private Sub Button1_Click(ByVal sender As System.Object, … Dim Adaptador As New OleDb.OleDbDataAdapter("Select * From Cliente", Cn)

Adaptador.Fill(Ds, "Cliente") End Sub

Botón para cargar el ListBox con la información del DataSet (este se carga como si fuera una colección): Private Sub Button2_Click(ByVal sender As System.Object, … Dim Registro As DataRow ListBox1.Items.Clear() For Each Registro In Ds.Tables("Cliente").Rows ListBox1.Items.Add(Registro(0) & " " & Registro("NomCliente") & " " & Registro(2)) Next End Sub Multiples Consultas Bajo un DataSet Un único dataset puede contener y manipular simultáneamente el contenido de varias tablas en forma independiente. Cada conjunto de filas a obtener debe ser retornado por un objeto de tipo DataAdapter, por lo que si se desea contar con el contenido de tres tablas, se necesitará definir tres adaptadores especificando en cada uno el elemento deseado. El siguiente ejemplo muestra como cargar las filas de la tabla Cliente, Clasif y Producto. Las mismas podrán ser cargadas posteriormente en único DataSet. Para ello es necesario invocar al método Fill de cada adaptador:

114

Dim Ds As New System.Data.DataSet Dim Registro As DataRow Dim DataAdapEmp, DataAdapTie, DataAdapAut As OleDb.OleDbDataAdapter DataAdapEmp = New OleDb.OleDbDataAdapter("Select * From Cliente", Cn) DataAdapTie = New OleDb.OleDbDataAdapter("Select * From Clasif", Cn) DataAdapAut = New OleDb.OleDbDataAdapter("Select * From Producto", Cn) DataAdapEmp.Fill(Ds, "Cliente") DataAdapTie.Fill(Ds, "Clasif") DataAdapAut.Fill(Ds, "Producto") For Each Registro In Ds.Tables("Clasif").Rows ListBox1.Items.Add(Registro(0) & " " & Registro(1)) Next Usando Relaciones dentro de DataSet En concepto básico de los manejadores de bases de datos es el hecho de poder relacionar tablas. ADO .NET provee esta habilidad en los DataSet a través de la clase DataRelation. Cada objeto de tipo DataRelation contiene un arreglo de objetos de tipo DataColumn que define dentro de la relación cual es la columna o columnas padre, o primary key, o foreign key. La integridad referencial es mantenida con las relaciones. El siguiente ejemplo muestra como crear una relación entre dos DataTable dentro de un DataSet. El mismo DataAdapter será usado para cargar el DataTable, y entonces crear el DataRelation entre estas dos: Dim adaptSQl As OleDb.OleDbDataAdapter Dim Ds As New System.Data.DataSet adaptSQl = New OleDb.OleDbDataAdapter("Select * From Cliente", Cn) adaptSQl.Fill(Ds, "Cliente") adaptSQl = New OleDb.OleDbDataAdapter("Select * From Factura", Cn) adaptSQl.Fill(Ds, "Factura") Dim relacion As New DataRelation("RelCtefact", Ds.Tables("Cliente").Columns("ClientePk"), Ds.Tables("Factura").Columns("ClientePk")) Ds.Relations.Add(relacion)

115

Acceder Datos Relacionados El principal uso del dataRelation es el permitir acceder a registros relacionados en diferentes tablas. Usted puede hacer esto usando el método GetChildRows o DataRow que permiten regresar un arreglo de DataRow. El siguiente ejemplo muestra como usar este método para acceder a los registros del ejemplo anterior: Dim CteTupla, FactTupla As DataRow Dim Tuplas() As DataRow CteTupla = Ds.Tables("Cliente").Rows(1) Tuplas = CteTupla.GetChildRows("Rel") For Each FactTupla In Tuplas ListBox1.Items.Add(FactTupla("FecFactura").ToString) Next

Como se puede observar el Listbox se llena con la información del las Fechas de las facturas que se corresponden con el segundo registro de Cliente. Si convirtiéramos este código a un Query quedaría de la siguiente manera: Nota: todo el código en Vb de este ejemplo va en el botón de Relaciones: select FecFactura From Cliente A Inner Join Factura B On (A.clientePk = B.clientepk) Where A.ClientePk = 2

116

Usando Restricciones (Constraints) Usted puede crear restricciones sobre un DataSet, puede copiar las existentes de un origen de datos. Restricciones de tipo Foreign Key Este tipo de restricción controlan lo que puede acontecer con los registros hijos, cuando el registro padre es actualizado o borrado. Usted puede especificar diferentes caminos para diferentes circunstancias. La siguiente tabla muestra los valores para las propiedades DeleteRule y UpdateRule de la restricción ForeignKeyConstraint.

Valor Descripción Cascade Borra o Actualiza cualquier registro hijo que este basado en el

registro padre. SetNull Coloca los valores relacionados a DBNull. SetDefault Coloca los valores relacionados a su Default. None No Afecta los registros relacionados. El siguiente ejemplo muestra como aplicar una restricción de tipo foreign key sobre dos tablas existentes en un DataSet. Si el registro en la tabla padre es borrado, los valores relacionado en la tabla hija serán colocados en DbNull. Si un registro de la tabla padre es modificado, los valores relacionados en la tabla hija serán igualmente modificados: Dim colPadre, colHija As DataColumn Dim RestForeign As ForeignKeyConstraint colPadre = Ds.Tables("Clasif").Columns("ClasifPk") colHija = Ds.Tables("Producto").Columns("ClasifPk") RestForeign = New ForeignKeyConstraint("ResClasProd", colPadre, colHija) RestForeign.DeleteRule = Rule.SetNull RestForeign.UpdateRule = Rule.Cascade Ds.Tables("Producto").Constraints.Add(RestForeign) Ds.EnforceConstraints = True Restricción UniqueConstraint Esta restricción puede ser agregada a una columna o un arreglo de columnas. Y se asegura que todos los valores dentro de una columna sean únicos. Cuando esta restricción es adicionada, ADO .NET verifica que los valores existentes no violen la restricción y mantiene esta para todos los cambios efectuados sobre DataTable.

117

El siguiente ejemplo muestra como adicionar un UniqueConstraint a una columna:

Dim unCliente As New UniqueConstraint("UniqueCliente", Ds.Tables("Cliente").Columns("RFCliente"))

Ds.EnforceConstraints = True Usar constraints Existentes de la Base de Datos Si las restricciones existen en el manejador de base de datos, estas se pueden copiar directamente al DataSet. El siguiente ejemplo muestra como usar el método FillSchema para copiar la información de las restricciones al DataSet:

Dim Ds As New System.Data.DataSet() Dim adaptSql As New OleDb.OleDbDataAdapter("Select * From Producto", Cn) adaptSql.FillSchema(Ds, SchemaType.Source, "Producto") adaptSql.Fill(Ds, "Producto") Nota: Las restricciones son adicionadas automáticamente cuando crea la relación entre las tablas. Un UniqueConstraint es adicionado en la llave primaria, y un ForeignKeyConstraint es adicionado en la llave foránea.

118

Afectando Datos en un DataSet Una vez que se creo el DataSet, usted puede adicionar, modificar y borrar datos. Cualquier cambio que se efectué en los datos, primero es cargado en memoria y posteriormente se aplican los cambios en el origen de datos. Para Adicionar un Registro Use los siguientes pasos para adicionar un registro a la tabla:

1. Instanciar un objeto de tipo DataRow usando el método NewRow del DataTable.

2. Cargar las columnas con los datos. 3. Llamar al método Add de la colección DataRows, pasándole el objeto

DataRow. El siguiente ejemplo muestra como adicionar un registro al DataSet: Dim drNewRow As DataRow = Ds.Tables("Clasif").NewRow drNewRow("ClasifPk") = TextBox1.Text drNewRow("NomClasif") = TextBox2.Text drNewRow("EmpresaPk") = TextBox3.Text Ds.Tables("Clasif").Rows.Add(drNewRow) Editar un Registro Use los siguientes pasos para editar un registro existente:

1. Llame el método BeginEdit del registro. 2. Cambie los datos en las columnas. 3. Invoque al método EndEdit o al CancelEdit para aceptar o descartar los

cambios. El siguiente ejemplo muestra como editar los datos en un registro: Dim drCambiarRow As DataRow = Ds.Tables("Clasif").Rows(7) drCambiarRow.BeginEdit() drCambiarRow("NomClasif") = "Alex Lora" drCambiarRow.EndEdit() Borrar Datos Use uno de los siguientes métodos para borrar un registro:

♦ Método Remove Llame al método Remove de la colección DataRows. Este remueve el registro del DataSet permanentemente.

♦ Método Delete Llame al método Delete del objeto DataRow. Este solo marca en registro a borrar en el DataSet, y si quiere deshacer el borrado llame al método RejectChanges.

119

El siguiente ejemplo muestra como borrar un registro existente en el DataSet:

Dim drDelRow As DataRow = Ds.Tables("Clasif").Rows(7) Dim Cb As New OleDb.OleDbCommandBuilder(Adaptador) drDelRow.Delete() Adaptador.Update(Ds, "Clasif") Ds.AcceptChanges() Confirmando los Cambios Para actualizar el DataSet, use el método apropiado al editar la tabla, y entonces acepte los cambios con AcceptChanges una vez actualiza el estado de las filas (RowState). La siguiente tabla muestra los posibles valores para esta propiedad:

Valor Descripción UnChanged No hay cambios que aplicar. Added Los registros que deben ser agregados a la tabla. Modified Los registros que fueron modificados. Deleted Los registros que fueron borrados con deleted. Detached Los registros que fueron borrados, o el registro que fue

creado pero no fue llamando al método Add.

120

Actualizando la Información del Dataset al Origen de Datos Como vimos anteriormente, el DataSet es cargado con registros de un origen de datos obtenidos por un objeto de tipo DataAdapter (adaptador). Cuando esto sucede, todas las filas son marcadas internamente con un estado denominado sin–cambios. Esto le indica a ADO .NET que las mismas no han sufrido modificaciones localmente desde que fueron obtenidas. Como se vio, una vez que la tabla es cargada dentro del DataSet, es posible eliminar, modificar o agregar nuevos registros, pero dichas acciones son gestionadas en memoria. Cada vez que se realiza una acción sobre algunas de ellas, el DataSet modifica automáticamente el estado de la o las mismas, dependiendo de la acción tomada sobre la fila. Esto le permitirá posteriormente a éste conocer qué tareas fueron realizadas sobre cada una de ellas. Por ejemplo, en el caso de que la misma sea eliminada, el DataSet marcará el registro con estado de eliminado. Lo mismo sucede cuando se modifica o agrega una fila, en la que ésta se registrará como modificado o nueva, respectivamente. Cuando se concilia la información de la copia local con el origen de datos, el adaptador automáticamente filtra y deja todos aquellos registros con estado diferente a sin-cambios, y posteriormente recorre cada una de las mismas y realiza las acciones necesarias basadas en su estado. Mediante esta sencilla técnica, el DataAdapter puede conocer las filas a transferir al origen, y a su vez el tipo de instrucción SQL que deberá emplear en cada caso. Por ejemplo, si se realizo una adición, el DataAdapter ejecutará un comando SQL de inserción (Insert Into ...) con la información del registro, mientras que en el caso de que sea una actualización o eliminación. El mismo ejecutará un comando de reemplazo (Update) o eliminación (Delete From), Respectivamente. Para realizar la sincronización del DataAdapter con la base de datos, se hace uso de la clase CommandBuilder esta clase facilita la tarea de especificar las instrucciones SQL para los diferentes comandos del DataAdapter. La idea principal es que solamente se establezca la cadena de selección, y que posteriormente ADO .NET deduzca las demás. Para dicho fin se provee una clase llamada OleDbCommandBuilder para el proveedor genérico o SqlCommandBuilder para el específico de SQL Server. El siguiente ejemplo muestra como insertar y borrar un registro en un DataSet, sincronizando dichas modificaciones con la Base de Datos:

121

Dim Ds As New System.Data.DataSet Dim Adaptador As New OleDb.OleDbDataAdapter("Select * From Clasif", Cn) Private Sub Form5_Load(ByVal sender As System.Object, … Adaptador.Fill(Ds, "Clasif") End Sub Private Sub Borrar_Click(ByVal sender As System.Object, … Dim drDelRow As DataRow = Ds.Tables("Clasif").Rows(7) Dim Cb As New OleDb.OleDbCommandBuilder(Adaptador) drDelRow.Delete() Adaptador.Update(Ds, "Clasif") Ds.AcceptChanges() End Sub Private Sub CargaListBox_Click(ByVal sender As System.Object, … Dim Registro As DataRow ListBox1.Items.Clear() For Each Registro In Ds.Tables("Clasif").Rows ListBox1.Items.Add(Registro(0) & " " & Registro("NomClasif")) NextEnd Sub Private Sub Insertar_Click(ByVal sender As System.Object, … Dim CB As New OleDb.OleDbCommandBuilder(Adaptador) Dim drNewRow As DataRow = Ds.Tables("Clasif").NewRow drNewRow("ClasifPk") = TextBox1.Text drNewRow("NomClasif") = TextBox2.Text drNewRow("EmpresaPk") = TextBox3.Text Ds.Tables("Clasif").Rows.Add(drNewRow) Adaptador.Update(Ds, "Clasif") Ds.AcceptChanges() End Sub La declaración de la variable CB de tipo OleDbCommandBuilder inspeccionará la sentencia de selección, deducirá y generará en base a ella las cláusulas de inserción, modificación y eliminación, las cuales serán posteriormente utilizadas por el adaptador en el momento de sincronización.

122

Puede hacer uso de este objeto cuando se utilizan sentencias SQL simples (Select, Insert, etc.) sobre una única tabla, pero no en el caso de que se requiera de comandos asociados a procedimientos almacenados. Los mismos deberán ser introducidos en forma explicita. Una vez realizada la tarea, el adaptador podrá ser sincronizado con el origen, y las modificaciones, eliminaciones o nuevas filas serán enviadas al mismo. El siguiente ejemplo muestra como agregar, modificar, o eliminar los registros de un DataSet, usando un DataGrid. Para ello usaremos la tabla Empresa de la Base de Datos Ferreteria, la cual contiene información sobre las diferentes Empresas que contempla el sistema:

Primero agregaremos las importaciones, así como también aquellas variables modulares: Imports System.Data Imports System.Data.OleDb Public Class Form6 Inherits System.Windows.Forms.Form Dim AD As OleDbDataAdapter Dim DS As New DataSet() En el evento Load de la forma configuraremos el DataSet con las filas obtenidas por el Adaptador. Así como asignaremos el DataSet al DataGrid. Private Sub Form6_Load(ByVal sender As System.Object, … AD = New OleDbDataAdapter("Select * From Empresa", Cn) AD.Fill(DS, "Emp") DataGrid1.DataSource = DS.Tables("Emp") End Sub

123

Como ultimo paso, agregaremos en el evento click del botón el código necesario para la sincronización del DataSet con la base de datos. Private Sub Sincronizar_Click(ByVal sender As System.Object,… Dim Cb As New OleDb.OleDbCommandBuilder(AD) If DS.HasChanges() Then AD.Update(DS, "Emp") DS.Tables("Emp").AcceptChanges() Else MsgBox("Sin Cambios", MsgBoxStyle.Information, "SRADFSASDA") End If End Sub Private Sub Cerrar_Click(ByVal sender As System.Object,… Me.Close() End Sub End Class El método AcceptChanges del DataSet modifica el estado de todas las filas a sin-cambios. Este método debe ser invocado siempre después de llamar al método Update del DataAdapter, ya que sise realiza antes de la sincronización, se eliminará la información del estado de aquellas filas modificadas, agregadas o eliminadas. De igual manera puede utilizar la propiedad HasChanges para conocer si hubo algún cambio en alguna de las tablas contenidas por el DataSet, y así se deberá proceder a la sincronización.

124

Enlace de Datos Una de las tareas más comunes en un lenguaje medianamente sofisticado es la de vincular el resultado de una consulta a un conjunto de controles dibujados sobre una ventana (cajas de texto, etiquetas, etc.). La facilidad de esto radica en que los controles se encuentran sincronizados entre sí, y todos ellos exhibirán siempre una única fila del DataSet a la vez. ADO .NET proporciona la clase llamada Binding, la cual trabaja en conjunto con los componentes visuales y un DataSet, y básicamente coopera para que se ofrezca una sola fila a la vez. Por supuesto que la clase Binding gestiona en forma automática el desplazamiento a través de las diferentes filas de la tabla, y adicionalmente refleja los nuevos valores en todos los componentes asociados. Los controles cuentan con un miembro llamado DataBindings, el cual ofrece el método add para realizar el vínculo entre el campo de la tabla cargado en el DataSet y el control. Con el fin de demostrar dicha funcionalidad, crearemos una nueva aplicación para Windows, y crearemos una ventana de altas bajas cambios, usando la tabla Clasif de acuerdo a la siguiente ventana:

Primero declare el DataSet y el adaptador a nivel forma Dim Ds As New DataSet Dim AD As New OleDb.OleDbDataAdapter("Select * From Clasif", Cn) En el evento Load del formulario cargaremos el DataSet con los registros de la consulta realizada sobre la tabla Clasif de la base de datos Ferreteria, así como hacer el enlace para cada una de las cajas de texto con el campo correspondiente: Private Sub Form7_Load(ByVal sender As System.Object, … AD.Fill(Ds, "Clasif") TextBox1.DataBindings.Add(New Binding("Text", Ds, "Clasif.ClasifPk")) TextBox2.DataBindings.Add(New Binding("Text", Ds, "Clasif.NomClasif")) TextBox3.DataBindings.Add(New Binding("Text", Ds, "Clasif.EmpresaPk")) End Sub

125

Ahora nos centraremos en la programación de los botones que realizarán el desplazamiento a través de las diferentes filas de la tabla. Los controles que utilizan Binding apuntan siempre a un único registro o registró activo. De acuerdo con esto, cualquier acción tomada sobre el mismo afectará a todos los elementos vinculados. Para dicho fin, los formularios de Windows cuentan con una propiedad llamada BindingContext, que hace posible desplazarse a través de las diferentes filas, simplemente asignando a la misma el ordinal de registro activo, así como también el DataSet y tabla. Private Sub Inicio_Click(ByVal sender As System.Object, … Me.BindingContext(Ds, "Clasif").Position = 0 End Sub Private Sub Atras_Click(ByVal sender As System.Object, … Me.BindingContext(Ds, "Clasif").Position -= 1 End Sub Private Sub Adelante_Click(ByVal sender As System.Object, … Me.BindingContext(Ds, "Clasif").Position += 1 End Sub Private Sub Fin_Click(ByVal sender As System.Object, … Me.BindingContext(Ds, "Clasif").Position = Me.BindingContext(Ds, "Clasif").Count() End Sub Private Sub Actualizar_Click(ByVal sender As System.Object,… Dim dr As DataRow = Ds.Tables("Clasif").Rows(Me.BindingContext(Ds, "Clasif").Position) dr.BeginEdit() dr("Clasifpk") = TextBox1.Text dr("NomClasif") = TextBox2.Text dr("EmpresaPk") = TextBox3.Text dr.EndEdit() End Sub Private Sub Nuevo_Click(ByVal sender As System.Object, … TextBox1.Text = "" TextBox2.Text = "" TextBox3.Text = "" Me.BindingContext(Ds, "Clasif").AddNew() End Sub Private Sub Borrar_Click(ByVal sender As System.Object, … Dim drDelRow As DataRow = Ds.Tables("Clasif").Rows(Me.BindingContext(Ds, "Clasif").Position) drDelRow.Delete() End Sub

126

Si efectuamos algún cambio en la información de las cajas de texto y queremos reflejarlo en la base de datos: Private Sub Salvar_Click(ByVal sender As System.Object, … Dim Cb As New OleDb.OleDbCommandBuilder(AD) AD.Update(Ds, "Clasif") Ds.AcceptChanges() Ds.Tables("Clasif").AcceptChanges() End Sub

127

Módulo 6. Utilización de Reportes en Visual Basic Gran parte de las aplicaciones necesitan evaluar información obtenida de uno o varios orígenes de datos mediante procesos realmente complejos, con el fin de brindar un resultado capaz de ser comprendido por un usuario final. Si bien el nuevo modelo de impresión podría ser el elegido para esta tarea, existe una tecnología aún más acorde, la cual permite establecer los procesos de obtención, análisis y publicación, en forma realmente sencilla. Este tipo de características se gestionan en Visual Basic desde ya hace bastante tiempo a través de los módulos de informe. Un módulo de este tipo establece los diferentes procesos que se le deberán realizar a un conjunto de datos. Antes de conocer más acerca de ellos, vamos a realizar una breve reseña sobre los cambios que éstos han experimentado en los últimos años. En la versión 3.0 de Visual Basic, Microsoft decidió incluir e integrar al generador de informes de la empresa Crystal Reports (ahora llamada Crystal Decisions) como parte del producto, debido a la alta demanda que este tipo de tareas requerían. En la versión 6.0 del producto, la compañía decidió excluir el mismo, adicionando su propia versión de generador de informes, el cual era realmente deficiente. Si bien era posible realizar tareas básicas sobre un conjunto de datos, no contaba con mayores posibilidades, y las opciones de integración con aplicaciones para Web eran realmente acotadas. Nuevamente en Visual Basic .NET el generador de informes de Crystal Decisions es incluido como tecnología nativa de la infraestructura .NET. Sin embargo, la misma no debe ser tomada como una simple herramienta para procesar datos y generar documentos, ya que el alcance de sus funcionalidades es realmente sorprendente. Ahora se cuenta con tres medios a través de los cuales los informes pueden ser publicados:

- Aplicaciones para Windows. - Páginas de servidor activo ASP.NET. - Servicios Web ASP.NET.

El primer tipo es el ya conocido, el cual envía el resultado de un informe a una ventana de vista preliminar de Windows. Para comprender las restantes opciones, es necesario estar al tanto de las demás funcionalidades provistas por la herramienta, las cuales veremos a continuación. Cuando se genera un informe para una aplicación, en general el proceso que confecciona el mismo y la vista previa son ejecutados dentro de la misma computadora. Esto es cierto en la mayor parte de los casos, pero, sin embargo, existen nuevas opciones del producto que marcan una diferencia sustancial con respecto a esta aproximación. Ahora es posible crear y hacer uso de informes desde una aplicación Web, a los efectos de que ellos puedan ser visualizados desde cualquier explorador que utilice los protocolos y formatos estándares de Internet (http, html, etc.). Para dicho fin, Crystal provee un conjunto de clases y un control Web para visualizar un informe, a los efectos de que los mismos puedan ser accesibles desde las diferentes página de servidor activo ASP.NET. Veremos

128

más adelante que es igual de sencillo crear uno de estos para una aplicación Windows que para una aplicación Web. A continuación veremos las funcionalidades básicas de la elaboración de un informe para una aplicación para Windows, y luego aprenderemos más sobre aplicaciones Web. Empleando informes en ambiente Windows Antes de comenzar a explicar sobre cómo crear un informe para una aplicación para Windows, es importante que conozca que Visual Basic instala una carpeta llamada Samples debajo del directorio Crystal, la cual contiene varios ejemplos sobre este tema. Bien, ahora vamos a crear un nuevo proyecto de aplicación para Windows, y será el encargado de obtener todos los productos por clasificación y agruparlos por dicho criterio.

Una vez creado el mismo, haremos clic derecho sobre el Explorador de soluciones, y luego Agregar – Agregar nuevo elemento. Esto abrirá la caja de diálogo similar a la Figura anterior, que nos permitirá agregar un nuevo módulo de informe. Vamos ahora a seleccionar al elemento Crystal Report, para posteriormente cambiarle el nombre del mismo a Productos.rpt, y por último hacer clic en aceptar para adicionar el mismo al proyecto.

129

Inmediatamente después de que un módulo de informe es adicionado al proyecto, la ventana de diálogo denominada Galería de Crystal Reports será exhibida, a los efectos de que sea posible especificar en forma sencilla el tipo de documento a elaborar. El mismo ofrece las siguientes opciones:

- Crear un documento mediante el Asistentes de informes. - Crear un informe en blanco. - Crear un nuevo informe a partir de un informe existente.

A su vez, el control de lista situado en la parte inferior hace posible seleccionar el tipo de asistente, a los efectos de adecuar los posteriores pasos del mismo. Para este ejemplo crearemos un informe utilizando la primera opción mediante el Asistente de informes, y haremos uso de la plantilla por defecto (estándar). El mismo nos guiará a través de diferentes ventanas, las cuales harán posible especificar la información del origen de datos, los campos a incluir en el mismo, los diferentes proceso a efectuar, etc. Una vez indicadas las opciones tendremos que hacer clic en Aceptar, lo cual exhibirá la primera página del asistente. Una ventana similar a la de la siguiente figura nos solicitará que especifiquemos el origen o fuente del cual deseamos obtener los datos para confeccionar el informe.

Como es posible apreciar, el número de opciones de conectividad es realmente elevado. Para este ejemplo utilizaremos OLE DB (ADO), aunque más adelante aprenderemos cómo hacer uso de la información obtenida de un origen desconectado mediante un objeto DataSet de ADO.NET. Emplearemos la base de datos Ferreteria, y dependiendo del origen de la misma, será el proveedor que tendrá que seleccionar.

130

La siguiente ventana –similar a la siguiente figura - nos solicitará información sobre cómo deberá ser establecida la conexión. Existen dos posibles opciones de autenticación, la primera es utilizando los usuarios definidos en Windows (Seguridad Integrada), mientras que la segunda es empleando aquellos definidos en SQL Server (o Windows). Esta última es de gran utilidad cuando se desea conectar al motor de datos desde ambientes no-Windows, lo cual no es el caso que nos ocupa en este momento. Debido a ello, haremos clic en Seguridad Integrada, a los efectos de que se empleen los privilegios del usuario actual. Por supuesto que es necesario que el mismo cuente con derechos para poder acceder a la base de Ferreteria. Por último, debemos especificar el nombre del equipo, o (local) si el servidor se encuentra en forma local, y luego indicar Ferreteria en la lista de Bases de datos.

Este bastará para que la conexión a la base pueda ser establecida, por lo que vamos a hacer clic en Finalizar para agregar la conexión a la lista de factibles de utilizar por un informe. El panel de la izquierda ahora exhibirá la conexión al origen Ferreteria, y bastará con hacer clic sobre la misma para ver las diferentes tablas y campos ofrecidos

131

por ella. Algo realmente interesante es que un informe podría basarse en datos de diferentes orígenes, sin que esto influyera en la complejidad de los procesos posteriores. Vamos entonces a expandir Ferreteria, luego dbo, y por último Tablas, a los efectos de acceder a las tablas de la conexión. Para este informe necesitaremos agregar a Clasif (Clasificacion), Producto (Productos), ya que, si recuerda, el objetivo original era el de exhibir todos los Productos, agrupados por su respectiva clasificación, y son éstas las tablas que proveen la información necesaria para este proceso. Para ello, basta con seleccionar las mismas y luego hacer clic en Insertar Tabla, lo que dará un resultado similar al de la siguiente figura:

Las tablas que se involucrarán en el proceso son ahora incluidas en la lista de la derecha con tablas del informe, lo que indica que serán ofrecidas por los demás pasos del asistente. Si vamos a la próxima ventana mediante Siguiente, veremos que los vínculos entre las tablas son automáticamente detectados:

132

Cuando se establece una conexión, el generador de informes no solamente obtiene la información de campos y tipos, sino también las diferentes relaciones y restricciones de las mismas. Esta información es posteriormente utilizada por el generador de informes para –por ejemplo- indicar un vínculo de tipo principal/detalle. Sin embargo, con algunos orígenes no es posible obtener dicha información, por lo que se brinda la posibilidad de indicar ésta en forma manual. Esto es de gran utilidad en dos situaciones, la primera es cuando se desean establecer vínculos diferentes a los originales, y la segunda cuando se tienen tablas que pertenecen a distintos orígenes de datos, y se desea conformar una relación. Haremos clic en Próximo, a los efectos de obtener una ventana similar al de la siguiente figura:

133

La misma nos permitirá seleccionar los campos que integrarán el informe. Si recuerda, la idea original era la de exhibir los datos de Clasificaciones y Productos, por lo que agregaremos los campos de ambas tablas (Clasif y Producto). Una vez hecho esto, haremos clic en Siguiente. La siguiente ventana solicita los campos necesarios para ordenar y agrupar las diferentes filas obtenidas del origen. Debido a que deseamos que los diferentes Productos sean agrupados por su respectiva clasificación, bastará con que indiquemos el campo NomClasif de la tabla de Clasif como elemento agrupador, en forma similar y como exhibe la Figura:

134

Las próximas ventanas brindan la posibilidad de agregar subtotales gráficos, etc., y no haremos uso de éstos en el ejemplo, por lo que haremos clic en Terminar, lo cual dará como resultado que un nuevo módulo de informe será agregado al proyecto, conteniendo características similares a las de la siguiente figura:

C D

A B

135

A continuación se describen las cuatro secciones de la figura: A. Explorador de campos. Permite agregar, modificar o eliminar campos del informe, así como también fórmulas. Es posible mostrar u ocultar esta ventana haciendo clic en el menú de la barra principal Ver, luego Otras Ventanas, y por último Esquema del Documento o CTRL.+ALT+T. B. Ventana principal del informe Exhibe el informe con sus campos y demás integrantes. Éste incluye las siguientes secciones:

1. Encabezado del informe.

Contiene aquellos elementos a ser exhibidos al comienzo del informe.

2. Encabezado de página. Aloja aquellos elementos que deberán ser incluidos en las diferentes páginas que integrarán al informe. Generalmente se incluyen en ésta los títulos de los diferentes campos, logotipo de la empresa, número de página, etc.

3. Encabezados de grupo.

Las secciones de este tipo contienen aquellos elementos que deberán ser exhibidos al comienzo de cada grupo, y generalmente se incluye una descripción del mismo. En la aplicación que realizamos anteriormente se incluía al identificador de autor.

4. Detalles.

Las secciones de este tipo contienen las diferentes filas (y campos) que integran el informe.

5. Pie del informe.

Contiene aquellos elementos a ser exhibidos en la hoja final del informe.

6. Pie de página.

Los elementos aquí adicionados serán exhibidos al final de cada página.

136

C. Barra principal de herramientas del informe Incluye las herramientas necesarias para ordenar, ver las propiedades del objeto seleccionado, etc. D. Barra secundaria de herramientas del informe Incluye las herramientas necesarias para insertar nuevos objetos al informe, así como también agregar nuevos grupos, etc. Las diferentes pautas sobre cómo tendrá que ser obtenida la información y los distintos procesos a realizar sobre ésta han sido guardados en el documento Productos.rpt. Sin embargo, el mismo no mantiene ninguna relación sobre cómo deberá ser exhibido el resultado, ya que éste se remite exclusivamente a almacenar los procesos especificados mediante el asistente. Esto es así con el fin de que un mismo informe pueda ser utilizados para generar su salida a una aplicación para Windows o para Web, sin que se requiera modificaciones, o la creación de informes similares. Antes de pasar a la siguiente sección del libro –en la cual nos encargaremos de exhibir el resultado del informe- haremos una pausa para poder interactuar con los diferentes elementos de la ventana del informe. Exhibiendo el informe Si bien los informes contienen los datos sobre el origen, campos y procesos a realizar, no están asociados con ningún tipo de presentación en especial, y ello tiene como cometido que el mismo pueda ser utilizado en diferentes tipos de proyecto. En el caso de aplicaciones para Windows, se ofrece un control de Windows llamado CrystalReportViewer (Visor de Crystal Report), el cual debe ser dibujado sobre un formulario, a los efectos de poder exhibir el resultado del informe previamente creado. Vamos entonces a agregar uno de éstos al formulario creado por defecto por la plantilla, en forma similar a como muestra la siguiente figura:

137

La forma más fácil de enlazar un informe al control de vista de informe es utilizar la ventana de propiedades. Para ello, el miembro ReportSource juega un rol fundamental, ya que basta con especificar la ruta y nombre del documento que contiene el informe para que el mismo se haga funcional. El resultado en ejecución que se obtendrá después de esto será similar al de la siguiente figura:

138

Como es posible apreciar, el crear y configurar las diferentes propiedades de un informe es una tarea realmente sencilla, pero no obstante, cuenta con dos grandes desventajas: la primera radica en que el mismo debe situarse siempre debajo de la misma ruta, y la segunda es que el origen de datos debe estar accesible de igual forma. Estas son cosas que raramente pasan entre el ambiente de desarrollo y el orden del usuario, por lo que aprenderemos a continuación cómo es posible gestionar este tipo de problemas mediante código. Para cambiar la localización del informe, basta con escribir la propiedad ReportSource con la nueva ruta y nombre de archivo: Sintaxis: ReportSource() As Object Public Sub New() MyBase.New() 'El Diseñador de Windows Forms requiere esta llamada. InitializeComponent() 'Agregar cualquier inicialización después de la llamada a _ InitializeComponent() CrystalReportViewer1.ReportSource = _ "D:\SistemAsist\SistemAsistencia\Productos.rpt"

End Sub

Esta tarea debe ser realizada a continuación de la configuración de los controles (procedimiento New del formulario), ya que si se establece en el evento Load se producirá un error. El segundo punto (modificación del origen de datos) involucra algo más de complejidad, pero nos será de utilidad para explicar la relación entre los informes y el modelo ADO.NET. Creando un informe que utilice ADO.NET Vimos anteriormente que un informe se conectaba a la base de datos a través del manejador o Driver especificado en la ventana de conexión. La misma permitía al desarrollador establecer todos los datos sin necesidad de la escritura de código. A este modelo se le llama Pull (Extraer), ya que cada vez que el informe es visualizado, el mismo se encarga de establecer la conexión, extraer las filas del origen y posteriormente realizar los procesos necesarios para por último mostrar el resultado del mismo. Esta es la forma empleada por defecto, pero no nos es de gran utilidad cuando deseamos especificar un nuevo origen y proveedor en tiempo de ejecución, diferente al que fue establecido originalmente en el informe. Para resolver este problema, los mismos proveen un modelo llamado Push (Introducir), el cual brinda una solución a este problema.

139

El modelo Push se basa en que el desarrollador debe escribir el código necesario para conectarse al origen, y posteriormente obtener un conjunto de filas, las cuales serán luego utilizadas para confeccionar el informe. Esta técnica permite que no solamente se pueda establecer un nuevo origen, sino también realizar algún tipo de proceso intermedio, como filtros, u otros pasos imperiosamente necesarios para la conexión ver la siguiente figura. De esta forma es posible basar un informe en un conjunto de elementos cuyo origen será indicado en tiempo de ejecución. Sin duda, para ello no hay nada mejor que el modelo desconectado propuesto por ADO.NET, y su objeto DataSet, el cual permite mediante un adaptador especificar el lugar de donde deberán ser obtenidas las filas. Sin embargo, el obtener la información durante la ejecución no es suficiente, ya que se deberá tener en tiempo de diseño los diferentes campos y sus respectivos tipos, a los efectos de poder crear el informe. Para ello, existe una característica brindada por el entorno de desarrollo, la cual hace posible obtener la estructura de un conjunto de tablas (campos, llave primaria, etc.), y posteriormente almacenarlas en un documento en formato de esquema XML. Afortunadamente, esta característica es ampliamente utilizada por el generador de informes, ya que permite basar el mismo en este documento, y posteriormente –en tiempo de ejecución- rellenar éste con las filas obtenidas del origen de datos. Para conocer esta nueva aproximación, usaremos el proyecto anterior. El primer paso será el de adicionar un nuevo elemento de tipo DataSet al proyecto, y para ello, haremos clic derecho sobre el proyecto en el Explorador de soluciones, y posteriormente seleccionaremos Agregar – Agregar nuevo elemento, y por último indicaremos el módulo DataSet y haremos clic en Abrir, lo que dará un resultado similar a la siguiente figura:

Base de Datos ADO

.Net Dataset

Crystal Report

140

El mismo contendrá la estructura de las diferentes tablas en las cuales se basará el informe pero nada sobre filas. En tiempo de ejecución, tendremos que cargar de forma programática los diferentes registros de las tablas involucradas, y posteriormente asociarlas al mismo. Vamos ahora a agregar al DataSet las estructuras que emplearemos en el informe, y para ello debemos ir al Explorador de servidores, y luego hacer clic derecho sobre Conexiones de datos y marcar Agregar conexión. El próximo paso será el de escribir los diferentes datos de la conexión, como se exhibe en la figura siguiente, y luego hacer clic en Aceptar.

141

Esto adicionará una nueva entrada a la lista, la cual usaremos para obtener las estructuras necesarias para confeccionar el informe. El próximo paso será el de expandir la misma hasta localizar las tablas de Clasif y Producto y arrastrar las mismas sobre el módulo de DataSet. Esto registrará la información de las tablas, como campos y claves primarias en el mismo en forma similar a como muestra en la siguiente figura. Es necesario que guarde el proyecto para poder continuar con la siguiente etapa.

El próximo paso será el de construir un informe, pero en vez de basar el mismo en una conexión ADO, como vimos anteriormente, emplearemos como fuente el documento creado anteriormente. Para ello vamos a hacer clic derecho sobre el proyecto, para luego agregar un módulo de informe de crystal llamado Productos2.rpt. En el primer diálogo seleccionaremos Mediante el Asistente de Informes, mientras que en el segundo vamos a expandir la opción Datos del Proyecto y luego Conjuntos de datos ADO.NET – Nombre del Proyecto. Esto exhibirá la información de tablas, obteniéndolas del DataSet incluido por el proyecto. Vamos a agregar Clasif y Producto, como exhibe en la siguiente figura. Debemos entonces repetir los mismos pasos que realizamos con el informe en la sección anterior.

142

A simple vista, el resultado final se parece a un informe del modelo Pull (el que confeccionamos anteriormente), pero encierra una diferencia importante, la cual radica en que ahora el mismo se basa en información de estructuras y tipos, pero no almacena nada con respecto al lugar de donde las filas deberán ser obtenidas. Esto último lo realizaremos mediante código utilizando ADO.NET. Si se intenta asociar el mismo a un control de visor de informe y ejecutar la aplicación, el mismo no podrá discernir de dónde obtener los datos, por lo que le solicitará al usuario dicha información. Vamos entonces a realizar la segunda etapa, la cual radica en obtener mediante código el conjunto de filas que empleará el mismo. Los informes Crystal cuentan con un conjunto de clases, las cuales permiten gestionar la totalidad de las funcionalidades de los mismos en forma programática. El primer paso para acceder a ellas fácilmente es siempre el agregar la importación al espacio de funcionalidades del motor de Crystal. Imports CrystalDecisions.CrystalReports.Engine Para manipular un informe en tiempo de ejecución es necesario hacer uso de un objeto llamado ReportDocument, el cual representa a un documento de informe. Dim Informe As New ReportDocument ( )

143

Básicamente, cargaremos en esta variable la definición del informe que creamos en tiempo de diseño, y luego le introduciremos al mismo las filas obtenidas por el origen de datos. Todo esto lo gestionaremos mediante los métodos y propiedades que este objeto nos provee. Primeramente debemos hacer uso del método Load para indicar el informe a utilizar: Sintaxis: Load (<Ruta> As String) Load (<Ruta> As String, <Forma de apertura> As OpenReportMethod) Informe.Load(“C:\Ruta\Productos2.rpt”) Esto alcanza para cargar en la variable la definición del mismo. En los próximos pasos obtendremos el conjunto de filas mediante ADO.NET (aunque puede ser también un Recordset de ADO), y por último asignaremos el mismo a la propiedad SetDataSource, la cual, además de aceptar una ruta a un documento RPT, admite un objeto de este tipo. Veamos entonces cómo se llevará a cabo este proceso: Imports CrystalDecisions.CrystalReports.Engine Imports System.Data.OleDb . . ‘Define un objeto de tipo ReportDocument Dim Informe As New ReportDocument ( ) ‘Carga la definición del Informe Informe.Load (“C:\MiRuta\MiInforme.rpt”) ‘Aquí se debe Abrir conexión y cargar objeto DataSet ‘Asigna el contenido resultante mediante la propiedad SetDataSource

Informe.SetDataSourse (<Recordset o DataSet>)

Con esto bastaría para que el origen y contenido de las tablas puedan ser especificados desde código. El último paso es siempre el de asignar el documento resultante al control de visor de informe. Me.CrystalReportViewer1.ReportSource = Informe Veamos entonces cómo sería el ejemplo completo empleando ADO.NET: Public Sub New() MyBase.New() 'El Diseñador de Windows Forms requiere esta llamada. InitializeComponent() 'Agregar cualquier inicialización después de la llamada a InitializeComponent() Dim Informe As New ReportDocument()

144

Dim adClasif As OleDbDataAdapter Dim adProducto As OleDbDataAdapter Dim ds As New DataSet() ' Crea un adaptador para cada tabla, los cuales podrian ser de diferentes ' Origenes de datos adClasif = New OleDbDataAdapter("Select * From Clasif", Cn) adProducto = New OleDbDataAdapter("Select * From Producto", Cn) 'Carga el DataSet adClasif.Fill(ds, "Clasif") adProducto.Fill(ds, "Producto") 'Carga el informe conteniendo la estructura del mismo Informe.Load("D:\SistemAsist\SistemAsistencia\Productos2.rpt") Informe.SetDataSource(ds) CrystalReportViewer1.ReportSource = Informe End Sub Nota: Tenga en cuenta que la ruta especificada en el método Load puede ser diferente en su ordenador. La ejecución de este código dará como resultado la información sobre Productos y Clasificaciones. En esta sección hemos aprendido sobre dos nuevas características, la primera sobre cómo se debe gestionar la obtención del conjunto de filas de un informe en tiempo de ejecución, y la segunda –y más importante- la relación entre ADO.NET y un módulo de informe Crystal.

145

Código de Barras

Poner un simple código de barras es una serie de rayas verticales negras y blancas, que pueden ser leídos por un lector de código de barras. Las líneas verticales negras y blancas contienen la clave de un producto, con la cual podremos acceder a la información de este en la base de datos, como su precio, peso, y tamaño. Una vez que es leído este se convierte en letras y números. Preferible que tener un cajero que tenga que teclear un número de 20 dígitos para cada producto. Por las necesidades particulares y demandas de varias industrias existen diferentes tipos de código de barras que pueden ser usados. Por ejemplo, el código UPC es usado para Cds, productos, y revistas (pero no está limitado para estos), mientras que el código 39 es usado en tiendas de renta de videos, identificación de tarjetas y para etiquetas. El código 128 es usado frecuentemente para el embarque industrial. En este documento nos concentraremos en el código de barras Interleaved 2 de 5. Cómo Generar el Código de Barras Interleaved 2 de 5 (cadena numérica) Este código se usa principalmente dentro de industrias y en almacenes industriales. Para hacer uso de este tipo de código de barras usaremos el tipo de letra i2of5txt.ttf, el cual deberá instalar en el panel de control. El código Interleaved 2 de 5 es un método compacto de codificar una secuencia de caracteres numéricos; solamente dígitos del 0 al 9 puede ser codificados, pero dentro de una cadena muy larga. Todos los códigos Interleaved 2 de 5 tiene un número fijo de caracteres numéricos. Por consiguiente un código de barras con un número impar de dígitos debe ser rellenado con un cero al inicio. El código de barras usa un carácter de Inicio al principio y un carácter de paro al final, para delimitar los dígitos codificados. Cada letra con el Font está representada con 2 dígitos del 00 al 99. Divide la cadena de números en pares de dígitos y convierte estos en código de barras usando la siguiente formula: A + ABS(A<=49)*48 + ABS(A>=50)*142 Donde A representa el par de dígitos a codificar. Los pares de números que vayan del 00 al 49 son convertidos a caracteres números en el rango de 48 (0x30) a 97 (0x61) mientras que los siguientes del 50 al 99 son convertidos en el rango de 192 (0xC0) al 241 (0xF1). El carácter de inicio es 40 (0x28) y el de paro es 41 (0x29).

146

El siguiente código muestra la fórmula (para implementar el interleaved 2 de 5) que se tiene que insertar en Crystal Reports, con sintaxis de visual (en la esquina superior derecha del Crystal Report).: Dim StartCode, StopCode, DataToPrint, DataToEncode As String Dim I, CurrentChar as number ‘ El numero a codificar esta fijo pero se puede colocar el nombre de un campo de la base de datos DataToEncode = "2002141520" DataToPrint = "" StartCode = Chr(40) StopCode = Chr(41) For I = 1 To Len(DataToEncode) Step 2 CurrentChar = Val((Mid(DataToEncode, I, 2))) DataToPrint = DataToPrint & Chr((CurrentChar + Abs(Iif((CurrentChar <= _ 49),1,0)) * 48 + Abs(Iif((CurrentChar >= 50),1,0)) * 142)) Next I Formula = StartCode + DataToPrint + StopCode

147

Informes en Aplicaciones para la Web La tecnología de informes utilizada en la plataforma .NET y provista por la empresa Crystal Decisions es sumamente flexible, ya que brinda la posibilidad de ser empleada en una aplicación Web en forma similar a una para Windows. La ventaja más importante es que el generador de informes hace uso de los recursos del servidor en vez del de cada cliente. Debido a que los mismos son publicados en formato HTML, el resultado puede ser visto desde cualquier explorador. Como beneficio adicional y debido a que la definición del informe no mantiene vinculación con la forma en la cual éste será presentado un mismo módulo puede ser empleado por una aplicación para la Web y Windows al mismo tiempo, lo que hace muy fácil su reutilización. Vamos ahora a crear un nuevo proyecto de aplicación Web ASP.NET llamado VentasProductos, a los efectos de demostrar las características bajo este modelo. Una vez hecho esto, adicionaremos un nuevo reporte llamado VentasProd.rpt, para el cual seguiremos las mismas pautas que en el informe de Windows. Nuevamente contamos con un archivo con extensión rpt conteniendo los diferentes campos y procesos del informe, pero aún no hemos terminado la forma en que éste se hará disponible. Para que cada página de servidor activo ASP.NET pueda ofrecer el mismo, es necesario emplear el control de visor de informes para formularios Web. Este último se encuentra en la caja de herramientas, generalmente localizado al final de la misma. Dibujaremos entonces el control en forma similar a como se muestra en la siguiente figura:

El próximo paso será el de vincular la definición del informe con el control Web. La propiedad ReportSource no existe en la ventana de propiedades, o por lo menos no de la misma forma que vimos en aplicaciones para Windows. Para poder acceder a la misma, necesitamos ir a la ventana de propiedades, y posteriormente hacer clic sobre DataBindings, la cual exhibirá una ventana similar a la de la siguiente figura:

148

La misma ofrece las distintas propiedades factibles de ser enlazadas a datos, o lo que nos interesa en este caso, a un documento de informe. Para realizar esta tarea es necesario marcar la propiedad ReportSource, y luego hacer clic en Expresión de enlace personalizado, lo que nos permitirá establecer el origen o ruta al informe, en forma similar al de la siguiente figura:

Cuidado

149

Una vez hecho esto, bastará con escribir entre comillas la ruta física completa al documento en la caja de texto situada en la parte inferior, y luego dar clic en aceptar. El resultado final es interesante, ya que adquiriremos en tiempo de diseñó la estructura de campos que tendrá el informe. Por ultimo deberá invocar al método DataBind del visor de reportes en el evento load de la página, como a continuación se muestra: Private Sub Page_Load(ByVal sender As System.Object, ... 'Introducir aquí el código de usuario para inicializar la página CrystalReportViewer1.DataBind() End Sub

150

Módulo 7. Hilos Threads Un hilo, denominado también proceso ligero, es una unidad básica de utilización del CPU; comprende un ID del hilo, un contador de programa, un conjunto de registros y una pila. El hilo comparte con otros hilos que pertenecen al mismo proceso su sección de código, su sección de datos y otros recursos del sistema operativo, como los archivos abiertos y señales. Debido a que tiene varios hilos de control, el proceso puede efectuar más de una tarea a la vez. La siguiente figura ilustra la diferencia entre un proceso tradicional de un solo hilo y un proceso multihilos. Mucho del software que se ejecuta en una Pc de escritorio moderna es mutihilos. Una aplicación se implementa típicamente como un proceso separado con varios hilos de control. Un navegador de red tiene un hilo que muestra imágenes o texto, en tanto otro hilo recupera datos de la red. Un procesador de palabras tiene un hilo para mostrar gráficos, otro hilo para leer las pulsaciones del usuario en el teclado y uno más para hacer una verificación de escritura y ortografía trabajando en background. En resumen los usos más típicos de las aplicaciones Multihilos son:

♦ Comunicación con dispositivos de acceso lento, sin necesidad de bloquear la aplicación durante dicho proceso.

♦ Ejecución de tareas en background, como limpieza de archivos no utilizados, seguimientos de las acciones realizadas por el usuario, etc.

♦ Inspección, actualización y exhibición del avance del algún proceso especifico.

♦ Realización de tareas que deban ser necesariamente realizadas a igual tiempo.

Crear y usar subprocesos Se puede crear un subproceso nuevo en Visual Basic .NET declarando una variable del tipo System.Threading.Thread y llamando al constructor con la instrucción AddressOf y el nombre del procedimiento o método que se desea ejecutar en el subproceso nuevo. El código siguiente proporciona un ejemplo: Dim MyThread As New System.Threading.Thread(AddressOf MiProcedimiento)

Código Datos Archivo

Un solo Hilo

Código Datos Archivo

Multihilos

Hilos

151

Para iniciar la ejecución de un nuevo subproceso, utilice el método Start, como en el código siguiente:

MyThread.Start() Para detener la ejecución de un subproceso, utilice el método Abort, como en el código siguiente:

MyThread.Abort()

Además de iniciar y detener subprocesos, también se puede realizar pausas en los subprocesos llamando a los métodos Sleep o Suspend, reanudar un subproceso suspendido con el método Resume y destruir un subproceso mediante el método Abort, como en el código siguiente:

MyThread.Sleep () MyThread.Suspend () MyThread.Abort ()

Desde otras partes de la aplicación es posible consultar si un hilo se está ejecutando o no, haciendo uso de la propiedad isAlive, la cual retornará falso en el caos de que el hilo no haya sido iniciado o haya sido destruido: If Hilo.IsAlive = False Then ‘Implementación End If Prioridades de los subprocesos Cada subproceso tiene una propiedad de prioridad, que determina qué porción de tiempo tarda en ejecutarse. El sistema operativo asigna mayores porciones de tiempo a los subprocesos de alta prioridad que a los subprocesos de baja prioridad. Los subprocesos nuevos se crean con el valor Normal, pero se puede ajustar la propiedad Priority a cualquiera de los otros valores de la enumeración System.Threading.ThreadPriority, estos se muestran en la siguiente tabla:

Prioridad Descripción AboveNormal Por encima del tiempo normal BelowNormal Por debajo del tiempo normal. Highest Valor más alto posible. Nota: Tener cuidado con este, ya que

puede bloquear la aplicación si no se utiliza correctamente. Lowest Puede programarse después de los subprocesos que tengan

cualquier otra prioridad. Normal Puede programarse después de los subprocesos con

prioridad AboveNormal y antes que los subprocesos con prioridad BelowNormal. Los subprocesos tienen prioridad Normal de forma predeterminada.

152

Subprocesos de primer plano y de fondo La propiedad isBackGround es fundamental, ya que indica que los hilos creados serán, de segundo plano. Esto no afecta al rendimiento de los mismos, simplemente le indica CLR que en el caso de que la aplicación sea cerrada, los hilos marcados como de fondo serán también destruidos. Si no se configura esta propiedad, CLR no podrá liberar los recursos en forma adecuada al finalizar la ejecución de la aplicación. Para demostrar esto vamos a crear un nuevo proyecto de aplicación Windows llamado CarreraCaballos, a los efectos de demostrar los puntos ya tratados. El mismo consistirá en una verdadera carrera de caballos, en la cual cada uno de éstos le estará dando vida a un hilo de ejecución. Adicionaremos los siguientes controles, como se muestra en la siguiente tabla:

Objeto Propiedad Valor TrackBar ID Caballo1 Máximo 100 TrackBar ID Caballo2 Máximo 100 Button ID Iniciar Text Iniciar Button ID Parar Text Parar Button ID Continuar Text Continuar Button ID Destruir Text Destruir Cuando se haga clic en el botón de Iniciar, se crearan dos hilos de ejecución asociados a dos procedimientos. Cada uno de estos se encargará de atender a un control en particular. Veamos el código: Imports System.Threading Public Class Form1 Inherits System.Windows.Forms.Form Dim Hilo1 As New Thread(AddressOf IniciarCaballo1) Dim Hilo2 As New Thread(AddressOf IniciarCaballo2) Private Sub IniciarCaballo1() Dim i, j As Integer Try For i = 1 To 100 Caballo1.Value = i For j = 1 To 1000000 Next Next Catch eX As ThreadAbortException MessageBox.Show("El Hilo del primer caballo fue destruido")

153

End Try End Sub Private Sub IniciarCaballo2() Dim i, j As Integer Try For i = 1 To 100 Caballo2.Value = i For j = 1 To 1000000 Next Next Catch eX As ThreadAbortException MessageBox.Show("El Hilo del segundo caballo fue destruido") End Try End Sub Private Sub Iniciar_Click(. . . Hilo1 = New Threading.Thread(AddressOf IniciarCaballo1) Hilo2 = New Threading.Thread(AddressOf IniciarCaballo2) Hilo1.IsBackground = True Hilo1.Priority = ThreadPriority.Lowest Hilo2.Priority = ThreadPriority.Lowest Hilo2.IsBackground = True Hilo1.Start() Hilo2.Start() End Sub Private Sub Continuar_Click(. . . Hilo1.Resume() End Sub Private Sub Parar_Click(. . . Hilo1.Suspend() End Sub Private Sub Destruir_Click(. . . Hilo1.Abort() End Sub Problemas por el Uso de Múltiples Hilos Existen algunos problemas que son derivados de la utilización de múltiples hilos en una aplicación, los cuales constituyen un nuevo conjunto de inconvenientes a tener en cuenta. Muchas de las tareas que son comúnmente realizadas utilizan recursos compartidos, como por ejemplo una estructura, un archivo, una variable, etc. En el modelo monotarea o tradicional no existen mayores inconvenientes, dado que sólo una sección de la aplicación accederá al recurso a la vez, por lo que esto se considera seguro. En el modelo multitarea las cosas son diferentes, debido a que múltiples trozos de código podrían estar accediendo al mismo recurso a igual tiempo. Como ejemplo, dos procedimientos podrían requerir acceso de la lectura o escritura de una misma variable a igual tiempo.

154

Lamentablemente, el lenguaje solamente asegura en algunos pocos casos que la tarea será realizada exitosamente. Ello es debido principalmente a que este tipo de procesos no son realizados por el procesador ni por CLR en forma atómica, y es allí donde debe intervenir el desarrollador para asegurar que las mismas sean efectuadas correctamente. Dependiendo del procesador, la modificación a una variable que utilice más de 32 bits (4 bytes) es gestionada en dos etapas. Esto es debido a que el mismo puede manipular registros de un máximo de este largo. Por otra parte, muchas de las tareas efectuadas por CLR son también realizadas en dos pasos. Por ejemplo, cuando se modifica el valor contenido por una variable, el mismo formaliza dicha tarea mediante tres pasos:

1. Lee y obtiene el contenido de la variable. 2. Modifica el valor. 3. Escribe el nuevo valor en la variable.

Ambos casos funcionan correctamente en el modelo monotarea, dado que las mismas son accedidas por un trozo de código a la vez, pero en el caso de que se utilicen múltiples hilos, el resultado final podría ser catastrófico. Si, por ejemplo, una variable está siendo modificada por un procedimiento, y el tiempo de procesador para el hilo asignado al mismo expira, en forma automática el sistema operativo pasará el control al próximo hilo. Si quien toma el control modifica el misma variable, y posteriormente su tiempo de procesador finaliza, el primer hilo continuará su tarea de escritura de la variable en forma habitual, con el valor que el mismo tenía originalmente almacenado, generándose así que la modificación efectuada por el segundo hilo sea descartada. Para enmendar esto, el espacio System.Threading brinda un amplio conjunto de clases para evitar que este tipo de situaciones ocurra, pero en todos los casos debe ser el desarrollador quien coordine esta tarea. Básicamente, todas ellas funcionan como un semáforo de tránsito, permitiendo que unos pasen y otros tengan que esperar su turno.

Tiempo1 Tiempo2

Variable

Hilo

155

Sincronización mediante Synclock Esta sincronización funciona en forma similar a un semáforo de trafico, cada vez que un objeto desea realizar un conjunto de tareas, el mismo deberá anteriormente fijarse en si el recurso se encuentra disponible (o no bloqueado). Si un hilo está modificando una o más variables, y otro hilo desea efectuar la misma tarea, entonces este último será puesto automáticamente en lista de espera hasta que el primero finalice.

Class Cache Private Shared Sub Add(ByVal x As Object) SyncLock GetType(Cache) End SyncLock End Sub Private Shared Sub Remove(ByVal x As Object) SyncLock GetType(Cache) End SyncLock End Sub End Class

156

Ejemplo Filósofos del Comedor Los Filósofos del comedor son generalmente utilizados para ilustrar problemas que pueden ocurrir cuando varios hilos sincronizados compiten por recursos. La historia transcurre así: cinco Filósofos están sentados alrededor de una mesa, y frente a cada uno de ellos hay un plato de arroz. Entre cada filosofo hay ademas un palillo chino. Antes de que alguno de los mismos pueda comenzar a comer, el filosofo debe contar con dos palillo chinos, uno que haya tomado del lado izquierdo y otro de la derecha. Los Filósofos deben encontrar entonces la manera de compartir los palillos de forma que todos puedan comer. Este particular algoritmo funciona de la siguiente manera: el filosofo busca primero el palillo de su derecha. Si el mismo está allí lo toma y levanta su mano derecha. Después intenta tomar el palillo de si izquierda y, si está disponible, el filósofo lo toma y levanta la otra mano. Ahora éste tiene ambos palillo, por lo que puede probar un poco de arroz. Después de realizar dicha tarea, el filósofo deberá dejar ambos palillos sobre la mesa, permitiendo así que algunos de sus dos vecinos los tome. Posteriormente la historia se repetirá, ya que probará otra vez con el palito a su derecha, etc. Entre cada intento de tomar un palito, cada filosofo deberá hacer una pausa por un periodo al azar. Crear un nuevo proyecto para Windows y en una forma hay que adicionar los siguientes controles:

Objeto Propiedad Valor ListBox ID Eventos Button ID IniciarBanquete Text Iniciar el Banquete

157

Una vez efectuado esto, agregaremos una clase denominada Filosofo, la cual representará al mismo con todas sus características, a continuación se muestra el código para esta clase: Imports System.Runtime.Remoting Imports System.Threading 'Implementa un Filósofo que come Public Class Filosofo 'Referencia al Palito Izquierdo que debe tomar Private _PalitoIzq As String 'Referencia al Palito Derecho que debe tomar Private _PalitoDer As String 'Referencia al ListBox en que el método Comer desplegará los mensajes Private _Mesa As ListBox 'Nombre del filósofo Private _Nombre As String 'Propiedades Public ReadOnly Property PalitoIzquierdo() As String Get Return _PalitoIzq End Get End Property Public ReadOnly Property PalitoDerecho() As String Get Return _PalitoDer End Get End Property Public ReadOnly Property Nombre() As String Get Return _Nombre End Get End Property 'Constructor Public Sub New(ByVal Nom As String, ByRef Mesa As ListBox, ByRef PalitoIzq As String, ByRef PalitoDer As String) 'Referencia al Palito Izquierdo que debe tomar _PalitoIzq = PalitoIzq 'Referencia al Palito Derecho que debe tomar _PalitoDer = PalitoDer 'Referencia al Listbox donde el método Comer desplegará los mensajes _Mesa = Mesa 'Nombre del filósofo _Nombre = Nom End Sub

158

'Método Comer Public Sub Comer() 'Hacer Do 'Intenta tomar el Palito Izquierdo que le corresponde SyncLock PalitoIzquierdo 'Si lo consigue despliega los mensajes en el ListBox _Mesa.Items.Add(Nombre & ": Tengo el palito izquierdo") _Mesa.Items.Add(Nombre & ": Estoy esperando por el derecho") 'Intenta tomar el Palito Derecho que le corresponde SyncLock PalitoDerecho 'Si lo consigue despliega los mensajes en el ListBox _Mesa.Items.Add(Nombre & ": Tengo el palito derecho") _Mesa.Items.Add(Nombre & ": Estoy comiendo!!! Mmmm") 'Come, demorando un tiempo aleatorio Dim i As Integer For i = 0 To CalcularTiempoEspera() 'Demora en comer Next i 'Suelta el Palito Derecho End SyncLock 'Suelta el Palito Izquierdor End SyncLock 'Despliega un mensaje notificando que terminó de comer _Mesa.Items.Add(Nombre & ": Terminé de comer") Dim j As Integer For j = 0 To CalcularTiempoEspera() 'Demora volver a intentar tomar un palito Next j Loop End Sub 'Retorna un entero aleatorio Private Function CalcularTiempoEspera() As Integer Dim TiempoMáximo As Integer 'Calcula el tiempo al azar en que demora en comer ' Inicializa el generador de números aleatorios. Randomize() ' Genera números aleatorios entre 100000000 y 103000000. 'Utiliza números grandes para que coman más lento TiempoMáximo = CInt(Int((3000000 * Rnd()) + 100000000)) Return (TiempoMáximo) End Function

159

End Class Código para la forma: Imports System.Threading Public Class Form1 Inherits System.Windows.Forms.Form 'Palitos disponibles para comer 'Son Strings ya que SyncLock necesita un objeto referenciable como semáforo Private Palitos() As String = {"Palito 1", "Palito 2", "Palito 3", _ "Palito 4", "Palito 5"} Private Sub IniciarBanquete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles IniciarBanquete.Click 'Declara e instancia a los comensales 'asignándoles un nombre, una referencia al ListBox 'y cual los Palitos correspondientes a utilizar Dim Juan As New Filosofo("Juan", Eventos, Palitos(0), Palitos(1)) Dim Erick As New Filosofo("Erick", Eventos, Palitos(1), Palitos(2)) Dim Abraham As New Filosofo("Abraham", Eventos, Palitos(2), Palitos(3)) Dim JuanPablo As New Filosofo("Juan Pablo", Eventos, Palitos(3), Palitos(4)) 'En este caso se invierte el orden de los Palitos ,(0, 4) en lugar de como seguría (4, 0) 'para evitar un DeadLock 'ya que tarde o temprano todos tomarían un Palito Izquierdo y todos quedarían esparando 'por los demás Dim Aristóteles As New Filosofo("Aristóteles", Eventos, Palitos(0), Palitos(4)) 'Declara e instancia los hilos de ejecución 'que ejecutarán el método Comer de cada Filósofo Dim Hilo1 As New Thread(AddressOf Juan.Comer) Dim Hilo2 As New Thread(AddressOf Erick.Comer) Dim Hilo3 As New Thread(AddressOf Abraham.Comer) Dim Hilo4 As New Thread(AddressOf JuanPablo.Comer) Dim Hilo5 As New Thread(AddressOf Aristóteles.Comer) 'Configura los hilos Hilo1.IsBackground = True Hilo2.IsBackground = True Hilo3.IsBackground = True Hilo4.IsBackground = True Hilo5.IsBackground = True Hilo1.Priority = ThreadPriority.Lowest Hilo2.Priority = ThreadPriority.Lowest Hilo3.Priority = ThreadPriority.Lowest

160

Hilo4.Priority = ThreadPriority.Lowest Hilo5.Priority = ThreadPriority.Lowest 'Inicia la ejecución de los hilos Hilo1.Start() Hilo2.Start() Hilo3.Start() Hilo4.Start() Hilo5.Start() End Sub End Class

161

Modulo 8: ASP .NET Introducción ASP .NET es la nueva tecnología propuesta por Microsoft para enfrentar los desafíos de interconexión entre dispositivos y sitios Web del nuevo milenio. En versiones anteriores de Visual Basic, las opciones brindadas para la programación de estos últimos no contaba con las ventajas que ofrecían otras herramientas, como visual InterDev. En esta versión, ahora se cuenta con un excelente editor de paginas para servidor activo ASP .NET y HTML, el cual viene incluido por el entorno de desarrollo como diseñador natural. Adicionalmente se han agregado varias características, a los efectos de que la programación de una aplicación para la Web cuente con todas las funcionalidades del lenguaje, y además se efectúe en forma similar a como se hacía bajo el modelo Windows. Las técnicas brindadas para dibujar un formulario de Windows no difieren en mucho de las ofrecidas para realizar la misma tarea en una página Web. Ahora se cuenta con un nuevo tipo de proyecto llamado aplicación Web ASP .NET, el cual hace uso de formularios Web o Web Forms, los cuales cumplen un rol similar al de los formularios estándares en el modelo Windows. Básicamente un formulario Web es exhibido de igual forma que uno Windows, pero, en realidad, el primero es gestionado internamente como una página de servidor activo y etiquetas HTML. La creación de la interfaz es muy sencilla, ya que basta con crear un nuevo proyecto de este tipo, y posteriormente arrastrar los controles del cuadro de herramientas, para así crear una nueva interfaz gráfica para la Web. En ASP .NET se lleva a cabo la separación del código y la información de presentación. Estructura Web Form Una de las características más destacables es la separación del código y la información de presentación. En la versión anterior ASP se utilizaba el mismo archivo para almacenar las etiquetas que definían la presentación y el código Script asociado a cada una de ellas. Por el contrario, en ASP .NET pueden emplearse dos archivos independientes, uno para la presentación y otro para contener la implementación lógica de los diferentes eventos, lo que permite que ambas partes puedan ser tratadas en forma independiente. El archivo que contiene la información de la interfaz tiene una extensión (*.aspx) y el otro el código (*.aspx.vb) para poder apreciar esto, basta con hacer clic en mostrar todos los archivos del explorador de soluciones.

162

Comparación entre aplicaciones Windows y Web. Característica Aplicaciones Windows Aplicaciones para Web Localización y ejecución

La aplicación se contiene dentro de una o más carpetas, y requiere solamente del soporte de la infraestructura .NET .

La aplicación se contiene dentro de uno o más directorios virtuales, y utilizará los servicios de Internet Information Server (IIS) y la infraestructura .NET para ejecutarse. Para utilizar la aplicación se requiere de un explorador capaz de entender HTML.

Instalación La instalación se realiza típicamente mediante un instalador. La mayor parte de la veces no requiere una conexión a una red interna o externa, y en el caso de que se deseen actualizar varios equipos se debe ejecutar el instalador tantas veces como los ordenadores lo requieran.

No requiere instalación en cada ordenador que desee hacer uso del mismo, aunque sí se necesita un explorador y conexión a la red. En el caso de que desee actualizar la aplicación, la tarea debe efectuarse solamente en el servidor.

Interfaz La interfaz creada invocando funciones internas de Windows. Se cuenta con cientos de controles para realizar la misma. Para situar los mismos dentro de la pantalla, se utilizan coordenadas X e Y expresadas en pixeles. El resultado final puede verse igual en los deferentes sistemas operativos de Windows.

La interfaz es creada mediante etiquetas HTML. Se cuenta con una decena de controles para confeccionar la misma. Para situarlos dentro de la pantalla, se debe emplear el sistema de coordenadas de HTML o DHTML en el caso de que el explorador lo soporte. El resultado final puede verse parecido en diferentes exploradores pero no igual.

Gráficos Los gráficos pueden ser creados mediante la utilización de GDI+

Los gráficos pueden ser creados mediante la utilización de GDI+ pero éste debe ser ejecutado del lado del servidor.

Ejecución de Eventos

La ejecución del código asociado a cada evento se produce tan pronto el mismo es iniciado, lo que ofrece una respuesta interactiva.

Los eventos no siempre son iniciados en forma automática, aunque si el explorador es Internet Explorer 5.0 o superior ASP .NET puede emplear en algunos casos DHTML, a los efectos de efectuar el evento en forma local (esto está sólo disponible en los controles de validación).

Acceso a recursos locales (memoria, archivos, red, etc.)

Las aplicaciones para Windows pueden tener acceso a los recursos, aunque existe la posibilidad de restringir los mismos.

El explorador restringe los recursos a los cuales la aplicación tendrá acceso.

Escalabilidad Es medianamente escalable, y se deben tener en cuenta los costos de instalación y/o actualización.

Es sumamente escalable, y el costo de instalación no es fuerte, ya que se debe efectuar una sola vez por versión de aplicación.

Mercado Clientes que utilizan sistema operativo Windows.

Clientes que utilizan cualquier sistema operativo.

163

Eventos en Formularios Web Los eventos en ASP .NET brindan la sensación al usuario de que se producen en el mismo lugar donde se sitúa la interfaz gráfica; sin embargo, la realidad es que ambas partes están ubicadas en diferentes lugares. La implementación de una aplicación para Windows reside en el mismo ordenador, y utiliza funciones internas del sistema operativo para crear sus ventanas y controles. A diferencia, las aplicaciones ASP .NET crean la interfaz en el servidor a través de una pagina, la cual posteriormente es enviada al explorador o cliente a través del protocolo http, y empleando como formato el archivo html. http es el protocolo empleado por Internet para transmitir información de una maquina a otra. Básicamente, el mismo se identifica por la utilización del prefijo http:// seguido de una dirección conformada por varias partes separadas por punto. A esto se le denomina comúnmente dirección Web o URL. A grandes rasgos, cuando se introduce una dirección de este tipo dentro de un explorador, el mismo genera un paquete de http que se compone de dos partes: el encabezado y el cuerpo. El encabezado incluye la sección Get, seguida de la información a donde se desea ir, o sea la dirección Web introducida, mientras que el cuerpo se deja generalmente en blanco. Una vez realizada esta tarea, se envía el paquete al servidor de Internet, efectuando algunos pasos previos. A este tipo de proceso se le llama petición http, debido principalmente a que solicita un recurso a un servidor. Adicionalmente, el explorador incluye en el encabezado su versión, la versión http utilizada, e información de remitente. Una vez que el paquete llega al servidor, el mismo extrae los datos e identifica el recurso al cual se hace referencia (pagina, archivo, etc.). Posteriormente arma uno o varios paquetes con igual conformación (encabezado y cuerpo) y carga en el cuerpo la información requerida, la cual posteriormente es enviada al cliente identificándolo por los datos del remitente cargados inicialmente. El servidor también adiciona en el encabezado varios datos más. A todo esto se le denomina método Get, dado que es la forma que emplea el explorador para solicitar un recurso Web. Cuando el paquete llega al cliente, el explorador se toma el trabajo de extraer los datos del cuerpo del mensaje y los exhibe. También pueden existir varias peticiones para satisfacer una página, en el caso de que la misma contenga elementos binarios, los cuales deben ser obtenidos nuevamente del servidor mediante un proceso similar. Adicionalmente, existe una funcionalidad ofrecida por http, la cual hace posible enviar al servidor no solamente información de la petición de un recurso, sino también el contenido de la página actual que tiene el explorador. Esto es muy común cuando se desea enviar a otra pagina valores de la página activa, como por ejemplo los datos contenidos por las cajas de texto. Para dicho fin, http provee una alternativa diferente, la cual permite cargar en el encabezado del mensaje la página solicitada, y en el cuerpo el contenido de los diferentes controles. A esto se le llama método Post, dado que permite enviar información al servidor. Este tipo de funcionalidades son gestionadas por HTML mediante la utilización de

164

elementos o marcas especificas, las cuales permiten delimitar aquellos controles a ser enviados del explorador al servidor. Relación del método Post con los eventos del Formulario Web Cada vez que un evento es iniciado en un formulario Web – por ejemplo el clic- el mismo no es ejecutado localmente dentro del explorador, sino que es enviado al servidor que es donde reside la implementación del mismo. Esto sucede gracias a que cada evento clic produce un método Post, el cual hace que se lleve a cabo una ida al servidor, enviando el contenido de los controles de la página. Por supuesto que el mismo apunta a la misma página que ya esta viendo el usuario en el explorador, ya que de lo contrario se cambiaría de página al iniciarse cada uno de los eventos. Una vez que la información es recibida por el servidor, el mismo identifica al recurso que inicio el evento, y posteriormente localiza la implementación del mismo y la ejecuta. Por último crea nuevamente la página y la envía al explorador, incluyendo las modificaciones necesarias realizadas por el código ejecutado. Por ello, cada vez que un evento es iniciado por ASP .NET, la página es completamente generada, lo que involucra una ida y vuelta al servidor. Esto da como resultado que la interactividad de una aplicación Web sea siempre menor que la de una aplicación para Windows. Esto es un punto importante que debe ser considerado en conjunto con el usuario final, a los efectos de que conozca los diferentes beneficios que obtendrá bajo esta tecnología, así como sus restricciones. El proceso que anterior es realizado de forma trasparente por ASP .NET, lo que brinda al desarrollador y usuario la sensación de que el evento es producido dentro del mismo equipo. No obstante, hay algunas tareas que realiza ASP .NET con el fin de minimizar las idas y vueltas al servidor de Internet, y así redundar en un mejor rendimiento. Una de las más importantes radica en que muchos de los eventos son dejados en espera (o encolados) hasta que otro evento sea iniciado, con el fin de que ambos sean procesados en forma conjunta. Vamos a aprender el funcionamiento de los eventos en espera, y para ello creamos un nuevo proyecto de aplicación Web ASP .NET llamado EventosenEspera, al cual agregaremos los controles de acuerdo con la siguiente tabla:

Objeto Propiedad Valor Label Text Productos CheckBoxList ID ListaDeProductos Items-Agregar-Text Monitor Items-Agregar-Text Teclado Items-Agregar-Text Mouse Items-Agregar-Text CD Label Text Productos Seleccionados Label ID ProductosSeleccionados

165

Label Text Productos Pedidos Label ID ProductosPedidos Button ID Pedir Text Pedir La aplicación permitirá al usuario seleccionar uno o más Productos, los cuales serán exhibidos en la etiqueta de ProductosSeleccionados, cada vez que éste haga clic sobre un botón de verificación de la lista de productos. Una vez finalizada la selección, podrá pedir los mismas haciendo clic sobre el botón Pedir. Esto dará como resultado que se muestre nuevamente la selección, pero esta vez en la etiqueta de Productos Pedidos. Vamos a agregar el código como a continuación se muestra: El evento SelectedIndexChanged de la lista se dispara cuando se selecciona un producto de la lista. El cual se encargará de copiar la selección del usuario a la etiqueta de productos seleccionados. Private Sub ListaDeProductos_SelectedIndexChanged(… Dim i As Integer ProductosSeleccionados.Text = "" For i = 0 To ListaDeProductos.Items.Count - 1 If ListaDeProductos.Items(i).Selected = True Then ProductosSeleccionados.Text += ListaDeProductos.Items(i).Text + " - " End If Next End Sub El botón de Pedir realizará la misma tarea, pero exhibirá los resultados en la etiqueta ProductosPedidos en vez de ProductosSeleccionados. Private Sub Pedir_Click(… Dim i As Integer ProductosPedidos.Text = "" For i = 0 To ListaDeProductos.Items.Count - 1 If ListaDeProductos.Items(i).Selected = True Then ProductosPedidos.Text += ListaDeProductos.Items(i).Text + "-" End If Next End Sub Al ejecutar la aplicación el resultado final no es el esperado debido a que el evento clic del control CheckBoxList es ejecutado solamente cuando se hace clic sobre el botón Pedir. Esto es debido a que ASP .NET deja en <<espera>> algunos eventos, hasta que se inicien otros. Lo importante a tener en cuenta de este ejemplo es que algunos eventos pueden ser iniciados en forma inmediata; no obstante, existe la posibilidad de forzar a que ellos sean enviados tan pronto como se efectúen mediante la propiedad

166

AutoPostBack, la cual si se establece en verdadero, forzará a que se ejecute el evento tan pronto como el mismo sea iniciado. Por supuesto esta propiedad debe ser empleada únicamente en casos extremos, dado que puede reanudar en un consumo innecesario de ancho de banda y CPU del Servidor Web.

Por otro lado, es importante destacar que hay muchos eventos que no están disponibles en el modelo Web, como aquellos producidos por el movimiento del ratón o la introducción de caracteres. La utilización de cajas de dialogo (msgbox) en las paginas Web no es posible por que estas utilizan recursos del Windows. Si se intenta, obtendrá como resultado un error en tiempo de ejecución.

167

Propiedades, métodos y eventos de páginas Web. Desde el punto de vista de ASP .NET una pagina es un objeto que gana o hereda sus funcionalidades de una clase llamada Page, la cual sirve como contenedor para otro controles. La idea es la de proveer mediante este objeto un modelo similar al propuesto por Windows. Toda página es entonces un objeto derivado de Page, el cual cuenta con varios eventos, propiedades y métodos. Los eventos más comunes utilizados son: INIT, PreRender, Load y Unload. Dos de ellos son familiares (Load y UnLoad) a los desarrolladores de Visual Basic y, de hecho, tienen cierto parecido. Cuando una pagina es requerida por un explorador, ASP .NET crea un objeto de tipo Page conteniendo la misma, y luego ejecuta su evento INIT, el cual es el encargado de configurar los diferentes controles contenidos por ella. Una vez que éstos han sido establecidos (pero antes de que ésta sea enviada al cliente a través de http) el evento load es iniciado, y es allí donde debe situarse todo el código necesario para inicializar los diferentes valores y propiedades de controles de la misma. Una vez que la pagina esta lista para ser enviada al explorador (pero antes que esto suceda) el evento PreRender es iniciado. El mismo tiene como principal diferencia con Load que en el momento en que éste es ejecutado, todos los cambios sobre la pagina derivados de Load y la implementación de otros eventos ya han sido efectuados. Por último, cuando todas las tareas han sido finalizadas (pero antes de que la página sea enviada al explorador) el evento UnLoad es iniciado. La siguiente figura muestra dicho proceso: Vimos anteriormente que los formularios Web estaban constituidos por dos archivos: aquel que contenía el código y aquel que contenía la información sobre la presentación. Si bien dijimos que ambos eran vistos por ASP .Net como un objeto de tipo Page, en realidad existe una separación aún más fina. Es importante comprender dicha diferencia, ya que cuando se está en modo de diseño en Visual Basic y se accede a la ventana de propiedades mediante la tecla F4, la misma exhibe aquellas correspondientes al objeto Document (presentación), más las del objeto Page, ver la siguiente figura:

INIT (Inicializa la Pagina)

Load

PreRender

UnLoad

Otros Eventos

168

Las propiedades del documento (Document) afectan a la presentación (color, tamaño,etc.), mientras que las del objeto Page modifican la forma en que el código será tratado. Por ejemplo la propiedad bgColor modifica el color del fondo de la página y pertenece a Document, mientras que la propiedad buffer indica si la misma será generada y enviada al cliente a medida que el servidor la vaya construyendo, o después de que se haya finalizado con la misma, y pertenece a Page. Como vemos, la ventana de propiedades es una serie de características de diseño (Document) y de ejecución (Page). Si bien algunas se traducen en código HTML y otras en lineamientos a seguir para la ejecución de la pagina, todas ellas son almacenadas como atributos dentro del archivo aspx, aunque en algunos casos pueden también ser establecidas desde programación (estas ultimas no viajan al explorador). Veamos un resumen de algunas de las propiedades de un formulario web:

Nombre Descripción Background Permite configurar una imagen de fondo al formulario Web. BgColor Color de fondo del formulario Web . ErrorPage Permite establecer la página a emplear en caso de que

ocurra una excepción. ShowGrid Exhibe o no la cuadrícula en tiempo de diseño. Title Configura el título que será mostrado por el explorador en la

barra superior de la ventana. Por supuesto que no todas las propiedades son incluidas en tiempo de diseño, ya que muchas están disponibles solamente durante la ejecución. Este es el caso de IsPostBack, la cual indica si la página está siendo accedida por primera vez o si la petición corresponde a una recarga de la misma. Ella puede ser producida por que el usuario hizo clic en la opción Actualizar del explorador, o como consecuencia de un evento. Como vimos anteriormente los eventos producen la reejecución de la página, y algunos valores deben ser establecidos solamente la primera vez. Para estos casos se cuenta con IsPostBack, que generalmente es utilizada dentro del evento Load.

Ventana de Propiedades

Presentación (Document) Directivas (Objeto Page)

169

Controles en los Formularios Web Los controles de formularios Web son componentes que dan como resultado uno o más controles HTML ante la petición de un explorador. Todos ellos heredan sus características de diferentes clases, las cuales residen dentro del espacio System.Web.UI.WebControls, el cual contiene todos aquellos elementos que pueden componer la interfaz gráfica. Los controles pueden ir desde simples (cajas de texto, botones, etc.) hasta algunos más complejos (calendarios, tablas enlazadas a datos, etc.). No obstante, todos ellos comparten varias cosas en común, como que son derivados originariamente de una clase llamada WebControl, la cual define un elemento Web con su contraparte ejecutable del lado del servidor. En forma general los controles, los podríamos separar en 4 granes grupos: • Estándares. Son aquellos controles ASP .Net que se corresponden con un

control HTML existente, o que, en el peor de los casos, lo simulan si no existe su contraparte. Algunos soportan el enlace a datos, pero no están exclusivamente diseñados para este fin.

• Complejos. Son aquellos controles de ASP .Net que se dibujan como un solo

control., pero que en realidad finalmente serán transformados en varios elementos HTML en el momento de la petición de la página por parte de un explorador. En muchas ocasiones realizan procesos realmente complejos del lado del servidor. Como por ejemplo el calendario, el visor de informes de Crystal Report, etc.

• Validación. Son controles de ASP .Net especialmente diseñados para realizar

tareas de validación sobre la información introducida por el usuario. • Vinculables a Datos. Son controles de ASP .Net especialmente desarrollados y

acondicionados para exhibir y manipular datos. Se cuenta con tres controles que componen este grupo: DataGrid, DataList, Repeater.

Con la finalidad de demostrar algunas de las características de este tipo de controles vamos a generar un nuevo proyecto llamado ValidacionDatos. El mismo permitirá a un usuario suscribirse a un congreso. Para dicha tarea realizaremos varias comprobaciones, a los efectos de verificar que la información haya sido introducida correctamente por el usuario. A continuación se muestra la lista de verificaciones de debemos implementar:

a) Deberá introducir la dirección de correo electrónico. b) La dirección de correo deberá utilizar un formato valido. c) La contraseña deberá introducirse. d) La contraseña deberá ser igual a su verificación. e) La contraseña y su verificación deberán ser iguales.

170

Cada una de estas comprobaciones se corresponderá con un control de verificación, como a continuación se detalla: RequiredFieldValidator Estará asociado a un control de TextBox que contendrá la dirección de correo, y verificará que tenga contenido. RegularExpressionValidator Corroborará que la dirección introducida tenga formato de correo electrónico. RequiredFieldValidator Estará asociado a un control de TextBox que almacenará la contraseña y verificará que la misma tenga contenido. RangeValidator Comprobará que la clave esta en el rango 1000 y 99999999. RequiredFieldValidator Estará asociado a un control TextBox que contendrá la reintroducción de la clave, a los efectos de verificar que contenga un valor. CompareValidator Verificará que la clave y contraseña contengan los mismos valores. El usuario deberá hacer clic en el botón de suscribirse a los efectos de que su información sea verificada e integrada al foro. El primer paso entonces es el de construir la interfaz gráfica:

Objeto Propiedad Valor Label ID Titulo Text Suscripción a el Congreso Font-Size Larger Label ID LblDirCorreo Text Dirección de Correo: TextBox ID DirCorreo Label ID LblContraseña Text Contraseña: TextBox ID Contraseña Text TextMode Password Label ID LblVerificacion Text Verificación TextBox ID Verificacion Text TextMode Password Label ID Mensaje Text Button ID Suscribirse Text Suscribirse

171

Debajo de cada uno de los TextBox agregaremos los diferentes controles de validación, los cuales sólo serán visibles durante la ejecución si el contenido de la caja no cumple con el requisito preestablecido. La vinculación entre el control a validar se realiza mediante la propiedad ControlToValidate, mientras que el texto a ser desplegado en el caso de que un error ocurra deberá ser establecido en ErrorMessage. Veamos con detalle la configuración de cada uno de los controles y sus propiedades:

Objeto Propiedad Valor RequiredFieldValidator ID DirCorreoVerificacionVacio ErrorMessage El campo no puede estar vació. ControlToValidate DirCorreo RegularExpressionValidator ID DirCorreoVerificaciónFormato ErrorMessage Dirección de correo no válida. ControlToValidate DirCorreo ValidationExpression Dirección de correo de Internet

(Internet E-mail Address) RequiredFieldValidator ID ContraseñaVerificacionVacio ErrorMessage El Campo no puede ser vació. ControlToValidate Contraseña RangeValidator ID ContraseñaVerificacionRango ErrorMessage La contraseña debe tener entre 4

y 8 dígitos. MinimimValue 1000 MaximumValue 99999999 ControlToValidate Contraseña. RequiredFieldValidator ID ReIngresoVerificacionVacio ErrorMessage El campo no puede ser vació. ControlToValidate Verificacion. CompareValidator ID ContraseñaVerificaciónReIngreso ErrorMessage La contraseña y su verificación

deben ser iguales. ControlToCompare Contraseña ControlToValidate Verificacion

172

Ahora implementaremos el código para el evento clic del botón Suscribirse. Todos los controles de validación son agrupados en forma automática en una colección de controles de este tipo, la cual es guardada bajo el miembro Validators del formulario. Gracias a éste es posible recorrer la misma y conocer si alguno de sus integrantes contiene un estado no valido. A continuación se muestra el código para realizar esto: Private Sub Suscribirse_Click(ByVal sender ... Dim Verificador As Object Dim ResultadoValidacion As Boolean = True For Each Verificador In Me.Validators If Verificador.isvalid = False Then ResultadoValidacion = False End If Next If ResultadoValidacion = True Then Mensaje.Text = "Se ha Agregado su Dirección como Participante del Congreso" End If End Sub

173

Como se puede observar, Validators retorna la colección de controles validación, los cuales pueden ser extraídos mediante la utilización de la estructura for each. Dentro de la misma se evalúa la propiedad isValid para conocer el estado, y así determinar cómo continuará la aplicación.

174

Web Form con ADO .Net En la implementación de sistemas la arquitectura más escalable es la de los sistemas distribuidos. El programa se encuentra en una maquina servidora y por medio de un navegador de Internet, las computadoras clientes acceden a el. La tecnología de ADO .Net se basa en la desconexión automática de la base de datos, para no consumir recursos excesivos del servidor, sino solamente cuando lo solicite. El tipo de ejecución unidireccional se refiere a los comandos Transact-SQL que no regresan registros como resultado de una consulta, como son: modificación, inserción o borrado. El modelo desconectado no requiere mayor complejidad debido a que una vez ejecutada la sentencia SQL, se desconecta la conexión. El tipo de ejecución bidireccional es más complejo, ya que además de ejecutar una sentencia SQL debe de regresar un conjunto de registros como resultado de una consulta, los cuales son almacenados en forma de copia local. Estos registros se convierten en una colección de datos que puede ser manipulada sin necesitar estar conectada con el origen de datos. La estructura que contiene a estos registros es llamada DataSet. Cuando sea necesario mantener la compatibilidad con sistemas ya existentes, es posible crear estructuras que interactúen con estos sistemas, lo que permite que el desarrollador pueda crear tablas personalizadas, para que se adapten a sistemas específicos, además que la estructura creada puede tener las mismas características que las de una base de datos. Los registros que se encuentren en forma de copia local se pueden ordenar, filtrar, borrar, agregar, modificar por lo que es muy flexible su utilización dentro de las aplicaciones, todo esto sin que se modifique el origen de datos, sino hasta que se haga una sincronización, esto lo hace ADO .Net en forma automática. En caso de que dos o más usuarios modifiquen un registro de su copia local y después quieran hacer la sincronización con el origen de datos, debe de existir algún mecanismo para evitar un conflicto. Otra situación se refiere a las relaciones entre las tablas, como sabemos en las bases de datos relacionales existen tablas, relaciones y llaves primarias. Es común encontrar tablas que dependan de otras, ahora es posible copiar las relaciones de las tablas del origen de datos (todos estos conceptos ya los tratamos en el capitulo 5 del presente manual, por lo que nos concretaremos a ejemplificar).

175

Controles Vinculables a Datos El siguiente ejemplo muestra como llenar un ListBox con los datos de la tabla de clientes de la base de datos Ferretería. Vamos a aprender a llenar un ListBox con la información de una tabla. Para ello creamos un nuevo proyecto de aplicación Web ASP .NET llamado ListaClientes, al cual agregaremos los controles de acuerdo con la siguiente tabla:

Objeto Propiedad Valor Label ID Titulo

Text Lista de Clientes Font-Size Large

ListBox ID Lista La ventana queda como a continuación se muestra:

Ahora implementaremos el código para el evento Load de la pagina, como a continuación se muestra: Imports System.Data.OleDb Public Class WebForm1 Inherits System.Web.UI.Page Dim Cn As New OleDb.OleDbConnection Private Sub Page_Load(ByVal sender As System.Object, … 'Introducir aquí el código de usuario para inicializar la página Dim Cadena As String Dim Comando As OleDbCommand Dim DataRead As OleDbDataReader Cn.ConnectionString = "Provider=SQLOLEDB;User Id=sa;Password=;Initial Catalog=Ferreteria;Data Source=(local);" Cn.Open() Comando = New OleDbCommand("Select * from Cliente", Cn) DataRead = Comando.ExecuteReader

176

While DataRead.Read Lista.Items.Add(DataRead.Item(1) & "--" & DataRead.Item(2) & "--" & DataRead.Item(3)) End While DataRead.Close() End Sub Private Sub Page_Unload(ByVal sender As Object, … Cn.Close() End Sub El resultado es el que se muestra en la siguiente ventana:

Funciones de ASP .Net En el espacio de nombres System.Web para poder disponer de una variedad de clases que darán funcionalidad a nuestras aplicaciones Web.

HttpResponse. Cuando se necesita mandar información al cliente como cookies, salida HTML y direcciones se utiliza la clase HttpResponse mediante el objeto Response y el método Write.

Response.Write("La fecha es: " & Now.Date) HttpRequest. Cuando se solicita información del explorador de una computadora cliente, tal como cookies, valores de las formas o una consulta se utiliza la clase HttpRequest mediante el objeto Request. Cabe mencionar que en ASP.NET muchas de las funciones de solicitud se hacen en forma automática, por esto se puede prescindir de éste para obtener valores, aunque sí se utiliza para algunas otras funciones.

Valor=Request.Form(“txtCaja.Text”)

177

IsClientConnected( ) As Bolean. Debido a que los procesos se realizan en el servidor, éstos pueden tomar tiempo y es necesario saber si el usuario sigue conectado, la función IsClienteConnected regresa un valor verdadero o falso que indica el resultado, se utiliza en conjunción con el objeto Response.

If Response.IsClientConnected Then

'Se realiza un proceso en caso de que el usuario este conectado. End If

Objeto.Redirect(“http://paginaWeb”). El objeto Response tiene un método llamado Redirect el cual dirige la página actual hacia otra; sin embargo, hay que tomar en cuenta que cuando el navegador de Internet encuentra una página que dirige hacia otra, el servidor envía la orden: “se debe de ir a otra página”, después de esto el explorador toma la dirección destino y va hacia esa página, por lo tanto este método ocasiona una ida y vuelta extra al servidor.

Response.Redirect(“http://www.miNuevaPagina.com ”)

Variables del servidor El servidor cuenta con variables especiales que pueden ser consultadas por el usuario, estas variables pueden regresarnos información como por ejemplo la versión de navegador de Internet del que se está haciendo la petición, la versión del servidor Web, etc. La propiedad ServerVariables se utiliza para esta operación. Por ejemplo, para obtener el navegador utilizado: Request.ServerVariables(“HTTP_USER_AGENT”) Para obtener la versión de servidor Request.ServerVariables(“SERVER_SOFTWARE”) Si queremos saber todas las variables de la propiedad ServerVariables, asignamos a una variable la siguiente instrucción: Variable=Request.ServerVariables(0)

178

Con el objetivo de ejemplificar estos dos temas crearemos un nuevo proyecto de aplicación Web ASP .NET llamado Variables, al cual agregaremos los controles de acuerdo con la siguiente tabla:

Objeto Propiedad Valor Button Text Response Button Text Request y Response

A continuación se muestra el código de los botones: Private Sub Button1_Click(ByVal sender As System.Object,…

'Mando escribir en pantalla un mensaje Response.Write("<H2>¿Hola cómo estás?</H2>") End Sub Private Sub Button2_Click(ByVal sender As System.Object, … Dim Valor As Object 'Manda escribir un mensaje a la pantalla del navegador de Internet Response.Write("<H2>Algunas propiedades de <I>Response.ServerVariables()</I></H2>") 'Tipo de navegador que se utiliza Valor = Request.ServerVariables("HTTP_USER_AGENT") Response.Write("<H2>El tipo de navegador que se utiliza es: " & Valor & "</H2>") 'Tipo de software que utiliza el navegador Valor = Request.ServerVariables("SERVER_SOFTWARE") Response.Write("<H2>El software del servidor es: " & Valor & "</H2>") 'Nombre del servidor que manda la información Valor = Request.ServerVariables("HTTP_HOST") Response.Write("<H2>El servidor es: " & Valor & "</H2>") 'Dirección URL Valor = Request.ServerVariables("HTTP_REFERER") Response.Write("<H2>La dirección URL: " & Valor & "</H2>") 'Ruta del archivo Valor = Request.ServerVariables("APPL_PHYSICAL_PATH") Response.Write("<H2>La ruta del archivo es: " & Valor & "</H2>") End Sub A continuación se muestra el resultado:

179

Lock y Unlock Los miembros Lock y Unlock se utilizan para que no existan problemas de concurrencia, ya que se debe alterar una variable por sesión a la vez. 'Pongo un candado para que nadie más, además del que accedió primero, 'pueda alterar la variable. Además de incrementar en uno el contador Application.Lock Application("Contador")=Application("Contador") + 1 'Quito el candado para que otra sesión pueda aumentar el contador Application.Unlock Variables de Aplicación Como lo explicamos antes, cada que una página es solicitada se regenera completamente. Es por eso que no guarda valores, y se utilizan las variables de aplicación, en las que no se permiten almacenar valores que puedan ser compartidos por todos los usuarios que acceden a la aplicación. El objeto Application permite que se almacene información dentro de la memoria del servidor Web, mientras que el servicio de IIS (Internet Information Server) no sea detenido. Por ejemplo, podemos indicar en qué momento se inició la aplicación y almacenarlo en una variable llamada T_DeComienzo.

180

'Now indica la fecha y hora actual del servidor. Application("T_DeComienzo")=Now Para acceder a esta variable lo podemos hacer de la siguiente forma: Response.Write("La aplicación comenzó: " & Application("T_DeComienzo")) Escribirá en la pantalla del navegador de Internet la fecha en que comenzó la aplicación. También podríamos almacenar el número de visitantes que ha tenido nuestra página, para comenzar necesitamos asignar algún valor a la variable. Application.Add("Clientes",2) Para obtener el valor de esta variable podemos utilizar el método Get del objeto Application, cabe mencionar que no es necesario declarar previamente la variable. Variable=Application.Get("Clientes") Variables de Session Cuando se necesita guardar información por usuario, entonces se utilizan las variables de sesión. La diferencia de este tipo de variables contra las de aplicación es que las variables de sesión tienen un conjunto de valores por usuario y las de aplicación son globales, las comparten todos los usuarios que acceden a la aplicación. El valor de estas variables son mantenidas mientras la sesión no termine, ya sea que el tiempo máximo de espera se alcance o el usuario cambie a otra página que no pertenezca a la misma aplicación. Es importante establecer lo que es una sesión para ASP.NET, cada ventana del navegador de Internet abierta es interpretada como una sesión. Por ejemplo, si tenemos tres ventanas del navegador abiertas en la misma computadora, será interpretada por ASP.NET como tres sesiones abiertas, sin embargo generalmente existe una sola ventana abierta del navegador de Internet por usuario. Para guardar la fecha y hora en que comenzó la sesión. Session("InicioDeSesión")=Now Para obtener el valor. Response.Write("La sesión comenzó: " & Session("InicioDeSesión")) Si quisiera establecer el color de fondo de cada usuario se almacenaría de la siguiente forma: Session.Add("Color","Verde") Para obtener el valor: ColorDeUsuario=Session("Color")

181

El estado de una sesión se puede almacenar de las siguientes formas: 1. Como parte del proceso de la aplicación. 2. Como un servicio de Microsoft Windows, en el que se permite a la aplicación ASP.NET compartir datos entre muchos procesadores (Web gardens). 3. Como dato en una base de datos de SQL Server, lo que permite que se pueda compartir la información entre muchas computadoras (Web forms). Es importante mencionar que ASP.NET no necesita cookies para guardar el estado de sesión, en el archivo Web.Config se puede especificar que se codifique el identificador de sesión en el URL (Uniform Resource Locator), con lo cual cualquier navegador de Internet, sin importar que no permita el uso de cookies, puede saber la información de sesión. Inicializar con Global.asax Es posible inicializar variables de aplicación o de sesión en el archivo Global.asax, si se necesita guardar información se utilizan variables de aplicación o de sesión. En ASP.NET existen varios eventos en la vida de la aplicación, algunos de éstos son: Eventos de aplicación. Los eventos de aplicación son aquellos que son compartidos por todos los usuarios. Application_Start. Se ocasiona cuando el primer usuario trata por primera vez de ingresar a la aplicación. Application_BeginRequest. Cuando un usuario solicita un URL. Application_EndRequest. Cuando la solicitud ha sido completada. Application_End. Se activa cuando ya no hay instancias de la clase Global. Eventos de sesión. Los eventos de sesión son aquellos que son exclusivos a un usuario. Session_Start. Es activado cuando una nueva sesión se va a generar. Session_End. Se activa cuando termina una sesión, por cualquiera de las siguientes razones:

• Cuando se invoca el método Session.Abandon dentro del código de la aplicación. • Cuando se ha excedido el tiempo de inactividad. Este tiempo es establecido automáticamente en veinte minutos, sin embargo se puede configurar como usted lo desee, por medio de la propiedad Session.Timeout. Hay que tomar en cuenta, que la configuración de un tiempo muy largo consume mayores recursos del servidor.

182

El siguiente ejemplo muestra como almacenar una variable de aplicación llamada Contador_Aplicación, que cuente el número de sesiones activas que tiene la aplicación actualmente. Almacenar una variable de sesión llamada Hora_Inicio, que guarde la fecha y hora en que se inicia cada sesión. Para terminar la sesión se contará con un botón que invocará a la función Session.Abadon() Crearemos un nuevo proyecto de aplicación Web ASP .NET llamado Sesiones, al cual agregaremos los controles de acuerdo con la siguiente tabla:

Objeto Propiedad Valor Label Text Ejemplo de Variables de

Aplicación y Sesión Label Text Label Text Button Text Cerrar Sesión Para ASP.NET, cada ventana del navegador de Internet que acceda a la aplicación es considerada como una nueva sesión. Cuando oprime dentro del navegador File + New + Window, se abre una segunda ventana hija de la ventana original, en este caso no se genera una nueva sesión. Solamente se genera una nueva cuando oprime el icono de Internet Explorer. El código de este ejercicio se escribe en dos archivos, el primero en Global.asax. En el primer archivo se inicializa a cero la variable Contador_Aplicacion dentro de la función Application_Start, se hace el incremento de esta variable cuando inicia una sesión dentro de la función Session_Start, se decrementa cuando termina una sesión dentro de la función Session_End. La fecha y hora en que comienza la sesión se guarda en la variable de sesión Hora_Inicio. Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) ' Se desencadena cuando se inicia la aplicación 'Inicio la variable Contador_Aplicacion con cero Application("Contador_Aplicacion") = 0 End Sub Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs) ' Se desencadena cuando se inicia la sesión 'Le sumo uno al contador

Application("Contador_Aplicacion")=Int(Application("Contador_Aplicacion")) + 1 'Indico la hora en que comenzó la sesión Session("Hora_Inicio") = Now End Sub Sub Session_End(ByVal sender As Object, ByVal e As EventArgs) ' Se desencadena cuando termina la sesión ' Le resto uno al contador Application("Contador_Aplicacion") = Int(Application("Contador_Aplicacion")) - 1 End Sub

183

El segundo archivo es WebForm1.aspx, a continuación se muestra el código: Private Sub Page_Load(ByVal sender As System.Object, … 'Introducir aquí el código de usuario para inicializar la página Dim Contador As Object Dim Hora As Date 'Bloqueo el acceso a los demás usuarios, para que sea el 'único en acceder a la variable Contador_Aplicacion. Application.Lock() 'Obtengo el valor de la variable de aplicación Contador_Aplicacion Contador = Application.Get("Contador_Aplicacion") 'Desbloqueo el acceso a la variable, para que otros 'usuarios puedan accederla Application.UnLock() Hora = Session("Hora_Inicio") 'Mando imprimir el número de sesión, la hora y fecha de inicio Label2.Text = "Numero de sesiones en el servidor : " & Contador Label3.Text = "Fecha y hora de inicio de esta sesión : " & Hora End Sub Private Sub Button1_Click(ByVal sender As System.Object, … 'Termina la sesión Session.Abandon() ' Deshabilito el control Button1.Enabled = False End Sub El resultado es el siguiente:

184

Datos Desconectados (DataSet) Como lo vimos en el capítulo de ADO .Net un DataSet obtiene un conjunto de filas de una o más tablas y posteriormente las aloja en memoria. Una vez realizado esto, procede a desconectarse de la Base de Datos. El DataSet se encargará de la gestión local de las filas obtenidas, así como también de las posibles acciones a ser realizadas sobre las mismas (modificaciones, eliminaciones, etc.), estas acciones serán aplicadas sobre la copia privada de los datos, sin que ello interfiera con la base de datos. Posteriormente, se necesitará sincronizar el DataSet con la base de datos a efecto de que los cambios efectuados en el DataSet se hagan efectivos en la base de batos. El siguiente ejemplo muestra como crear altas, bajas, cambios sobre la tabla de clientes de la base de datos Ferretería. Vamos a aprender a llenar un Grid con la información de una tabla. Para ello creamos un nuevo proyecto de aplicación Web ASP .NET llamado ABClientes, al cual agregaremos los controles de acuerdo con la siguiente tabla:

Objeto Propiedad Valor Label Text Catálogo de Clientes

Font-Size Large Label Text Clave: Label Text Nombre: Label Text Domicilio: Label Text Ciudad: Label Text RFC: Button ID Agregar

Text Agregar Button ID Editar

Text Editar Button ID Borrar

Text Borrar Button ID Grabar

Text Grabar Button ID Cancelar

Text Cancelar TextBox MaxLength 5

ID ClientePk TextBox MaxLength 80

ID NomCliente TextBox MaxLength 80

ID DomCliente TextBox MaxLength 50

ID Ciudad TextBox MaxLength 15

ID RFCliente

185

Objeto Propiedad Valor DataGrid ID DGClientes

RequiredFieldValidator ErrorMessage No puede estar en blanco ControlToValidate ClientePk

RangeValidator ErrorMessage Valor fuera de rango ControlToValidate ClientePk MinimumValue 1 MaximumValue 99999

RequiredFieldValidator ErrorMessage No puede estar en blanco ControlToValidate NomCliente

RequiredFieldValidator ErrorMessage No puede estar en blanco ControlToValidate DomCliente

RequiredFieldValidator ErrorMessage No puede estar en blanco ControlToValidate Ciudad

RequiredFieldValidator ErrorMessage No puede estar en blanco ControlToValidate RFCliente

Después de agregar el control DataGrid haga clic derecho sobre éste y seleccione Formato Automático y por ultimo Profesional 2. Para seleccionar un registro completo del Grid, se necesita una columna extra que permita realizar esta operación. En la propiedad Columnas y haga clic en (colección) . En la ventana que aparece en seguida seleccione Columnas y abra la opción de Columna Botón escoja Seleccionar, después establezca el Texto del Encabezado, el Nombre de Comando es el nombre con el cual será identificado en la programación. Una vez establecido estas propiedades oprima el botón de Aceptar.

186

A continuación se muestra el código de la pagina: Imports System.Data.OleDb Public Class WebForm1 Inherits System.Web.UI.Page Dim Cn As OleDbConnection Dim celdaGrid As TableCellCollection Dim Ds As DataSet Dim Adaptador As OleDbDataAdapter Private Sub Page_Load(ByVal sender As System.Object, … 'Introducir aquí el código de usuario para inicializar la página Cn = New OleDbConnection Cn.ConnectionString = "Provider=SQLOLEDB;User Id=sa;Password=;Initial Catalog=Ferreteria;Data Source=(local);" Cn.Open() Call CargarDataGrid() If Not IsPostBack Then Call HabilitarBotones() DGClientes.Visible = True End If End Sub Private Sub HabilitarBotones() Agregar.Enabled = True Editar.Enabled = True Borrar.Enabled = True Cancelar.Enabled = False Grabar.Enabled = False 'Se bloquean las cajas de texto ClientePk.ReadOnly = True NomCliente.ReadOnly = True DomCliente.ReadOnly = True Ciudad.ReadOnly = True RFCliente.ReadOnly = True End Sub Private Sub DesHabilitarBotones() Agregar.Enabled = False Editar.Enabled = False Borrar.Enabled = False Cancelar.Enabled = True Grabar.Enabled = True 'Se Desbloquean las cajas ClientePk.ReadOnly = False NomCliente.ReadOnly = False DomCliente.ReadOnly = False Ciudad.ReadOnly = False RFCliente.ReadOnly = False End Sub

187

'Este procedimiento se genera cuando quiero cargar el DataGrid Private Sub CargarDataGrid() Ds = New DataSet Adaptador = New OleDbDataAdapter("Select ClientePk,NomCliente,DomCliente,Ciudad,RFCliente From Cliente", Cn) Adaptador.Fill(Ds, "Clientes") DGClientes.DataSource = Ds If IsPostBack = False Then DGClientes.DataBind() 'selecciono el primer elemento DGClientes.SelectedIndex = 0 'Pongo todas las celdas del Item cero que es el primer elemento del grid. celdaGrid = DGClientes.Items(0).Cells() Call LlenarCajas(celdaGrid) End If End Sub 'Procedimiento para llenar las cajas de texto con el registro ´ 'seleccionado del grid Private Sub LlenarCajas(ByVal CeldaGrid As TableCellCollection) ClientePk.Text = CeldaGrid(1).Text NomCliente.Text = CeldaGrid(2).Text DomCliente.Text = CeldaGrid(3).Text Ciudad.Text = CeldaGrid(4).Text RFCliente.Text = CeldaGrid(5).Text End Sub 'Evento ItemCommand que se ejecuta cuando oprimo la primer columna, 'donde se encuentra seleccionar Private Sub DGClientes_ItemCommand(ByVal source As Object, … 'Checo si el tipo de comando es el de seleccionar If e.CommandName = "Select" Then DGClientes.SelectedIndex = e.Item.ItemIndex celdaGrid = e.Item.Cells Call LlenarCajas(celdaGrid) End If End Sub Private Sub Agregar_Click(ByVal sender As Object, … Session("EsAgregar") = True 'Vacío las cajas de texto ClientePk.Text = String.Empty NomCliente.Text = String.Empty DomCliente.Text = String.Empty Ciudad.Text = String.Empty RFCliente.Text = String.Empty Call DesHabilitarBotones() DGClientes.Visible = False End Sub

188

Private Sub Grabar_Click(ByVal sender As System.Object,… 'Valido las cajas de texto If RequiredFieldValidator1.IsValid And RequiredFieldValidator2.IsValid And RequiredFieldValidator3.IsValid And RequiredFieldValidator4.IsValid And RequiredFieldValidator5.IsValid And RangeValidator1.IsValid Then 'Variable de tipo SqlCommandBuilder Dim sqlCommBuilder As OleDbCommandBuilder 'La variable "EsAgregar" se definió en Global.axa, ya que 'las páginas ASP no recuerdan las variables, porque 'la página se regenera completamente después de un evento. If Session("EsAgregar") = True Then Try 'Creo un nuevo renglón de la tabla Clientes en la copia local Dim NuevoRenglon As DataRow = Ds.Tables("Clientes").NewRow 'Creo una vista, para que muestre los datos ordenados Dim vista As New DataView 'Pongo el valor de las cajas de texto en el nuevo renglón NuevoRenglon("ClientePK") = ClientePk.Text NuevoRenglon("NomCliente") = NomCliente.Text NuevoRenglon("DomCliente") = DomCliente.Text NuevoRenglon("Ciudad") = Ciudad.Text NuevoRenglon("RFCliente") = RFCliente.Text 'Agrego el renglon a la colección de renglones Ds.Tables("Clientes").Rows.Add(NuevoRenglon) 'Creo el CommandBuilder para que se pueda generar 'automáticamente la sentencia SQL de inserción sqlCommBuilder = New OleDbCommandBuilder(Adaptador) 'Sincronizo con el origen de datos Adaptador.Update(Ds, "Clientes") 'Confirmo los cambios Ds.Tables("Clientes").AcceptChanges() 'Le cargo a la vista la del DataSetSQL "Clientes" vista.Table = Ds.Tables("Clientes") 'Establezco un criterio de ordenación de menor a mayor vista.Sort = "ClientePk ASC" 'Cargo el DataGrid con el valor de la vista DGClientes.DataSource = vista 'Actualizo el control DataGrid DGClientes.DataBind() 'Me posiciono en el renglon donde se encuentra el nuevo libro 'vista.Find() devuelve el renglón donde se encuentra la columna 'que sirve como llave, en este caso ClientePk, es por eso que 'pongo el valor de la primer caja de texto como parámetro. DGClientes.SelectedIndex = vista.Find(ClientePk.Text) 'Obtengo los valores del nuevo libro, a partir de su posición

189

'en el DataGrid celdaGrid = DGClientes.Items(DGClientes.SelectedIndex).Cells() 'Procedimiento para llenar las cajas de texto Call LlenarCajas(celdaGrid) Catch mensaje As Exception Label6.Text = "Error, la clave de ese libro ya existe" Exit Sub End Try 'En caso de que sea edición Else Dim Tabla As DataTable Dim Renglon As DataRow Tabla = Ds.Tables("Clientes") Renglon = Tabla.Rows(DGClientes.SelectedIndex) Renglon("ClientePk") = ClientePk.Text Renglon("NomCliente") = NomCliente.Text Renglon("DomCliente") = DomCliente.Text Renglon("Ciudad") = Ciudad.Text Renglon("RFCliente") = RFCliente.Text 'Termino la edición Renglon.EndEdit() sqlCommBuilder = New OleDbCommandBuilder(Adaptador) 'sincronizar con la base de datos Adaptador.Update(Ds, "Clientes") 'Confirmo los cambios Ds.Tables("Clientes").AcceptChanges() 'Actualizo el control DataGrid DGClientes.DataBind() End If Call HabilitarBotones() DGClientes.Visible = True End If End Sub Private Sub Cancelar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Cancelar.Click 'Las validaciones a verdadero, porque se está haciendo una cancelación RequiredFieldValidator1.IsValid = True RequiredFieldValidator2.IsValid = True RequiredFieldValidator3.IsValid = True RequiredFieldValidator4.IsValid = True RequiredFieldValidator5.IsValid = True RangeValidator1.IsValid = True 'Almaceno en CeldaGrid los valores de la fila que tiene seleccionado Grid celdaGrid = DGClientes.Items(DGClientes.SelectedIndex).Cells() Call LlenarCajas(celdaGrid) Call HabilitarBotones() DGClientes.Visible = True End Sub

190

Private Sub Borrar_Click(ByVal sender As System.Object, … Dim Tabla As DataTable Dim Renglon As Long Tabla = Ds.Tables("Clientes") 'Asigno a la variable Renglon, el renglón seleccionado del DataGrid Renglon = DGClientes.SelectedIndex Tabla.Rows(Renglon).Delete() Dim CommBuilder As New OleDbCommandBuilder(Adaptador) 'Sincronizo los cambios con la base de datos. Adaptador.Update(Ds, "Clientes") Ds.Tables("Clientes").AcceptChanges() DGClientes.DataBind() If DGClientes.Items.Count() - 1 > 0 Then celdaGrid = DGClientes.Items(DGClientes.SelectedIndex - 1).Cells() 'Actualiza las cajas de texto Call LlenarCajas(celdaGrid) End If Call HabilitarBotones() End Sub Private Sub Editar_Click(ByVal sender As System.Object, … 'Significa que no estoy agregando, lo utilizo como 'bandera para el momento en que oprima Grabar. Session("EsAgregar") = False Call DesHabilitarBotones() 'Cuando se edita no se puede permitir que se modifique el código 'del producto, en todo caso se debe de borrar y agregar como un 'nuevo producto. ClientePk.ReadOnly = True 'No permito que se vea el DataGrid DGClientes.Visible = False End Sub

191

Es importante hacer hincapié en que cada vez que se genera un evento, como por ejemplo un clic, la página es regenerada completamente, es por eso que se hace uso de la instrucción IsPostBack, que checa que las instrucciones que se encuentren dentro de ella, solamente se ejecuten la primera vez que se cargue la página. Otro punto importante es que no es conveniente almacenar valores en las variables locales, porque estos valores se pierden. Es por eso que en el archivo Global.asax, que es creado automáticamente, asignamos valores que utilizaremos en el transcurso de la aplicación. Hay dos formas de almacenamiento, una por aplicación, que quiere decir que cada que se inicie la aplicación se guardará o manipulará la variable sin importar cuántas sesiones se creen. La otra es por sesión, en donde cada vez que se cree una sesión se guardará o manipulará la variable. Dentro del código se utiliza la variable EsAgregar, su valor depende si el usuario ha oprimido el botón de Agregar ó Editar. Debido a que se utiliza un solo procedimiento para grabar la información, esta variable indica si su valor es verdadero, que se debe agregar un nuevo registro, si su valor es falso, entonces se debe modificar un registro ya existente.

192

Creación de Controles Dinámicamente El diseño de una página Web involucra la inserción de controles en tiempo de diseño, sin embargo hay ocasiones en las cuales no es posible determinar el número de controles que usara la página, para remediar esta situación el entorno de Visual Studio .NET brinda un conjunto de contenedores de controles, los cuales proporcionan una serie de funciones para crear la cantidad de controles que necesitemos. Durante el desarrollo de este documento aprenderá cuales son los contenedores de controles, como adicionar controles usando el contenedor panel (a través de un ejemplo práctico) y como leer el valor de cada control. Contenedores de controles Los objetos que tienen la capacidad de incluir varios controles dentro de si son llamados contenedores de controles. En ASP .NET los controles que tienen esa facultad son:

• Panel • Placeholder • Table con sus clases TableRow (fila) y TableCell (columna)

Estos controles se localizan dentro de la barra de herramientas que aparece por defecto en el entorno de Visual Studio .NET. Por la sencillez de programación, el contenedor que se utilizara a lo largo del texto es el control Panel. El siguiente ejemplo muestra como desarrollar una página en ASP .NET que muestre un examen de opción múltiple y que presente las respuestas que selecciono el usuario. Creación de controles en ASP .NET El desarrollo de este ejemplo esta basado en dos tablas, de las cuales se muestra su estructura a continuación:

Después de crear las tablas, se procede a insertar información en la tabla de pregunta y luego a generar las respuestas para cada pregunta relacionada.

193

Para llevar a cabo esta actividad, crearemos un proyecto de aplicación Web ASP .NET, al cual llamaremos CtrlDinamicos. En este adicionaremos los siguientes controles:

Control Propiedad Valor Label Text Creación de controles dinámicos Bold Trae Font - Size Large Label Text Examen de prueba Label Text Sección de preguntas Label Text Sección de respuestas Button Text Mostrar respuestas Panel Dar un clic Eliminar el texto “Panel” Panel Dar un Clic Eliminar el texto “Panel” El diseño de la página quedara como la siguiente figura:

Una vez creado el diseño, continuaremos con la inserción del código para darle funcionalidad a la página. El siguiente código se incluira en el evento load de la página: Imports System.Data.OleDb Public Class WebForm1 Inherits System.Web.UI.Page 'Sección variables de usuario Dim Cn As New OleDbConnection Dim Ds As New DataSet Dim i As Integer Dim lblPreg As Label Dim rblResp As RadioButtonList Private Sub Page_Load(ByVal sender As System.Object, … 'Introducir aquí el código de usuario para inicializar la página Cn.ConnectionString = "Provider=SQLOLEDB; User Id=SA; Password=; Initial Catalog=Pruebas; Data Source=(Local)" Cn.Open()

194

LlenarDatos(lblPreg, rblResp) End Sub Ahora se procederá a insertar el código del procedimiento para crear los controles dinámicamente: Private Sub LlenarDatos(ByVal lbl As Label, ByVal rbl As RadioButtonList) Dim ComResp As New OleDbCommand Dim DatRead As OleDbDataReader Dim RegPreg As DataRow Dim AdaptPreg As New OleDbDataAdapter("Select IdPregunta,Pregunta from Pregunta", Cn) Dim AdaptResp As OleDbDataAdapter AdaptPreg.Fill(Ds, "Pregunta") For Each RegPreg In Ds.Tables("Pregunta").Rows lbl = New Label rbl = New RadioButtonList lbl.Text = "<BR>" & " " & RegPreg(0) & ". " & RegPreg(1) Panel1.Controls.Add(lbl) ComResp = New OleDbCommand("Select IdRespuesta,Respuesta from Respuesta where IdPregunta=" & RegPreg(0) & "", Cn) DatRead = ComResp.ExecuteReader While DatRead.Read rbl.Items.Add(New ListItem(DatRead.Item(1), DatRead.Item(0))) End While Panel1.Controls.Add(rbl) DatRead.Close() Next End Sub

Y por último, la siguiente sección de código va incluida en el botón que mostrara las respuestas seleccionadas y con el cual se leerán los valores de los controles: Private Sub Button1_Click(ByVal sender As System.Object, … Dim RadioBoton As RadioButtonList Dim label As Label Dim j As Integer For i = 1 To Panel1.Controls.Count - 1 Step 2 RadioBoton = Panel1.Controls(i) For j = 0 To RadioBoton.Items.Count - 1 If RadioBoton.Items(j).Selected = True Then label = New Label label.Text = RadioBoton.SelectedItem.Value & ". " & RadioBoton.SelectedItem.Text & "<BR>" Panel2.Controls.Add(label) End If Next Next End Sub Concluida la inserción de código se procede a ejecutar la página y el resultado que mostrara será como el siguiente:

195

196

El contenido del presente manual esta basado en la siguiente Bibliografía Programación en Visual Basic .Net LUIS MIGUEL BLANCO A Programmer’s Introduction to Visual Basic.NET SAMS ASP.NET for Developers Michael Amundsen ASP.NET Web Developer’s Guide SYNGRESS Mastering Visual Basic .Net BÜHLER, Erich R., Visual Basic.NET Guía de migración y actualización, Editorial

McGraw-Hill Profesional, España, 2002, 949 pp. MICROSOFT CORPORATION, Programming with Microsoft Visual Basic .NET,

Microsoft Corporation, Colombia, 2002, 577 pp. Tesis: INTRODUCCIÓN A LA TECNOLOGÍA ASP.NET ING. HÉCTOR FLORES ACEVEDO L.I. Alejandro Hernandez Montalvo