Anda di halaman 1dari 196

Curso de Titulación en Sistemas con Visual Basic .

Net 2003

Marzo 2004 Alejandro Guzmán Zazueta


a_zazuetag@hotmail.com

1
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

2
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

3
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

4
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.

5
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

6
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

7
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

CLR
Aplicación Proceso1 Aplicación Proceso 2
Proceso
EXE
EXE DLL Ensamblado Ensamblado

DLL DLL
DLL
Aplicación Proceso3
Ensamblado
DLL
DLL
EXE

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

8
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.

9
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

10
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.

11
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.

12
El nuevo entorno Integrado

A) Barra de
Herramienta E) Proyectos
s y Menús abiertos
propiedades,
ayuda, etc.

B) Cuadro de
Herra-
mientas y
explorador
servidores
D) Área de
diseño y
edición
C) Opciones
referentes a
compilación,
o lista de
tareas
pendientes

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).

13
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.

14
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.

15
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.

16
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.

17
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()

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

18
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()

19
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

20
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.

21
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 Contiene varias características que afectan únicamente a las
diseñador 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

22
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.

23
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.

24
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.

Libreria

Clas Herencia Namespace Métod

25
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.

26
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:

27
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.

28
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.

29
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.

30
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.

31
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.

32
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,

33
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 Fecha y Hora
DateTime
Decimal Decimal +/–79,228,162,514,264,337,593,543,950,335
sin 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

34
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:

160Kb 140Kb

300Kb

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.

35
Dim NombreAlumno as New System.Text.StringBuilder

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)

36
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 Label1
Text TiempoString:
TextBox Nombre TiempoString
Text
Label Nombre Label2
Text Tiempo StringBuilder:
TextBox Nombre TiempoStringBuilder
Text
Button Nombre PruebaString
Text Prueba String
Button Nombre PruebaStringBuilder
Text 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

37
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

38
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)

39
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)

40
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.

41
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

42
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.

43
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

44
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

45
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.

46
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.

47
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

48
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

49
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:

50
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

51
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 )

52
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)

53
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)

54
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.

55
Módulo 3: Programación Orientado a Objetos para Visual Basic .Net
Introducción
La 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

56
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 Cliente.vb

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.
Public Class Cliente

End Class
Cliente
Public Class Cliente

End Class

57
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.

58
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

59
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

60
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

61
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.

62
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.

63
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.

64
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))

65
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()

66
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.

67
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

68
‘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.

69
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.

70
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.

H
E X
R
E
N
C Y Z
I
A

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.

71
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

72
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

73
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

74
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>

75
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

76
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

77
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”

78
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.

79
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.

80
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.

81
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

82
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

83
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

84
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.

85
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

86
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.

87
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

88
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.

89
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

90
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

91
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.

92
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.

93
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.

94
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.

95
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.

96
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.

97
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

98
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.

99
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.

100
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.

101
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:

System

Data

OleDb

SqlClient
(Proveedor especifico)
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

102
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.

103
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

104
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

105
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

106
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

107
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)

108
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.

109
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()

110
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.

DataSet

OleDbDataAdapter o DataAdapter
SQLDataAdapter

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.

111
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:

112
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:

113
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)

114
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

115
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.

116
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.

117
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.

118
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.

119
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:

120
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.

121
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

122
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.

123
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

124
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

125
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

126
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

127
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.

128
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.

129
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

130
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:

131
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:

132
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:

133
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

B
A

134
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.

135
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:

136
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:

137
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.

138
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.

Base de Crystal
Datos ADO Dataset Report
.Net

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:

139
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.

140
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.

141
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 ( )

142
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()

143
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.

144
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).

145
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

146
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:

147
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

148
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

149
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.

Código Datos Archivo Código Datos Archivo

Hilos

Un solo Hilo 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)

150
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.

151
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")

152
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.

153
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.

Tiempo1 Tiempo2

Variable

Hilo

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.

154
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

155
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

156
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

157
'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

158
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

159
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

160
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.

161
Comparación entre aplicaciones Windows y Web.

Característica Aplicaciones Windows Aplicaciones para Web


Localización y La aplicación se contiene dentro de una o La aplicación se contiene dentro de uno o
ejecución más carpetas, y requiere solamente del más directorios virtuales, y utilizará los
soporte de la infraestructura .NET . 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 No requiere instalación en cada ordenador
mediante un instalador. La mayor parte de que desee hacer uso del mismo, aunque
la veces no requiere una conexión a una sí se necesita un explorador y conexión a
red interna o externa, y en el caso de que la red. En el caso de que desee actualizar
se deseen actualizar varios equipos se la aplicación, la tarea debe efectuarse
debe ejecutar el instalador tantas veces solamente en el servidor.
como los ordenadores lo requieran.
Interfaz La interfaz creada invocando funciones La interfaz es creada mediante etiquetas
internas de Windows. Se cuenta con HTML. Se cuenta con una decena de
cientos de controles para realizar la controles para confeccionar la misma.
misma. Para situar los mismos dentro de Para situarlos dentro de la pantalla, se
la pantalla, se utilizan coordenadas X e Y debe emplear el sistema de coordenadas
expresadas en pixeles. El resultado final de HTML o DHTML en el caso de que el
puede verse igual en los deferentes explorador lo soporte. El resultado final
sistemas operativos de Windows. puede verse parecido en diferentes
exploradores pero no igual.
Gráficos Los gráficos pueden ser creados mediante Los gráficos pueden ser creados mediante
la utilización de GDI+ la utilización de GDI+ pero éste debe ser
ejecutado del lado del servidor.
Ejecución de La ejecución del código asociado a cada Los eventos no siempre son iniciados en
Eventos evento se produce tan pronto el mismo es forma automática, aunque si el explorador
iniciado, lo que ofrece una respuesta es Internet Explorer 5.0 o superior ASP
interactiva. .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 Las aplicaciones para Windows pueden El explorador restringe los recursos a los
recursos locales tener acceso a los recursos, aunque existe cuales la aplicación tendrá acceso.
(memoria, la posibilidad de restringir los mismos.
archivos, red,
etc.)
Escalabilidad Es medianamente escalable, y se deben Es sumamente escalable, y el costo de
tener en cuenta los costos de instalación instalación no es fuerte, ya que se debe
y/o actualización. efectuar una sola vez por versión de
aplicación.
Mercado Clientes que utilizan sistema operativo Clientes que utilizan cualquier sistema
Windows. operativo.

162
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

163
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

164
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

165
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.

166
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:

INIT (Inicializa la Pagina)

Load

Otros Eventos
PreRender

UnLoad

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:

167
Presentación (Document) Directivas (Objeto Page)

Ventana de Propiedades

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.

168
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.

169
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

170
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

171
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

172
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.

173
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).

174
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

175
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”)

176
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)

177
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:

178
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.

179
'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")

180
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.

181
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_Aplica
cion")) + 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

182
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:

183
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

184
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.

185
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

186
'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

187
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

188
'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

189
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

190
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.

191
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.

192
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()

193
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:

194
195
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

196

Anda mungkin juga menyukai