En este curso podrs aprender a desarrollar aplicaciones con la prxima generacin
de herramientas de desarrollo Microsoft Visual Studio 2005. Veremos las principales diferencias con Visual Basic 6, tanto a nivel del lenguaje como de la infraestructura de desarrollo utilizada, y acabaremos desarrollando una aplicacin real con los conceptos aprendidos. Al final de cada leccin tendrs disponible un video en el que podrs ver los conceptos explicados de forma prctica sobre el entorno de Visual Studio 2005. Te recomendamos tambin que veas la aplicacin MSDN Video, que desarrollaremos al finalizar el curso y de la que podrs descargar su cdigo fuente y videos explicativos. Recuerda que si quieres poner en prctica este curso tienes disponibles una versin sin limitaciones de Visual Basic 2005 Express, que incluye la base de datos SQL Server 2005 Express. Puedes descargarla en el rea de versiones Express. Disfruta del curso!
1 Introduccin En esta primera leccin veremos los tipos de datos que .NET Framework pone a nuestra disposicin, as mismo veremos las diferencias con respecto a los tipos de datos de Visual Basic 6.0 y las equivalencias entre los tipos de ambos entornos, de esta forma nos resultar ms fcil familiarizarnos. Aunque no debemos olvidar que en .NET los tipos de datos tienen un tratamiento, en algunos casos, especial que pueden llevarnos a confusin, por tanto en los casos que pueda existir esa posibilidad de funcionamiento diferente, veremos ejemplos de cmo los manejbamos en VB6 y cmo tendremos que usarlos desde Visual Basic 2005. A continuacin daremos un repaso a conceptos bsicos o elementales sobre los tipos de datos, que si bien nos sern familiares, es importante que lo veamos para poder comprender mejor cmo estn definidos y organizados los tipos de datos en .NET y de paso veremos las equivalencias con respecto a Visual Basic 6.0.
Tipos de datos de .NET Visual Basic 2005 est totalmente integrado con .NET Framework, por tanto los tipos de datos que podremos usar con este lenguaje sern los definidos en este "marco de trabajo", por este motivo vamos a empezar usando algunas de las definiciones que nos encontraremos al recorrer la documentacin que acompaa a este lenguaje de programacin. Los tipos de datos que podemos usar en Visual Basic 2005 son los mismo tipos de datos definidos en .NET Framework y por tanto estn soportados por todos los lenguajes que usan esta tecnologa. Estos tipos comunes se conocen como el Common Type System, (CTS), que traducido viene a significar el sistema de tipos comunes de .NET. El hecho de que los tipos de datos usados en todos los lenguajes .NET estn definidos por el propio Framework nos asegura que independientemente del lenguaje que estemos usando, siempre utilizaremos el mismo tipo interno de .NET, si bien cada lenguaje puede usar un nombre (o alias) para referirse a ellos, aunque lo importante es que siempre sern los mismos datos, independientemente de cmo se llame en cada lenguaje. Esto es una gran ventaja, ya que nos permite usarlos sin ningn tipo de problemas, al menos en cuanto a lo que a conversiones se refiere, cosa que no ocurra en Visual Basic 6.0, cuando, por ejemplo, queramos interoperar con otros lenguajes, como era el caso de C/C++, el caso ms claro era a la hora de acceder a funciones incluidas en libreras (DLL), por ejemplo del API de Windows, siempre debamos tener en cuenta que ciertos tipos de datos no eran equivalentes, el ejemplo ms claro lo tenemos en las cadenas, ya que no es lo mismo una cadena de VB6 que una de C/C++. En .NET este problema de "compatibilidad" no existe, por la sencilla razn de que una cadena de Visual Basic es lo mismo que una cadena de C#, ya que estos dos lenguajes usan el tipo definido en .NET Framework.
En los siguientes enlaces tenemos los temas a tratar en esta primera leccin del mdulo sobre las caractersticas del lenguaje Visual Basic 2005. 2
Tipos primitivos o Sufijos o caracteres y smbolos identificadores para los tipos o Tipos por valor y tipos por referencia Variables y constantes o Consejo para usar las constantes o Declarar variables o Declarar variables y asignar el valor inicial o El tipo de datos Char o Obligar a declarar las variables con el tipo de datos o Aplicar Option Strict On a un fichero en particular o Aplicar Option Stict On a todo el proyecto o Ms opciones aplicables a los proyectos Enumeraciones: Constantes agrupadas o El nombre de los miembros de las enumeraciones o Los valores de una enumeracin no son simples nmeros Arrays (matrices) o Declarar e inicializar un array o Cambiar el tamao de un array o Eliminar el contenido de un array o Los arrays son tipos por referencia Tipos primitivos
Veamos en la siguiente tabla los tipos de datos definidos en .NET Framework y los alias utilizados en Visual Basic 2005, as como el equivalente de VB6.
.NET Frameor! VB 2005 VB" System.Boolean Boolean Boolean * System.Byte Byte Byte System.Int16 Short Integer System.Int32 Integer Long System.Int64 Long N.A. System.Single Single Single System.Double Double Double System.Decimal Decimal Currency * System.Char Char N.A. (ChrW) 3 System.String String String * System.Object Object Variant / Object * System.DateTime Date Date *
System.SByte SByte N.A. System.UInt16 UShort N.A. System.UInt32 UInteger N.A. System.UInt64 ULong N.A. Ta#la $.$. Tipos de datos % e&uivalencia entre len'ua(es En la columna de equivalencias con VB6 tenemos algunos indicados con N.A., estos tipos no tienen equivalencia, por otro lado, los marcados con un asterisco (*) no tienen equivalencia directa, pero el o los tipos indicados seran los que ms se aproximaran. El caso del tipo String es un caso especial, realmente un String de .NET es casi como uno de VB6, con la diferencia de que las cadenas en .NET son inmutables, es decir, una vez que se han creado no se pueden modificar y en caso de que queramos cambiar el contenido, .NET se encarga de usar la anterior y crear una nueva cadena, por tanto si usamos las cadenas para realizar concatenaciones (unin de cadenas para crear una nueva), el rendimiento es inferior al que tenamos en VB6, si bien existe una clase en .NET que es ideal para estos casos y cuyo rendimiento es, si no superior al menos parecido al de VB6: la clase StringBuilder. Las ltimas filas mostradas en la tabla son tipos especiales que si bien son parte del sistema de tipos comunes (CTS) no forman parte de la Common Language Specification (CLS), es decir la especificacin comn para los lenguajes "compatibles" con .NET, por tanto, si queremos crear aplicaciones que puedan interoperar con todos los lenguajes de .NET, esos tipos no debemos usarlos como valores de devolucin de funciones ni como tipo de datos usado en los argumentos de las funciones, propiedades o procedimientos.
Los tipos mostrados en la tabla 1 son los tipos primitivos de .NET y por extensin de Visual Basic 2005, es decir son tipos "elementales" para los cuales cada lenguaje define su propia palabra clave equivalente con el tipo definido en el CTS de .NET Framework. Todos estos tipos primitivos podemos usarlos tanto por medio de los tipos propios de Visual Basic, los tipos definidos en .NET o bien como literales. Por ejemplo, podemos definir un nmero entero literal indicndolo con el sufijo I: 12345I o bien asignndolo a un valor de tipo Integer o a un tipo Sytem.Int32 de .NET. La nica excepcin de los tipos mostrados en la tabla 1 es el tipo de datos Object, este es un caso especial del que nos ocuparemos en la prxima leccin.
4 Sufios o caracteres y s!mbolos identificadores para los tipos Cuando usamos valores literales numricos en Visual Basic 2005, el tipo de datos que le asigna el compilador es el tipo Double, por tanto si nuestra intencin es indicar un tipo de datos diferente podemos indicarlos aadiendo una letra como sufijo al tipo, esto es algo que los ms veteranos de VB6 ya estarn acostumbrados, e incluso los ms noveles tambin, en Visual Basic 2005 algunos de ellos se siguen usando, pero el tipo asociado es el equivalente al de este nuevo lenguaje (tal como se muestra en la tabla 1), por ejemplo para indicar un valor entero podemos usar la letra I o el signo ), de igual forma, un valor de tipo entero largo (Long) lo podemos indicar usando * o +, en la siguiente tabla podemos ver los caracteres o letra que podemos usar como sufijo en un literal numrico para que el compilador lo identifique sin ningn lugar a dudas.
Tipo de datos S,m#olo -ar.cter Short N.A. S Integer % I Long & L Single ! F Double # R Decimal @ D UShort N.A. US UInteger N.A. UI ULong N.A. UL Ta#la $.2. Sufi(os para identificar los tipos de datos El uso de estos caracteres nos puede resultar de utilidad particularmente para los tipos de datos que no se pueden convertir en un valor doble. Por ejemplo, si queremos asignar este valor literal a un tipo Decimal: 12345678901234567890, tal como vemos en la figura 1, el IDE de Visual Basic 2005 nos indicar que existe un error de desbordamiento (Oerflo!) ya que esa cifra es muy grande para usarlo como valor Double, pero si le agregamos el sufijo / o 0 ya no habr dudas de que estamos tratando con un valor Decimal.
5 Fi'ura $.$. Error de des#ordamiento al intentar asi'nar un valor /ou#le a una varia#le /ecimal
Tipos por valor % tipos por referencia Los tipos de datos de .NET los podemos definir en dos grupos: Tipos por valor Tipos por referencia Los tipos por valor son tipos de datos cuyo valor se almacena en la pila o en la memoria "cercana", como los numricos que hemos visto. Podemos decir que el acceso al valor contenido en uno de estos tipos es directo, es decir se almacena directamente en la memoria reservada para ese tipo y cualquier cambio que hagamos lo haremos directamente sobre dicho valor, de igual forma cuando copiamos valores de un tipo por valor a otro, estaremos haciendo copias independientes. Por otro lado, los tipos por referencia se almacenan en el "monto" ("eap) o memoria "lejana", a diferencia de los tipos por valor, los tipos por referencia lo nico que almacenan es una referencia (o puntero) al valor asignado. Si hacemos copias de tipos por referencia, realmente lo que copiamos es la referencia propiamente dicha, pero no el contenido. Estos dos casos los veremos en breve con ms detalle.
Varia#les % constantes Disponer de todos estos tipos de datos no tendra ningn sentido si no los pudiramos usar de alguna otra forma que de forma literal. Y aqu es donde entran en juego las variables y constantes, no vamos a contarte qu son y para que sirven, 6 salvo en el caso de las constantes, ya que no todos los desarrolladores las utilizamos de la forma adecuada.
Conseo para usar las constantes Siempre que tengamos que indicar un valor constante, ya sea para indicar el mximo o mnimo permitido en un rango de valores o para comprobar el trmino de un bucle, deberamos usar una constante en lugar de un valor literal, de esta forma si ese valor lo usamos en varias partes de nuestro cdigo, si en un futuro decidimos que dicho valor debe ser diferente, nos resultar ms fcil realizar un solo cambio que cambiarlo en todos los sitios en los que lo hemos usado, adems de que de esta forma nos aseguramos de que el cambio se realiza adecuadamente y no tendremos que preocuparnos de las consecuencias derivadas de no haber hecho el cambio en todos los sitios que deberamos. Pero esto no es algo nuevo, las constantes se definen de la misma forma que en VB6, salvo que ahora podemos obligarnos a indicar el tipo de datos que esa constante va a contener. Esto lo veremos en la siguiente seccin.
"eclarar variables La declaracin de las variables en Visual Basic 2005 se hace de la misma forma que en VB6, o casi, las excepciones vienen dadas, como hemos comentado antes, de la posibilidad de obligar a definir el tipo de cada variable y de cmo podemos definir ms de una variable en una misma instruccin Dim. Para no embrollar mucho la cosa, veamos ejemplos de cmo definir o declarar variables siguiendo las buenas costumbres, es decir, indicando siempre el tipo de datos de la variable. En VB6 podamos definir ms de una variable en una misma instruccin Dim, aunque dependiendo de cmo hiciramos esa declaracin poda ser que, normalmente para nuestro pesar, que no todas las variables fuesen del tipo que "supuestamente" habamos querido indicar. Por ejemplo, con esta declaracin: Dim a, b, c As Integer A primera vista estamos declarando tres variables de tipo Integer, pero realmente solo declara con el tipo indicado a la ltima variable, las otras dos, se declaran con tipo #ariant o el tipo de datos predefinido. En Visual Basic 2005 al usar esa misma lnea de cdigo estamos declarando tres variables de tipo Integer, esto es algo que debemos tener en cuenta, sobre todo si nuestra intencin era hacer precisamente lo que VB6 haca, es decir, declarar dos variables de tipo #ariant y una de tipo Integer. 7
"eclarar variables y asignar el valor inicial En Visual Basic 2005 tambin podemos inicializar una variable con un valor distinto al predeterminado, que en los tipos numricos es un cero, en las fechas es el 1 de enero del ao 1 a las doce de la madrugada (10$20$2000$ $23003004M1) y en la cadenas es un valor nulo ($ot"ing), para hacerlo, simplemente tenemos que indicar ese valor, tal como veremos es muy parecido a como se declaran las constantes. Por ejemplo: Dim a As Integer = 10 En esa misma lnea podemos declarar y asignar ms variables, pero todas deben estar indicadas con el tipo de datos: Dim a As Integer = 10, b As Integer = 25 Por supuesto, el tipo de datos puede ser cualquiera de los tipos primitivos: Dim a As Integer = 10, b As Integer = 25, s As String = "Hola" Aunque para que el cdigo sea ms legible, y fcil de depurar, no deberamos mezclar en una misma instruccin Dim ms de un tipo de datos.
Nota3 Es importante saber que en las cadenas de Visual Basic 2005 el valor de una variable de tipo String no inicializada NO es una cadena vaca como ocurra en VB6, sino un valor nulo ($ot"ing).
El tipo de datos C#ar En Visual Basic 2005 podemos declarar valores de tipo C"ar, este tipo de datos es un carcter Unicode y podemos declararlo y asignarlo a un mismo tiempo. El problema con el que nos podemos encontrar es a la hora de indicar un carcter literal. Si bien en VB6 no existe el tipo de datos C"ar, si podemos convertir un valor numrico en un carcter (realmente en una cadena) o bien podemos convertir un carcter en su correspondiente valor numrico. Dim c As String 8 c = Chr(65) Dim n As Integer n = Asc("A") En Visual Basic 2005 tambin podemos usar esas mismas funciones, aunque en el caso de C"r, el valor que devuelve esta funcin es un valor de tipo C"ar, no de tipo String como ocurre en VB6, pero debido a que un valor de tipo C"ar se puede convertir en una cadena, podemos hacer una asignacin como la mostrada en el cdigo anterior sin ningn tipo de problemas. Si nuestra intencin es asignar un valor C"ar a una variable, adems de la funcin C"r, podemos hacerlo con un literal, ese valor literal estar encerrado entre comillas dobles, (al igual que una cadena), aunque para que realmente sea un carcter debemos agregarle una c justo despus del cierre de las comillas dobles: Dim c As Char = "A"c
$bligar a declarar las variables con el tipo de datos La obligatoriedad, a la que hacamos referencia anteriormente, de declarar las variables y constantes con el tipo de datos adecuado, la podemos aplicar a todo el proyecto o a un mdulo en particular, para ello tenemos que usar la instruccin Option Strict On, una vez indicado, se aplicar a todo el cdigo, no solo a las declaraciones de variable, constantes o al tipo de datos devuelto por las funciones y propiedades, sino tambin a las conversiones y asignaciones entre diferentes tipos de datos. No debemos confundir Option Strict con Option %&plicit, este ltimo, al igual que en VB6, nos obliga a declarar todas las variables, mientras que el primero lo que hace es obligarnos a que esas declaraciones tengan un tipo de datos. Tanto una como la otra tienen dos estados: conectado o desconectado dependiendo de que agreguemos On u Off respectivamente. Como recomendacin para buenas prcticas, debemos "conectar" siempre estas dos opciones, si bien Option %&plicit On ya viene como valor por defecto, cosa que no ocurre con Option Strict, que por defecto est desconectado.
9 Aplicar $ption Strict $n a un fic#ero en particular Cuando en VB6 agregbamos un nuevo formulario, mdulo BAS o mdulo de clase, se agregaba automticamente la instruccin Option %&plicit, en Visual Basic 2005 esta opcin est predefinida y no se agrega a ningn mdulo, pero eso no significa que no se aplique, aunque siempre podemos escribir esas instrucciones (con el valor On al final) en cada uno de los mdulos o ficheros de cdigo que agreguemos a nuestro proyecto. Lo mismo podemos hacer con Option Strict On, en caso de que nos decidamos a hacerlo, esas lneas de cdigo deben aparecer al principio del fichero y solamente pueden estar precedidas de comentarios (instrucciones '%( o lneas iniciadas con una comilla simple). En la figura 1 mostrada anteriormente tenemos una captura del editor de Visual Basic 2005 en la que hemos indicado que queremos tener comprobacin estricta.
Aplicar $ption Stict $n a todo el proyecto Tambin podemos hacer que Option Strict funcione igual que Option %&plicit, es decir, que est activado a todo el proyecto, en este caso no tendramos que indicarlo en cada uno de los ficheros de cdigo que formen parte de nuestro proyecto, si bien solamente ser aplicable a los que no tengan esas instrucciones, aclaremos esto ltimo: si Option Strict (u Option %&plicit) est definido de forma global al proyecto, podemos desactivarlo en cualquiera de los ficheros, para ello simplemente habra que usar esas declaraciones pero usando Off en lugar de On. De igual forma, si ya est definido globalmente y lo indicamos expresamente, no se producir ningn error. Lo importante aqu es saber que siempre se usar el estado indicado en cada fichero, independientemente de cmo lo tengamos definido a nivel de proyecto. Para que siempre se usen estas asignaciones en todo el proyecto, vamos a ver cmo indicarlo en el entorno de Visual Basic 2005. Abrimos el Visual Basic 2005 Express y una vez que se haya cargado, (no hace falta crear ningn nuevo proyecto, de este detalle nos ocuparemos en breve), seleccionamos la opcin 5erramientas67pciones... se mostrar un cuadro de dilogo y del panel izquierdo seleccionamos la opcin 8ro%ectos % soluciones, la expandimos y seleccionamos Valores predeterminados de VB y veremos ciertas opciones, tal como podemos comprobar en la figura 1.2: 10 Fi'ura $.2. 7pciones de pro%ectos 9opciones m,nimas: De la lista despegable 7ption Strict, seleccionamos 7n. Por defecto ya estarn seleccionadas las opciones 7n de 7ption E;plicit y Binar% de 7ption -ompare, por tanto no es necesario realizar ningn cambio ms, para aceptar los cambios y cerrar el cuadro de dilogo, pulsamos en el botn 4ceptar.
Nota3 Aunque en esta captura muestre: C:\vbexpB1 en Default project location, salvo que lo cambiemos, aparecer el path por defecto dentro de Mis documentos.
Como podemos observar, aparecen pocas opciones, realmente el IDE de Visual Basic 2005 Express nos muestra las predeterminadas para los desarrolladores de Visual Basic, si bien, podemos hacer que se muestren todas las disponibles, para hacerlo, debemos marcar la casilla que est en la parte inferior izquierda en la que podemos leer: Mostrar todas las confi'uraciones, al seleccionar esa opcin nos mostrar un nmero mayor de opciones, tal como podemos ver en la figura 1.3: 11 Fi'ura $.<. 7pciones de pro%ectos 9todas las opciones:
Desde este momento el compilador de VB se volver estricto en todo lo relacionado a las declaraciones de variables y conversiones, tal como vemos en la figura 1.4 al intentar declarar una variable sin indicar el tipo de datos. Fi'ura $.=. 4viso de 7ption Strict al declarar una varia#le sin tipo
12 Nota3 Una de las ventajas del IDE (Integrated Deelopment %nironment, entorno de desarrollo integrado) de Visual Basic 2005 es que nos avisa al momento de cualquier fallo que cometamos al escribir el cdigo, este "pequeo" detalle, aunque alguna veces puede llegar a parecer fastidioso, nos facilita la escritura de cdigo, ya que no tenemos que esperar a realizar la compilacin para que tengamos constancia de esos fallos.
%&s opciones aplicables a los proyectos Aunque en estos mdulos no trataremos a fondo el entorno de desarrollo, ya que la finalidad de este curso online es tratar ms en el cdigo propiamente dicho, vamos a mostrar otro de los sitios en los que podemos indicar dnde indicar que se haga una comprobacin estricta de tipos y, como veremos, tambin podremos indicar algunas "nuevas peculiaridades" de Visual Basic 2005, todas ellas relacionadas con el tema que estamos tratando. Cuando tengamos un proyecto cargado en el IDE de Visual Basic 2005 Express, (pronto veremos cmo crear uno), podemos mostrar las propiedades del proyecto, para ello seleccionaremos del men 8ro%ecto la opcin 8ropiedades de >Nom#re/el8ro%ecto6 y tendremos un cuadro de dilogo como el mostrado en la figura 1.5. 13 Fi'ura $.5. Fic?a -ompilar de las opciones del pro%ecto actual Seleccionando la ficha -ompilar, adems de las tpicas opciones de 7ption Strict, 7ption E;plicit y 7ption -ompare, (estas asignaciones solo sern efectivas para el proyecto actual), tendremos cmo queremos que reaccione el compilador si se cumple algunas de las condiciones indicadas. Entre esas condiciones, tenemos algo que muchos desarrolladores de Visual Basic siempre hemos querido tener: Que nos avise cuando una variable la hemos declarado pero no la utilizamos (Varia#le local no utili@ada). Al tener marcada esta opcin (normalmente como una 4dvertencia), si hemos declarado una variable y no la usamos en el cdigo, (siempre que no le hayamos asignado un valor al declararla), nos avisar, tal como podemos ver en la figura 1.6: 14 Fi'ura $.". 4viso de varia#le no usada
Enumeraciones3 -onstantes a'rupadas Las enumeraciones no son una novedad de Visual Basic 2005, aunque como veremos, si lo es la forma de usarlas, e incluso de declararlas. Aunque esos cambios son menores y la funcionalidad que tenamos en VB6 sigue siendo la misma. De todas formas echemos un breve repaso a las enumeraciones para que tengamos los conceptos claros. Una enumeracin es una serie de constantes que estn relacionadas entre s. La utilidad de las enumeraciones es ms manifiesta cuando queremos manejar una serie de valores constantes con nombre, es decir, podemos indicar un valor, pero en lugar de usar un literal numrico, usamos un nombre, ese nombre es, al fin y al cabo, una constante que tiene un valor numrico. En Visual Basic 2005 las enumeraciones pueden ser de cualquier tipo numrico integral, incluso enteros sin signo, aunque el valor predefinido es el tipo Integer. Podemos declarar una enumeracin de varias formas: 1- De forma, normal, a la vieja usanza: n!m Colores "o#o $er%e A&!l n% n!m 15 En este primer caso, el tipo de datos de cada miembro de la enumeracin ser Integer. 2- Indicando el tipo de datos que realmente tendr: n!m Colores As 'ong "o#o $er%e A&!l n% n!m En este segundo caso, el valor mximo que podemos asignar a los miembros de una enumeracin ser el que pueda contener un tipo de datos Long. 3- Indicando el atributo )lags*ttibute, (realmente no hace falta indicar el sufijo *ttribute cuando usamos los atributos) de esta forma podremos usar los valores de la enumeracin para indicar valores que se pueden "sumar" o complementar entre s, pero sin perder el nombre, en breve veremos qu significa esto de "no perder el nombre". ()lags()* + n!m Colores As ,-te "o#o = 1 $er%e = 2 A&!l = . n% n!m
Nota3 Los atributos los veremos con ms detalle en otra leccin de este mismo mdulo.
16 El nombre de los miembros de las enumeraciones Tanto si indicamos o no el atributo )lags a una enumeracin, la podemos usar de esta forma: Dim c As Colores = Colores/A&!l 0r Colores/"o#o Es decir, podemos "sumar" los valores definidos en la enumeracin. Antes de explicar con detalle que beneficios nos puede traer el uso de este atributo, veamos una caracterstica de las enumeraciones que no tenamos en VB6. Como hemos comentado, las enumeraciones son constantes con nombres, pero en Visual Basic 2005 esta definicin llega ms lejos que en VB6, de hecho, podemos saber "el nombre" de un valor de una enumeracin, para ello tendremos que usar el mtodo ToString, (el cual se usa para convertir en una cadena cualquier valor numrico). Por ejemplo, si tenemos la siguiente asignacin: Dim s As String = Colores/A&!l/1oString La variable s contendr la palabra A4@ulA no el valor 4. Esto es aplicable a cualquier tipo de enumeracin, se haya o no usado el atributo )lags*ttribute. Una vez aclarado este comportamiento de las enumeraciones en Visual Basic 2005, veamos que es lo que ocurre cuando sumamos valores de enumeraciones a las que hemos aplicado el atributo )lags y a las que no se lo hemos aplicado. Empecemos por este ltimo caso. Si tenemos este cdigo: n!m Colores As ,-te "o#o = 1 $er%e = 2 A&!l = . n% n!m Dim c As Colores = Colores/A&!l 0r Colores/"o#o Dim s As String = c/1oString 17 El contenido de la variable s ser A5A, es decir, la representacin numrica del valor contenido: 4 + 1, ya que el valor de la constante 4@ul es 4 y el de la constante Bo(o es 1. Pero si ese mismo cdigo lo usamos de esta forma (aplicando el atributo )lags a la enumeracin): ()lags()* + n!m Colores As ,-te "o#o = 1 $er%e = 2 A&!l = . n% n!m Dim c As Colores = Colores/A&!l 0r Colores/"o#o Dim s As String = c/1oString El contenido de la variable s ser: ABo(oC 4@ulA, es decir, se asignan los nombres de los miembros de la enumeracin que intervienen en ese valor, no el valor "interno".
'os valores de una enumeraci(n no son simples n)meros Como hemos comentado, los miembros de las enumeraciones realmente son valores de un tipo de datos entero (en cualquiera de sus variedades) tal como podemos comprobar en la figura 1.7: Fi'ura $.D. *os tipos su#%acentes posi#les de una enumeracin 18
Por tanto, podemos pensar que al igual que ocurra en VB6, podemos usar cualquier valor para asignar a una variable declarada como una enumeracin, al menos si ese valor est dentro del rango adecuado. Pero en Visual Basic 2005 esto no es posible, al menos si lo hacemos de forma "directa" y con 7ption Strict conectado, ya que recibiremos un error indicndonos que no podemos convertir, por ejemplo, un valor entero en un valor del tipo de la enumeracin. En la figura 1.8 podemos ver ese error al intentar asignar el valor 3 a una variable del tipo -olores (definida con el tipo predeterminado Integer). Fi'ura $.E. Error al asi'nar un valor AnormalA a una varia#le del tipo -olores
El error nos indica que no podemos realizar esa asignacin, pero el entorno integrado de Visual Basic Express 2005 tambin nos ofrece alternativas para que ese error no se produzca, esa ayuda se obtiene pulsando en el signo de admiracin que tenemos justo donde est el cursor del ratn, pero no solo nos dice cmo corregirlo, sino que tambin nos da la posibilidad de que el propio IDE se encargue de corregirlo, tal como podemos apreciar en la figura 1.9. Fi'ura $.F. 7pciones de correccin de errores Lo nico que tendramos que hacer es pulsar en la sugerencia de correccin, que en este caso es la nica que hay, pero en otros casos pueden ser varias las opciones y tendramos que elegir la que creamos adecuada. 19 El cdigo final (una vez corregido) quedara de la siguiente forma: Dim c As Colores = C1-2e(3, Colores) CType es una de las formas que nos ofrece Visual Basic 2005 de hacer conversiones entre diferentes tipos de datos, en este caso convertimos un valor entero en uno del tipo -olores. Si compilamos y ejecutamos la aplicacin, sta funcionar correctamente. Aunque sabemos que es posible que usando CType no asignemos un valor dentro del rango permitido. En este caso, el valor 3 podramos darlo por bueno, ya que es la suma de 1 y 2 (Bo(o y Verde), pero que pasara si el valor asignado es, por ejemplo, 15? En teora no deberamos permitirlo. Estas validaciones podemos hacerlas de dos formas: 1- Con la clsica solucin de comprobar el valor indicado con todos los valores posibles. 2- Usando funciones especficas del tipo %num. Aunque en este ltimo caso, solo podremos comprobar los valores definidos en la enumeracin. En el siguiente ejemplo podemos hacer esa comprobacin. S!b mostrarColor(,-$al c As Colores) 4 com2robar si el 5alor in%ica%o es correcto 4 si no est67 %e8ini%o, !sar el 5alor A&!l I8 9n!m:/IsDe8ine%(;et1-2e(Colores), c) = )alse 1hen c = Colores/A&!l n% I8 Console/<rite'ine("l color es =0>", c) n% S!b Este cdigo lo que hace es comprobar si el tipo de datos -olores tiene definido el valor contenido en la variable c, en caso de que no sea as, usamos un valor predeterminado.
Nota3 La funcin IsDefined slo comprueba los valores que se han definido en la enumeracin, no las posibles combinaciones que podemos conseguir sumando cada uno de sus miembros, incluso aunque hayamos usado el atributo )lags*ttribute.
20 4rra%s 9matrices: Los arrays (o matrices) tambin es algo que podamos usar en VB6, si bien la forma en que se almacena en la memoria y la forma en que podemos usarlas en Visual Basic 2005 ha cambiado, aunque la forma en que podemos usarlas es idntica a VB6... o casi. En Visual Basic 2005 la declaracin de un array la haremos de la misma forma que en VB6, en el siguiente ejemplo declaramos un array de tipo String llamado nom#res: Dim nombres() As String Al igual que en VB6, podemos indicar el nmero de elementos que contendr el array: Dim nombres(10) As String Tal como ocurre en VB6, al realizar esta declaracin lo que conseguimos es definir un array de 11 elementos: desde cero hasta 10. Si bien, en VB6 tenemos la posibilidad de indicar cual era el valor del ndice inferior predeterminado de los arrays, podemos elegir entre cero y uno mediante la instruccin 7ption Base indicando a continuacin un 0 o un 1. En Visual Basic 2005 no existe esa instruccin, es ms en Visual Basic 2005 todos los arrays deben tener como ndice inferior el valor cero. Otra posibilidad que tenamos en VB6 era indicar el rango de ndices que podamos asignar, esto lo logrbamos usando la clusula To al definir un array, por ejemplo: Dim nombres(10 1o 25) As String Pero esta declaracin es ilegal en Visual Basic 2005, por el hecho de que los arra%s de .NET siempre de#en tener el valor cero como ,ndice inferior. Lo que si podemos hacer en Visual Basic 2005 es usar To para indicar el valor mximo del ndice, aunque no tiene la misma "potencia" que en VB6, al menos de esta forma el cdigo resultar ms legible: Dim nombres(0 1o 10) As String
"eclarar e iniciali*ar un array Lo que no podemos hacer en VB6 era declarar un array y al mismo tiempo asignarle valores. En Visual Basic 2005 esto lo hacemos indicando los valores a asignar justo despus de la declaracin y encerrndolos entre llaves: 21 Dim nombres() As String = ="?e2e", "@!an", "'!isa"> Con el cdigo anterior estamos creando un array de tipo String con tres valores cuyos ndices van de cero a dos. Si el array es bidimensional (o con ms dimensiones), tambin podemos inicializarlos al declararlo, pero en este caso debemos usar doble juego de llaves: Dim nombres(,) As String = =="@!an", "?e2e">, ="Ana", "5a">> En este cdigo tendramos un array bidimensional con los siguientes valores: nombres(0,0)= Juan nombres(0,1)= Pepe nombres(1,0)= Ana nombres(1,1)= Eva
Cambiar el tama+o de un array Para cambiar el tamao de un array, al igual que en VB6, usaremos la instruccin 'eDim, pero a diferencia de VB6 no podemos usar 'eDim para definir un array, en Visual Basic 2005 siempre hay que declarar previamente los arrays antes de cambiarles el tamao. Lo que tambin podemos hacer en Visual Basic 2005 es cambiar el tamao de un array y mantener los valores que tuviera anteriormente, para lograrlo debemos usar 'eDim +resere. Si bien tanto 'eDim como 'eDim +resere se pueden usar en arrays de cualquier nmero de dimensiones, en los arrays de ms de una dimensin solamente podemos cambiar el tamao de la ltima dimensin.
Eliminar el contenido de un array Una vez que hemos declarado un array y le hemos asignado valores, es posible que nos interese eliminar esos valores de la memoria, para lograrlo, podemos hacerlo de tres formas: 1. Redimensionando el array indicando que tiene cero elementos, aunque en el mejor de los casos, si no estamos trabajando con arrays de ms de una 22 dimensin, tendramos un array de un elemento, ya que, como hemos comentado anteriormente, los arrays de .NET el ndice inferior es cero. 2. Usar la instruccin %rase. Al igual que en VB6, %rase elimina totalmente el array de la memoria. 3. Asignar un valor $ot"ing al array. Esto funciona en Visual Basic 2005 pero no en VB6, debido a que en Visual Basic 2005 los arrays son tipos por referencia.
'os arrays son tipos por referencia Como acabamos de ver, en Visual Basic 2005 los arrays son tipos por referencia, y tal como comentamos anteriormente, los tipos por referencia realmente lo que contienen son una referencia a los datos reales no los datos propiamente dichos. Cual es el problema? Vemoslo con un ejemplo y as lo tendremos ms claro. Dim nombres() As String = ="@!an", "?e2e", "Ana", "5a"> Dim otros() As String otros = nombres nombres(0) = "Antonio" En este ejemplo definimos el array nom#res y le asignamos cuatro valores. A continuacin definimos otro array llamado otros y le asignamos lo que tiene nom#res. Por ltimo asignamos un nuevo valor al elemento cero del array nom#res. Si mostramos el contenido de ambos arrays nos daremos cuenta de que realmente solo existe una copia de los datos en la memoria, y tanto nom#res90: como otros90: contienen el nombre A4ntonioA. En VB6 cada array era independiente del otro y esa asignacin a nom#res90: no afectaba al valor contenido en otros90:. Qu ha ocurrido? Que debido a que los arrays son tipos por referencia, solamente existe una copia de los datos y tanto la variable nombres como la variable otros lo que contienen es una referencia (o puntero) a los datos. Si realmente queremos tener copias independientes, debemos hacer una copia del array nombres en el array otros, esto es fcil de hacer si usamos el mtodo CopyTo. 23 ste mtodo existe en todos los arrays y nos permite copiar un array en otro empezando por el ndice que indiquemos. El nico requisito es que el array de destino debe estar inicializado y tener espacio suficiente para contener los elementos que queremos copiar. En el siguiente cdigo de ejemplo hacemos una copia del contenido del array nom#res en el array otros, de esta forma, el cambio realizado en el elemento cero de nom#res no afecta al del array otros. Dim nombres() As String = ="@!an", "?e2e", "Ana", "5a"> Dim otros() As String "eDim otros(nombres/'ength) nombres/Co2-1o(otros, 0) nombres(0) = "Antonio"
Adems del mtodo CopyTo, los arrays tienen otros miembros que nos pueden ser de utilidad, como por ejemplo la propiedad Lengt" usada en el ejemplo para saber cuantos elementos tiene el array nom#res. Para averiguar el nmero de elementos de un array, tambin podemos usar la funcin ,Bound, que es la que nos serva en Visual Basic 6.0 para saber esa informacin. Sin embargo, el uso de la funcin LBound, (que sirve para averiguar el ndice inferior de un array), no tiene ningn sentido en Visual Basic 2005, ya que todos los arrays siempre tienen un valor cero como ndice inferior. Para finalizar este tema, solo nos queda por decir, que los arrays de Visual Basic 2005 realmente son tipos de datos derivados de la clase *rray y por tanto disponen de todos los miembros definidos en esa clase, aunque de esto hablaremos en la prxima leccin, en la que tambin tendremos la oportunidad de profundizar un poco ms en los tipos por referencia y en como podemos definir nuestros propios tipos de datos, tanto por referencia como por valor.
24 Introduccin En la leccin anterior vimos los tipos de datos predefinidos en .NET Framework, en esta leccin veremos cmo podemos crear nuestros propios tipos de datos, tanto por valor como por referencia. Tambin tendremos ocasin de ver los distintos niveles de accesibilidad que podemos aplicar a los tipos, as como a los distintos miembros de esos tipos de datos. De los distintos miembros que podemos definir en nuestros tipos, nos centraremos en las propiedades para ver en detalle los cambios que han sufrido con respecto a VB6. Tambin veremos temas relacionados con la programacin orientada a objetos (POO) en general y de forma particular los que ataen a las interfaces.
-lases % estructuras Clases: Tipos por referencia definidos por el usuario o Las clases: El corazn de .NET Framework La herencia: Caracterstica principal de la Programacin Orientada a Objetos Encapsulacin y Polimorfismo: Dos viejos conocidos de VB Object: La clase base de todas las clases de .NET o Definir una clase Una clase especial: Module Los miembros de una clase Caractersticas de los mtodos y propiedades Accesibilidad, mbito y miembros compartidos Parmetros y parmetros opcionales Array de parmetros opcionales (ParamArray) Sobrecarga de mtodos y propiedades Parmetros por valor y parmetros por referencia o Instanciar: Crear un objeto en la memoria Declarar y asignar en un solo paso El constructor: El punto de inicio de una clase Constructores parametrizados Cuando Visual Basic 2005 no crea un constructor automticamente El destructor: El punto final de la vida de una clase Estructuras: Tipos por valor definidos por el usuario o Definir una estructura o Constructores de las estructuras o Destructores de las estructuras o Los miembros de una estructura o Cmo usar las estructuras 25 Accesibilidad y &mbito o mbito mbito de bloque mbito de procedimiento mbito de mdulo mbito de espacio de nombres o La palabra clave Global o Accesibilidad Accesibilidad de las variables en los procedimientos o Las accesibilidades predeterminadas o Anidacin de tipos Los tipos anidables El nombre completo de un tipo Importacin de espacios de nombres Alias de espacios de nombres ,ropiedades o Definir una propiedad o Propiedades de solo lectura o Propiedades de solo escritura o Diferente accesibilidad para los bloques Get y Set o Propiedades predeterminadas Sobrecarga de propiedades predeterminadas -nterfaces o Qu es una interfaz? o Qu contiene una interfaz? o Una interfaz es un contrato o Las interfaces y el polimorfismo o Usar una interfaz en una clase o Acceder a los miembros implementados o Saber si un objeto implementa una interfaz o Implementacin de mltiples interfaces o Mltiple implementacin de un mismo miembro o Dnde podemos implementar las interfaces? o Un ejemplo prctico usando una interfaz de .NET
-lases3 Tipos por referencia definidos por el usuario Tal como vimos en la leccin anterior, los tipos de datos se dividen en dos grupos: tipos por valor y tipos por referencia. Los tipos por referencia realmente son clases, de la cuales debemos crear una instancia para poder usarlas, esa instancia o copia, se crea siempre en la memoria lejana ("eap) y las variables lo nico que contienen es una referencia a la direccin de memoria en la que el CLR (Common Language 'untime, motor en tiempo de ejecucin de .NET), ha almacenado el objeto recin creado. 26 Al igual que ocurre en VB6, tambin podemos crear nuestras propias clases en Visual Basic 2005, aunque como veremos tanto la forma de definirlas como de instanciarlas ha cambiado un poco. Aunque ese cambio es solo, digamos, en la forma, ya que en el fondo es lo mismo, siempre salvando las distancias, ya que como veremos, las clases de Visual Basic 2005 pueden llegar a ser mucho ms verstiles e incluso potentes que las de VB6. Antes de entrar en detalles sintcticos, veamos la importancia que tienen las clases en .NET Framework y como repercuten en las que podamos definir nosotros usando Visual Basic 2005.
*as clases3 el cora@n de .NET Frameor! Prcticamente todo lo que podemos hacer en .NET Framework lo hacemos mediante clases. La librera de clases de .NET Framework es precisamente el corazn del propio .NET, en esa librera de clases est todo lo que podemos hacer dentro de este marco de programacin; para prcticamente cualquier tarea que queramos realizar existen clases, y si no existen, las podemos definir nosotros mismos, bien ampliando la funcionalidad de alguna clase existente mediante la herencia, bien implementando algn tipo de funcionalidad previamente definida o simplemente crendolas desde cero.
'a #erencia: Caracter!stica principal de la ,rogramaci(n $rientada a $betos El concepto de Programacin Orientada a Objetos (POO) es algo intrnsico al propio .NET Framework, por tanto es una caracterstica que todos los lenguajes basados en este "marco de trabajo" tienen de forma predeterminada, entre ellos el Visual Basic 2005. De las caractersticas principales de la POO tenemos que destacar la herencia, que en breve podemos definir como una caracterstica que nos permite ampliar la funcionalidad de una clase existente sin perder la que ya tuviera previamente. Gracias a la herencia, podemos crear una nueva clase que se derive de otra, esta nueva clase puede cambiar el comportamiento de la clase base y/o ampliarlo, de esta forma podemos adaptar la clase, llammosla, original para adaptarla a nuestras necesidades. El tipo de herencia que .NET Framework soporta es la herencia simple, es decir, solo podemos usar una clase como base de la nueva, si bien, como veremos ms adelante, podemos agregar mltiple funcionalidad a nuestra nueva clase. Esta funcionalidad nos servir para aprovechar la funcionalidad de muchas de las clases existentes en .NET Framework, funcionalidad que solamente podremos aplicar si previamente hemos firmado un contrato que asegure a la clase de .NET que la nuestra est preparada para soportar esa funcionalidad, esto lo veremos dentro de poco con ms detalle.
27 Encapsulaci(n y ,olimorfismo: "os vieos conocidos de V. La encapsulacin y el polimorfismo son otras dos caractersticas de la programacin orientada a objetos. Aunque estas dos no nos resultarn desconocidas, ya que desde la versin 5.0 de Visual Basic estaban a nuestra disposicin. La encapsulacin nos permite abstraer la forma que tiene de actuar una clase sobre los datos que contiene o manipula, para poder lograrlo se exponen como parte de la clase los mtodos y propiedades necesarios para que podamos manejar esos datos sin tener que preocuparnos cmo se realiza dicha manipulacin. El polimorfismo es una caracterstica que nos permite realizar ciertas acciones o acceder a la informacin de los datos contenidos en una clase de forma semi- annima, al menos en el sentido de que no tenemos porqu saber sobre que tipo objeto realizamos la accin, ya que lo nico que nos debe preocupar es que podemos hacerlo, por la sencilla razn de que estamos usando ciertos mecanismos que siguen unas normas que estn adoptadas por la clase. El ejemplo clsico del polimorfismo es que si tengo un objeto que "sabe" cmo morder, da igual que lo aplique a un ratn o a un dinosaurio, siempre y cuando esas dos "clases" expongan un mtodo que pueda realizar esa accin... y como deca la documentacin de Visual Basic 5.0, siempre ser preferible que nos muerda un ratn antes que un dinosaurio.
$bect: 'a clase base de todas las clases de .NET Todas las clases de .NET se derivan de la clase Object, es decir, lo indiquemos o no, cualquier clase que definamos tendr el comportamiento heredado de esa clase. El uso de la clase Object como base del resto de las clases de .NET es la nica excepcin a la herencia simple soportada por .NET, ya que de forma implcita, todas las clases de .NET se derivan de la clase Object independientemente de que estn derivadas de cualquier otra. Esta caracterstica nos asegura que siempre podremos usar un objeto del tipo Object para acceder a cualquier clase de .NET, aunque no debemos abrumarnos todava, ya que en el texto que sigue veremos con ms detalle que significado tiene esta afirmacin. De los miembros que tiene la clase Object debemos resaltar el mtodo ToString, el cual ya lo vimos en la leccin anterior cuando queramos convertir un tipo primitivo en una cadena. Este mtodo est pensado para devolver una representacin en formato cadena de un objeto. El valor que obtengamos al usar este mtodo depender de cmo est definido en cada clase y por defecto lo que devuelve es el nombre completo de la clase, si bien en la mayora de los casos el valor que obtendremos al usar este mtodo debera ser ms intuitivo, por ejemplo los tipos de datos primitivos tienen definido este mtodo para devuelva el valor que contienen, de igual forma, nuestras clases tambin deberan devolver un valor adecuado al contenido almacenado. De cmo hacerlo, nos ocuparemos en breve.
28 Nota3 Todos los tipos de datos de .NET, ya sean por valor o por referencia siempre estn derivados de la clase Object, por tanto podremos llamar a cualquiera de los mtodos que estn definidos en esa clase. Aunque en el caso de los tipos de datos por valor, cuando queremos acceder a la clase Object que contienen, .NET Framework primero debe convertirla en un objeto por referencia (boxing) y cuando hemos dejado de usarla y queremos volver a asignar el dato a la variable por valor, tiene que volver a hacer la conversin inversa (unboxing).
/efinir una clase En Visual Basic 6.0, cada vez que definimos una clase tenemos que agregar al proyecto un fichero con extensin .cls, y a partir de ese momento, todo el cdigo escrito en ese fichero formaba parte de la clase. En Visual Basic 2005 esto ha cambiado, y aunque lo habitual es que usemos un fichero independiente para cada clase que escribamos, esto solo es algo opcional, en principio porque en VB2005 solo existe un tipo de fichero para escribir el cdigo: un fichero con extensin .v#, en el que podemos escribir una clase o cualquiera de los tipos que el lenguaje nos permite.
Nota3 En Visual Basic 2005 cualquier cdigo que queramos escribir estar dentro de una clase.
En Visual Basic 2005 las clases se definen usando la palabra clave Class seguida del nombre de la clase, esa definicin acaba indicndolo con %nd Class. En el siguiente ejemplo definimos una clase llamada Cliente que tiene dos campos pblicos. Class Cliente ?!blic Aombre As String ?!blic A2elli%os As String n% Class Una vez definida la clase podemos agregar los elementos (o miembros) que creamos conveniente. En el ejemplo anterior, para simplificar, hemos agregado dos campos pblicos, aunque tambin podramos haber definido cualquiera de los miembros permitidos en las clases. 29
Gna clase especial3 Module En Visual Basic 2005 tambin podemos definir una clase especial llamada Module, este tipo de clase tiene un tratamiento especial y es el equivalente a los mdulos B4S de VB6. La definicin se hace usando la instruccin (odule seguida del nombre a usar y acaba con %nd (odule. Cualquier miembro definido en un (odule siempre estar accesible en todo el proyecto y para usarlos no tendremos que crear ningn objeto en memoria. Las clases definidas con la palabra clave (odule realmente equivalen a las clases en las que todos los miembros estn compartidos y por tanto siempre disponibles a toda la aplicacin. De todos estos conceptos nos ocuparemos en las siguientes lecciones, pero es necesario explicar que existe este tipo de clase ya que ser el tipo de datos que el IDE de Visual Basic 2005 usar al crear aplicaciones del tipo consola, ya que ese ser el tipo de proyecto que crearemos para practicar con el cdigo mostrado en este primer mdulo.
*os miem#ros de una clase Una clase puede contener cualquiera de estos elementos (miembros): Enumeraciones Campos Mtodos (funciones o procedimientos) Propiedades Eventos *as enumeraciones, como vimos en la leccin anterior, podemos usarlas para definir valores constantes relacionados, por ejemplo para indicar los valores posibles de cualquier "caracterstica" de la clase. *os campos son variables usadas para mantener los datos que la clase manipular. *os mHtodos son las acciones que la clase puede realizar, normalmente esas acciones sern sobre los datos que contiene. Dependiendo de que el mtodo devuelva o no un valor, podemos usar mtodos de tipo )unction o de tipo Sub respectivamente. 30 *as propiedades son las "caractersticas" de las clases y la forma de acceder "pblicamente" a los datos que contiene. Por ejemplo, podemos considerar que el nombre y los apellidos de un cliente son dos caractersticas del cliente. *os eventos son mensajes que la clase puede enviar para informar que algo est ocurriendo en la clase.
-aracter,sticas de los mHtodos % propiedades
Accesibilidad/ &mbito y miembros compartidos Aunque estos temas los veremos en breve con ms detalle, para poder comprender mejor las caractersticas de los miembros de una clase (o cualquier tipo que definamos), daremos un pequeo adelanto sobre estas caractersticas que podemos aplicar a los elementos que definamos. Accesibilidad y mbito son dos conceptos que estn estrechamente relacionados. Aunque en la prctica tienen el mismo significado, ya que lo que representan es la "cobertura" o alcance que tienen los miembros de las clases e incluso de las mismas clases que definamos. Si bien cada uno de ellos tienen su propia "semntica", tal como podemos ver a continuacin: mbito Es el alcance que la definicin de un miembro o tipo puede tener. Es decir, cmo podemos acceder a ese elemento y desde dnde podemos accederlo. El mbito de un elemento de cdigo est restringido por el "sitio" en el que lo hemos declarado. Estos sitios pueden ser: Im#ito de #lo&ue3 Disponible nicamente en el bloque de cdigo en el que se ha declarado. Im#ito de procedimiento3 Disponible nicamente dentro del procedimiento en el que se ha declarado. Im#ito de mdulo3 Disponible en todo el cdigo del mdulo, la clase o la estructura donde se ha declarado. Im#ito de espacio de nom#res3 Disponible en todo el cdigo del espacio de nombres.
31 Accesibilidad A los distintos elementos de nuestro cdigo (ya sean clases o miembros de las clases) podemos darle diferentes tipos de accesibilidad. Estos tipos de "acceso" dependern del mbito que queramos que tengan, es decir, desde dnde podremos accederlos. Los modificadores de accesibilidad son: 8u#lic3 Acceso no restringido. 8rotected3 Acceso limitado a la clase contenedora o a los tipos derivados de esta clase. Friend3 Acceso limitado al proyecto actual. 8rotected Friend3 Acceso limitado al proyecto actual o a los tipos derivados de la clase contenedora. 8rivate3 Acceso limitado al tipo contenedor. Por ejemplo, podemos declarar miembros privados a una clase, en ese caso, dichos miembros solamente los podremos acceder desde la propia clase, pero no desde fuera de ella.
Nota3 Al igual que ocurre en VB6, al declarar una variable con Dim, por regla general, el mbito que le estamos aplicando es privado, pero como veremos, en Visual Basic 2005, dependiendo del tipo en el que declaremos esa variable, el mbito puede ser diferente a privado.
Miembros compartidos Por otro lado, los miembros compartidos de una clase o tipo, son elementos que no pertenecen a una instancia o copia en memoria particular, sino que pertenecen al propio tipo y por tanto siempre estn accesibles o disponibles, dentro del nivel del mbito y accesibilidad que les hayamos aplicado, y su tiempo de ida es el mismo que el de la aplicacin.
Nota3 Lo ms parecido en VB6 a los miembros compartidos de Visual Basic 2005 son los mtodos y campos declarados en un mdulo .BAS.
Del mbito, la accesibilidad y los miembros compartidos nos ocuparemos con ms detalle en una leccin posterior, donde veremos ciertas peculiaridades, como puede 32 ser la limitacin del mbito de un miembro que aparentemente tiene una accesibilidad no restringida.
,ar&metros y par&metros opcionales En Visual Basic 2005, tanto los miembros de una clase, (funciones y mtodos Sub), como las propiedades pueden recibir parmetros. Esto no es algo nuevo, ya que en VB6 podamos tener parmetros en estos tipos de miembros (o elementos) de una clase, adems se siguen soportando tanto los parmetros opcionales (Optional) como los arrays de parmetros (+aram*rray). Con los parmetros opcionales lo que conseguimos es permitir que el usuario no tenga que introducir todos los parmetros, sino solo los que realmente necesite, del resto, (los no especificados), se usar el valor predeterminado que hayamos indicado, ya que una de las "restricciones" de este tipo de parmetros con respecto a VB6 es que siempre que indiquemos un parmetro opcional, debemos indicar tambin el valor por defecto que debemos usar en caso de que no se especifique. Esto realmente sigue funcionando igual que en VB6 cuando al parmetro opcional le indicamos el tipo de datos. Lo que Visual Basic 2005 no permite es definir un parmetro opcional de tipo #ariant, entre otras cosas, porque ese tipo de datos no existe en VB2005. Veamos unos ejemplo para aclarar nuestras ideas: )!nction S!ma(n1 As Integer, 02tional n2 As Integer = 15) As Integer S!ma = n1 B n2 n% )!nction En este primer ejemplo, el primer parmetro es obligatorio (siempre debemos indicarlo) y el segundo es opcional, si no se indica al llamar a esta funcin, se usar el valor 15 que es el predeterminado. Para llamar a esta funcin, lo podemos hacer de estas tres formas: 4 1C in%ican%o los %os 2ar6metros (el res!lta%o ser6 .= 1 B 3) t = S!ma(1, 3) 4 2C In%ican%o solamente el 2rimer 2ar6metro (el res!lta%o ser6 16= 1 B 15) t = S!ma(1) 4 3C In%ican%o los %os 2ar6metros, 2ero en el o2cional !samos el nombre 33 t = S!ma(1, n2D= E) El tercer ejemplo solamente tiene utilidad si hay ms de un parmetro opcional.
Nota3 Los parmetros opcionales deben aparecer en la lista de parmetros del mtodo, despus de los "obligatorios"
Array de par&metros opcionales (,aramArray) En cuanto al uso de +aram*rray, este tipo de parmetro opcional nos permite indicar un nmero indeterminado de parmetros, aunque el funcionamiento con respecto a VB6 es algo diferente, en Visual Basic 2005 siempre debe ser una array de un tipo especfico e internamente se trata como un array normal y corriente, es decir, dentro del mtodo o propiedad se accede a l como si de un array se tratara... entre otras cosas, porque es un array! En VB6 no siempre era as y algunas veces tenamos verdaderos problemas al acceder a esos datos, por suerte, en Visual Basic 2005 el uso de los parmetros opcionales con +aram*rray es ms simple e intuitivo, aunque a algunos puede que le parezca menos "potente", ya que en VB6, cualquiera de los parmetros poda ser a su vez un array. Pero en Visual Basic 2005, al menos si tenemos activado Option Strict, esto no es posible, ya que solo aceptar los parmetros del tipo indicado. Veamos lo que VB6 permite y cmo hacerlo en VB2005: )!nction S!ma(?aramArra- n() As $ariant) As 'ong Dim total As 'ong Dim i As 'ong Dim # As 'ong 4 )or i = 0 1o F,o!n%(n) I8 IsArra-(n(i)) 1hen )or # = ',o!n%(n(i)) 1o F,o!n%(n(i)) total = total B n(i)(#) AeGt 34 lse total = total B n(i) n% I8 AeGt S!ma = total n% )!nction 4 ?ara !sarloD Dim t As 'ong Dim a(2) As 'ong a(0) = 1D a(1) = 2D a(2) = 3 t = S!ma(a, ., 5, 6) Hsg,oG(t)
Para usar este cdigo en Visual Basic 2005 tendremos que desactivar Option Strict y cambiar el tipo #ariant por Object, (cosa que el IDE har automticamente si pegamos el cdigo), lo dems se puede quedar igual, incluso los tipos de datos, aunque debemos recordar que un Long de VB6 es un Integer de VB2005. Pero como no debemos "acostumbrarnos" a desactivar Option Strict, vamos a ver el cdigo "bueno" de Visual Basic 2005, aunque no nos permita usar un array en los parmetros al llamar a la funcin. )!nction S!ma(,-$al ?aramArra- n() As Integer) As Integer Dim total As Integer )or i As Integer = 0 1o n/'ength C 1 total B= CInt(n(i)) AeGt "et!rn total n% )!nction 35 4 ?ara !sarloD Dim t As Integer = S!ma(1, 2, 3, ., 5, 6) 4 Hsg,oG(t) Aqu vemos el nuevo "look & feel" de Visual Basic 2005: - Usamos 'eturn para devolver el valor en lugar de usar el nombre de la funcin, aunque se sigue soportando. - Podemos inicializar la variable a usar con el bucle )or, esto lo veremos con ms detalle cuando tratemos la accesibilidad. - Usamos propiedades para saber la cantidad de elementos del array. - Comprobamos que en Visual Basic 2005 tenemos otra forma de incrementar el contenido de una variable: t B= CInt(n(i)) es lo mismo que t = t B CInt(n(i)), que aunque parezca un snobismo, realmente ahorra algn ciclo de reloj.
Nota3 Cuando queramos usar ParamArray para recibir un array de parmetros opcionales, esta instruccin debe ser la ltima de la lista de parmetros de la funcin (mtodo). Tampoco se permite tener parmetros opcionales y ParamArray en la misma funcin.
Sobrecarga de m0todos y propiedades La sobrecarga de funciones (realmente de mtodos y propiedades), es una caracterstica que nos permite tener una misma funcin con diferentes tipos de parmetros, ya sea en nmero o en tipo. Aunque nos pueda parecer que la sobrecarga de mtodos la podemos "simular" en VB6 con los parmetros opcionales, realmente no es lo mismo. Ahora veremos porqu. Supongamos que queremos tener dos funciones (o ms) que nos permitan hacer operaciones con diferentes tipos de datos, y que, segn el tipo de datos usado, el valor que devuelva sea de ese mismo tipo. En VB6 es imposible hacer esto. Salvo que creemos dos funciones con nombres diferentes, pero en ese caso ya no estaremos usando la sobrecarga. 36 En este ejemplo, tenemos dos funciones que se llaman igual pero una recibe valores de tipo entero y la otra de tipo decimal: )!nction S!ma(n1 As Integer, n2 As Integer) As Integer "et!rn n1 B n2 n% )!nction )!nction S!ma(n1 As Do!ble, n2 As Do!ble) As Do!ble "et!rn n1 B n2 n% )!nction Como podemos comprobar las dos funciones tienen el mismo nombre, pero tanto una como otra reciben parmetros de tipos diferentes. Con Visual Basic 2005 podemos sobrecargar funciones, pero lo interesante no es que podamos hacerlo, sino cmo podemos usar esas funciones. En el cdigo anterior tenemos dos funciones llamadas Suma, la primera acepta dos parmetros de tipo Integer y la segunda de tipo Double. Lo interesante es que cuando queramos usarlas, no tenemos que preocuparnos de cual vamos a usar, ya que ser el compilador el que decida la ms adecuada al cdigo que usemos, por ejemplo: 4 n este caso, se !sar6 la I!e recibe %os 5alores enteros Hsg,oG( S!ma(10, 22) ) 4 n este caso se !sar6 la I!e recibe 5alores %e ti2o Do!ble Hsg,oG( S!ma(10/5, 22) El compilador de Visual Basic 2005 es el que decide que funcin usar, esa decisin la toma a partir de los tipos de parmetros que hayamos indicado. En el segundo ejemplo de uso, el que mejor coincide es el de los dos parmetros de tipo Double. Tambin podemos tener sobrecarga usando una cantidad diferente de parmetros, aunque stos sean del mismo tipo. Por ejemplo, podemos aadir esta declaracin al cdigo anterior sin que exista ningn tipo de error, ya que esta nueva funcin recibe tres parmetros en lugar de dos: )!nction S!ma(n1 As Integer, n2 As Integer, n3 As Integer) As Integer "et!rn n1 B n2 B n3 n% )!nction 37 Por tanto, cuando el compilador se encuentre con una llamada a la funcin Suma en la que se usen tres parmetros, intentar usar esta ltima.
Nota3 Para que exista sobrecarga, la diferencia debe estar en el nmero o en el tipo de los parmetros, no en el tipo del valor devuelto.
Cuando, desde el IDE de Visual Basic 2005, queremos usar los mtodos sobrecargados, nos mostrar una lista de opciones indicndonos las posibilidades de sobrecarga que existen y entre las que podemos elegir, tal como vemos en la figura 1.10:
Fi'ura $.$0. *ista de par.metros soportados por un mHtodo
Tal como comentbamos hace unas lneas, en VB6 se puede simular la sobrecarga mediante los parmetros opcionales, aunque realmente no es lo mismo. Pero esa "simulacin" nos viene como anillo al dedo para dar un consejo o advertencia: J-uidado con las funciones &ue reci#en par.metros opcionalesC %a &ue el compilador puede producir un error si esa funcin entra en conflicto con otra Aso#recar'adaAK Mejor lo vemos con un ejemplo: )!nction S!ma(n1 As Integer, 02tional n2 As Integer = 33) As Integer "et!rn n1 B n2 n% )!nction Si tenemos esta declaracin adems de las anteriores, el programa no compilar, ya que si hacemos una llamada a la funcin Suma con dos parmetros enteros, el compilador no sabr si usar esta ltima o la primera que declaramos, por tanto producir un error. 38
,ar&metros por valor y par&metros por referencia Al igual que tenemos dos tipos de datos diferentes, en los parmetros de las funciones tambin podemos tenerlos, esto tampoco es una novedad de Visual Basic 2005, ya que en VB6 tambin podemos usar By#al o By'ef para indicar al compilador cmo debe tratar a los parmetros. Cuando un parmetro es por valor (By#al), el runtime antes de llamar a la funcin hace una copia de ese parmetro y pasa la copia a la funcin, por tanto cualquier cambio que hagamos a ese parmetro dentro de la funcin no afectar al valor usado "externamente". En el caso de que el parmetro sea por referencia (By'ef), el compilador pasa una referencia que apunta a la direccin de memoria en la que estn los datos, por tanto si realizamos cambios dentro de la funcin, ese cambio si que se ver reflejado en el parmetro usado al llamar a la funcin.
Nota3 Hay que tener en cuenta que si pasamos un objeto a una funcin, da igual que lo declaremos por valor o por referencia, ya que en ambos casos se pasa una referencia a la direccin de memoria en la que estn los datos, porque, como sabemos, las variables de los tipos por referencia siempre contienen una referencia a los datos, no los datos en s.
Hasta aqu todo como en VB6. Lo que cambia en Visual Basic 2005 es que ahora los parmetros en los que no se indique si es por valor (By#al) o por referencia (By'ef), sern tratados como parmetros por valor. En VB6 era al revs.
Nota3 Si usamos el IDE de Visual Basic 2005 Express para escribir el cdigo, no debemos preocuparnos de este detalle, ya que si no indicamos si es por valor o por referencia, automticamente le aadir la palabra clave ByVal, para que no haya ningn tipo de dudas.
39 Instanciar3 -rear un o#(eto en la memoria Una vez que tenemos una clase definida, lo nico de lo que disponemos es de una especie de plantilla o molde a partir del cual podemos crear objetos en memoria. La forma de crear esos objetos en Visual Basic 2005 no ha cambiado con respecto al VB6, salvo en pequeos detalles, veamos algunos ejemplos y as aclararemos esas diferencias, que por otro lado son importantes. Lo primero que tenemos que hacer es declarar una variable del tipo que queremos instanciar, esto lo hacemos usando la instruccin Dim: Dim c As Cliente Con esta lnea de cdigo lo que estamos indicando al Visual Basic es que tenemos intencin de usar una variable llamada c para acceder a una clase de tipo -liente. Esa variable, cuando llegue el momento de usarla, sabr todo lo que hay que saber sobre una clase Cliente, pero hasta que no tenga una "referencia" a un objeto de ese tipo no podremos usarla. La asignacin de una referencia a un objeto -liente podemos hacerla de dos formas distintas: La primera es creando un nuevo objeto en la memoria: c = AeJ Cliente
Nota3 En VB6 esta forma de crear un nuevo objeto producira un error, ya que para "asignar" a una variable un nuevo objeto tenemos que usar la instruccin Set, en Visual Basic 2005 ya no es necesario el uso de esa instruccin, de hecho si la usamos, recibiremos un error.
A partir de este momento, la variable c tiene acceso a un nuevo objeto del tipo -liente, por tanto podremos usarla para asignarle valores y usar cualquiera de los miembros que ese tipo de datos contenga: c/Aombre = "Antonio" c/A2elli%os = ""!i& "o%rKg!e&"
40 "eclarar y asignar en un solo paso Con las clases o tipos por referencia tambin podemos declarar una variable y al mismo tiempo asignarle un nuevo objeto, esto tampoco es una novedad, aunque s lo es la forma de comportarse. Veamos primero cmo hacerlo y despus entraremos en detalles. Dim c As AeJ Cliente O tambin: Dim c As Cliente = AeJ Cliente Las dos formas producen el mismo resultado, por tanto es recomendable usar la primera. En VB6 este tipo de declaracin y asignacin no era recomendable, ya que aade trabajo extra al compilador de VB. Ese trabajo extra consiste en comprobar si el objeto al que queremos acceder ya est creado, en caso de que no sea as, primero lo crea y despus lo usa, el inconveniente es que todas estas comprobaciones las realiza cada vez que usemos la variable! En Visual Basic 2005 este comportamiento ya no es as, el objeto se crea en la memoria y se asigna a la variable, a partir de ese momento no se comprueba si ya est creado o no, ya que se supone que s est creado. Lo que debemos tener muy presente es que adems de la sobrecarga de trabajo que aade VB6 a este tipo de instanciacin, el comportamiento es muy diferente al de Visual Basic 2005. Por ejemplo, si en VB6 asignamos un valor nulo a una variable declarada de esta forma y acto seguido usamos la variable, el motor en tiempo de ejecucin (runtime) crea un nuevo objeto y asunto arreglado. Pero en Visual Basic 2005, si asignamos un valor nulo a una variable, le estamos diciendo al CLR (el runtime de .NET) que ya no queremos usar ms ese objeto, por tanto lo elimina de la memoria; si despus queremos volver a usar la variable, debemos crear otro objeto, (con $e!), de no hacerlo, se producir un error, ya que en Visual Basic 2005 no existe la auto-instanciacin.
El constructor: El punto de inicio de una clase Cada vez que creamos un nuevo objeto en memoria estamos llamando al constructor de la clase. En VB6 el constructor es el mtodo Class-Initiali.e. Sin embargo en Visual Basic 2005 el constructor es un mtodo de tipo Sub llamado $e!. En el constructor de una clase podemos incluir el cdigo que creamos conveniente, pero realmente solamente deberamos incluir el que realice algn tipo de inicializacin, en caso de que no necesitemos realizar ningn tipo de inicializacin, 41 no es necesario definir el constructor, ya que el propio compilador lo har por nosotros. Esto es as porque todas las clases deben implementar un constructor, por tanto si nosotros no lo definimos, lo har el compilador de Visual Basic 2005. Si nuestra clase -liente tiene un campo para almacenar la fecha de creacin del objeto podemos hacer algo como esto: Class Cliente ?!blic Aombre As String ?!blic A2elli%os As String ?!blic )echaCreacion As Date 4 ?!blic S!b AeJ() )echaCreacion = Date/AoJ n% S!b n% Class
De esta forma podemos crear un nuevo -liente y acto seguido comprobar el valor del campo Fec?a-reacion para saber la fecha de creacin del objeto. En los constructores tambin podemos hacer las inicializaciones que, por ejemplo permitan a la clase a conectarse con una base de datos, abrir un fichero o cargar una imagen grfica, etc., aunque en algunos de estos casos nos facilitar la tarea una nueva caracterstica que VB6 no tiene.
Constructores parametri*ados De la misma forma que podemos tener mtodos y propiedades sobrecargadas, tambin podemos tener constructores sobrecargados, ya que debemos recordar que en Visual Basic 2005, un constructor realmente es un mtodo de tipo Sub, y como todos los mtodos y propiedades de Visual Basic 2005, tambin admite la sobrecarga. La ventaja de tener constructores que admitan parmetros es que podemos crear nuevos objetos indicando algn parmetro, por ejemplo un fichero a abrir o, en el caso de la clase -liente, podemos indicar el nombre y apellidos del cliente o cualquier otro dato que creamos conveniente. 42 Para comprobarlo, podemos ampliar la clase definida anteriormente para que tambin acepte la creacin de nuevos objetos indicando el nombre y los apellidos del cliente. Class Cliente ?!blic Aombre As String ?!blic A2elli%os As String ?!blic )echaCreacion As Date 4 ?!blic S!b AeJ() )echaCreacion = Date/AoJ n% S!b 4 ?!blic S!b AeJ(elAombre As String, losA2elli%os As String) Aombre = elAombre A2elli%os = losA2elli%os )echaCreacion = Date/AoJ n% S!b n% Class Teniendo esta declaracin de la clase -liente, podemos crear nuevos clientes de dos formas: Dim c1 As AeJ Cliente Dim c2 As AeJ Cliente("@ose", "S6nche&") Como podemos comprobar, en ciertos casos es ms intuitiva la segunda forma de crear objetos del tipo -liente, adems de que as nos ahorramos de tener que asignar individualmente los campos Nom#re y 4pellidos. Esta declaracin de la clase Cliente la podramos haber hecho de una forma diferente. Por un lado tenemos un constructor "normal" (no recibe parmetros) en el que asignamos la fecha de creacin y por otro el constructor que recibe los datos del nombre y apellidos. En ese segundo constructor tambin asignamos la fecha de creacin, ya que, se instancie como se instancie la clase, nos interesa saber siempre la fecha de creacin. En este ejemplo, por su simpleza no es realmente un problema repetir la asignacin de la fecha, pero si en lugar de una inicializacin necesitramos 43 hacer varias, la verdad es que nos encontraramos con mucha "duplicidad" de cdigo. Por tanto, en lugar de asignar los datos en dos lugares diferentes, podemos hacer esto otro: Class Cliente ?!blic Aombre As String ?!blic A2elli%os As String ?!blic )echaCreacion As Date 4 ?!blic S!b AeJ() )echaCreacion = Date/AoJ n% S!b 4 ?!blic S!b AeJ(elAombre As String, losA2elli%os As String) AeJ() Aombre = elAombre A2elli%os = losA2elli%os n% S!b n% Class Es decir, desde el constructor con argumentos llamamos al constructor que no los tiene, consiguiendo que tambin se asigne la fecha. Esta declaracin de la clase -liente realmente no compilar. Y no compila por la razn tan "simple" de que aqu el compilador de Visual Basic 2005 no sabe cual es nuestra intencin, ya que $e! es una palabra reservada que sirve para crear nuevos objetos y, siempre, una instruccin tiene preferencia sobre el nombre de un mtodo, por tanto, podemos recurrir al objeto especial Me el cual nos sirve, al igual que en VB6, para representar al objeto que actualmente est en la memoria, as que, para que esa declaracin de la clase Cliente funcione, debemos usar (e.$e!/0 para llamar al constructor sin parmetros.
Nota3 El IDE de Visual Basic 2005 colorea las instrucciones y tipos propios del lenguaje, tal y como lo hace el de VB6, en el caso de Me, VB6 no la colorea, pero este no es el motivo de esta nota, lo que no debe confundirnos es que cuando declaramos Sub New, tanto Sub como 44 New se muestran coloreadas, cuando solo se debera colorear Sub; sin embargo cuando usamos Me.New, solo se colorea Me y no New que es correcto, tal como vemos en la figura 1.11, ya que en este caso New es el nombre de un procedimiento y los procedimientos no son parte de las instrucciones y tipos de .NET.
Fi'ura $.$$. -oloreo errneo de Ne
Cuando Visual .asic 1223 no crea un constructor autom&ticamente Tal como hemos comentado, si nosotros no definimos un constructor (Sub $e!), lo har el propio compilador de Visual Basic 2005, y cuando lo hace automticamente, siempre es un constructor sin parmetros. Pero hay ocasiones en las que nos puede interesar que no exista un constructor sin parmetros, por ejemplo, podemos crear una clase -liente que solo se pueda instanciar si le pasamos, por ejemplo el nmero de identificacin fiscal, (NIF), en caso de que no se indique ese dato, no podremos crear un nuevo objeto Cliente, de esta forma, nos aseguramos siempre de que el NIF siempre est especificado. Seguramente por ese motivo, si nosotros definimos un constructor con parmetros, Visual Basic 2005 no crea uno automticamente sin parmetros. Por tanto, si definimos un constructor con parmetros en una clase y queremos que tambin tenga uno sin parmetros, lo tenemos que definir nosotros mismos.
El destructor: El punto final de la vida de una clase De la misma forma que una clase tiene su punto de entrada o momento de nacimiento en el constructor, tambin tienen un sitio que se ejecutar cuando el 45 objeto creado en la memoria ya no sea necesario, es decir, cuando acabe la vida del objeto creado. El destructor de Visual Basic 2005 es un mtodo llamado )inali.e, el cual se hereda de la clase Object, por tanto no es necesario que escribamos nada en l. El propio CLR se encargar de llamar a ese mtodo cuando el objeto ya no sea necesario. En VB6, el destructor es el mtodo Class-Terminate. La principal diferencia con VB6 en lo referente a la forma en que se destruyen los objetos, es que en VB6 si a un objeto le asignamos un valor nulo ($ot"ing) el destructor se ejecuta inmediatamente y el objeto se elimina de la memoria, pero en .NET la forma en que se destruyen los objetos es diferente, nunca podremos tener la certeza de cuando se destruye, incluso si le asignamos un valor nulo. Esto es as debido a que en .NET los objetos no se destruyen inmediatamente y existe un "sistema" que se encarga de realizar esta gestin de limpieza: El recolector de basura o de objetos no usados (1arbage Collector, GC). Este recolector de objetos no usados se encarga de comprobar constantemente cuando un objeto no se est usando y es el que decide cuando hay que llamar al destructor. Debido a esta caracterstica de .NET, si nuestra clase hace uso de recursos externos que necesiten ser eliminados cuando la el objeto ya no se vaya a seguir usando, debemos definir un mtodo que sea el encargado de realizar esa liberacin, pero ese mtodo debemos llamarlo de forma manual, ya que, aunque en .NET existen formas de hacer que esa llamada sea automtica, nunca tenderemos la seguridad de que se llame en el momento oportuno, y esto es algo que, segn que casos, puede ser un inconveniente.
Becomendacin3 Si nuestra clase utiliza recursos externos, por ejemplo un fichero o una base de datos, debemos definir un mtodo que se encargue de liberarlos y a ese mtodo debemos encargarnos de llamarlo cuando ya no lo necesitemos. Por definicin a este tipo de mtodos se les suele dar el nombre Close o Dispose, aunque este ltimo tiene un significado especial y por convencin solo debemos usarlo siguiendo las indicaciones de la documentacin.
Estructuras3 Tipos por valor definidos por el usuario De la misma forma que podemos definir nuestros propios tipos de datos por referencia, Visual Basic 2005 nos permite crear nuestros propios tipos por valor. Para crear nuestros tipos de datos por referencia, usamos la "instruccin" Class, por tanto es de esperar que tambin exista una instruccin para crear nuestros tipos por valor, y esa instruccin es: Structure, por eso en Visual Basic 2005 a los tipos por valor definidos por el usuario se llaman estructuras. 46
Nota3 El equivalente en VB6 a una estructura de Visual Basic 2005 es Type, aunque es solo eso: un parecido, pero realmente no es equivalente, al menos al 100%, tal como tendremos oportunidad de comprobar en esta leccin.
Las estructuras pueden contener los mismos miembros que las clases, aunque algunos de ellos se comporten de forma diferente o al menos tengan algunas restricciones, como que los campos definidos en las estructuras no se pueden inicializar al mismo tiempo que se declaran o no pueden contener constructores "simples", ya que el propio compilador siempre se encarga de crearlo, para as poder inicializar todos los campos definidos. Otra de las caractersticas de las estructuras es que no es necesario crear una instancia para poder usarlas, ya que es un tipo por valor y los tipos por valor no necesitan ser instanciados para que existan. "efinir una estructura Las estructuras se definen usando la palabra Structure seguida del nombre y acaba usando las instrucciones %nd Structure. El siguiente cdigo define una estructura llamada Punto en la que tenemos dos campos pblicos. Str!ct!re ?!nto ?!blic L As Integer ?!blic M As Integer n% Str!ct!re Para usarla podemos hacer algo como esto: Dim 2 As ?!nto 2/L = 100 2/M = N5 Tambin podemos usar New al declarar el objeto: Dim 2 As AeJ ?!nto 47 Aunque en las estructuras, usar New, sera algo redundante y por tanto no necesario.
Las estructuras siempre se almacenan en la pila, por tanto deberamos tener la precaucin de no crear estructuras con muchos campos o con muchos miembros, ya que esto implicara un mayor consumo del "preciado" espacio de la pila.
Constructores de las estructuras Tal y como hemos comentado, las estructuras siempre definen un constructor sin parmetros, este constructor no lo podemos definir nosotros, es decir, siempre existe y es el que el propio compilador de Visual Basic 2005 define. Por tanto, si queremos agregar algn constructor a una estructura, este debe tener parmetros y, tal como ocurre con cualquier mtodo o como ocurre en las clases, podemos tener varias sobrecargas de constructores parametrizados en las estructuras. La forma de definir esos constructores es como vimos en las clases: usando distintas sobrecargas de un mtodo llamado $e!, en el caso de las estructuras tambin podemos usar la palabra clave (e para referirnos a la instancia actual. Esto es particularmente prctico cuando los parmetros del constructor se llaman de la misma forma que los campos declarados en la estructura, tal como ocurre en el constructor mostrado en la siguiente definicin de la estructura Punto. Str!ct!re ?!nto ?!blic L As Integer ?!blic M As Integer 4 S!b AeJ(,-$al G As Integer, ,-$al - As Integer) He/L = G He/M = - n% S!b n% Str!ct!re
48 Nota3 Tanto en las estructuras como en las clases podemos tener constructores compartidos, en el caso de las estructuras, este tipo de constructor es el nico que podemos declarar sin parmetros.
"estructores de las estructuras Debido a que las estructuras son tipos por valor y por tanto una variable declarada con un tipo por valor "contiene el valor en si misma", no podemos destruir este tipo de datos, lo ms que conseguiramos al asignarle un valor nulo ($ot"ing) sera eliminar el contenido de la variable, pero nunca podemos destruir ese valor. Por tanto, en las estructuras no podemos definir destructores.
'os miembros de una estructura Como hemos comentado, los miembros o elementos que podemos definir en una estructura son los mismos que ya vimos en las clases. Por tanto aqu veremos las diferencias que existen al usarlos en las estructuras. Campos Como vimos, las variables declaradas a nivel del tipo, son los campos, la principal diferencia con respecto a las clases, es que los campos de una estructura no pueden inicialiarse en la declaracin y el valor que tendrn inicialmente es un valor "nulo", que en el caso de los campos de tipo numricos es un cero. Por tanto, si necesitamos que los campos tengan algn valor inicial antes de usarlos, deberamos indicarlo a los usuarios de nuestra estructura y proveer un constructor que realice las inicializaciones correspondientes, pero debemos recordar que ese constructor debe tener algn parmetro, ya que el predeterminado sin parmetros no podemos "reescribirlo". Los nicos campos que podemos inicializar al declararlos son los campos compartidos, pero como tendremos oportunidad de ver, estos campos sern accesibles por cualquier variable declarada y cualquier cambio que realicemos en ellos se ver reflejado en el resto de "instancias" de nuestro tipo. Mtodos y otros elementos El resto de los miembros de una estructura se declaran y usan de la misma forma que en las clases, si bien debemos tener en cuenta que el modificador de 49 accesibilidad predeterminado para los miembros de una estructura es +ublic, (incluso si son campos declarados con D2m). Otro detalle a tener en cuenta es que en una estructura siempre debe existir al menos un evento o un campo no compartido, no se permiten estructuras en las que solo tienen constantes, mtodos y/o propiedades, estn o no compartidos.
C(mo usar las estructuras Tal como hemos comentado las estructuras son tipos por valor, para usar los tipos por valor no es necesario instanciarlos explcitamente, ya que el mero hecho de declararlos indica que estamos creando un nuevo objeto en memoria. Por tanto, a diferencia de las clases o tipos por referencia, cada variable definida como un tipo de estructura ser independiente de otras variables declaradas, aunque no las hayamos instanciado. Esta caracterstica de las estructuras nos permite hacer copias "reales" no copia de la referencia (o puntero) al objeto en memoria como ocurre con los tipos por referencia. Vemoslos con un ejemplo. Dim 2 As AeJ ?!nto(100, N5) Dim 21 As ?!nto 21 = 2 21/L = 200 4 2/L 5ale 100 - 21/L 5ale 200 En este trozo de cdigo definimos e instanciamos una variable del tipo 8unto, a continuacin declaramos otra variable del mismo tipo y le asignamos la primera, si estos tipos fuesen por referencia, tanto una como la otra estaran haciendo referencia al mismo objeto en memoria, y cualquier cambio realizado a cualquiera de las dos variables afectaran al mismo objeto, pero en el caso de las estructuras (y de los tipos por valor), cada cambio que realicemos se har sobre un objeto diferente, por tanto la asignacin del valor 200 al campo L de la variable p$ solo afecta a esa variable, dejando intacto el valor original de la variable p.
4ccesi#ilidad % .m#ito Tal y como comentamos anteriormente, dependiendo de dnde y cmo estn declarados los tipos de datos y los miembros definidos en ellos, tendremos o no acceso a esos elementos. 50 Recordemos que el mbito es el alcance con el que podemos acceder a un elemento y depende de dnde est declarado, por otro lado, la accesibilidad depende de cmo declaremos cada uno de esos elementos.
4mbito Dependiendo de donde declaremos un miembro o un tipo, ste tendr mayor alcance o cobertura, o lo que es lo mismo, dependiendo del mbito en el que usemos un elemento, podremos acceder a l desde otros puntos de nuestro cdigo. A continuacin veremos con detalle los mbitos en los que podemos declarar los distintos elementos de Visual Basic 2005. Im#ito de #lo&ue3 Disponible nicamente en el bloque de cdigo en el que se ha declarado. Por ejemplo, si declaramos una variable dentro de un bucle )or o un If T"en, esa variable solo estar accesible dentro de ese bloque de cdigo. Im#ito de procedimiento3 Disponible nicamente dentro del procedimiento en el que se ha declarado. Cualquier variable declarada dentro de un procedimiento (mtodo o propiedad) solo estar accesible en ese procedimiento y en cualquiera de los bloques internos a ese procedimiento. Im#ito de mdulo3 Disponible en todo el cdigo del mdulo, la clase o la estructura donde se ha declarado. Las variables con mbito a nivel de mdulo, tambin estarn disponibles en los procedimientos declarados en el mdulo (clase o estructura) y por extensin a cualquier bloque dentro de cada procedimiento. Im#ito de espacio de nom#res3 Disponible en todo el cdigo del espacio de nombres. Este es el nivel mayor de cobertura o alcance, aunque en este nivel solo podemos declarar tipos como clases, estructuras y enumeraciones, ya que los procedimientos solamente se pueden declarar dentro de un tipo.
Nota3 Por regla general, cuando declaramos una variable en un mbito, dicha variable "ocultar" a otra que tenga el mismo nombre y est definida en un bloque con mayor alcance, aunque veremos que en Visual Basic 2005 existen ciertas restricciones dependiendo de dnde declaremos esas variables.
En VB6 los mbitos disponibles solamente eran los de mdulo y procedimiento, en estos casos, cualquier variable declarada en un procedimiento oculta a una definida en un mdulo. En Visual Basic 2005 podemos definir una variable dentro de un bloque de cdigo, en ese caso dicha variable solo ser accesible dentro de ese bloque. Aunque, como veremos a continuacin, en un procedimiento solamente podremos definir variables que no se oculten entre s, estn o no dentro de un bloque de cdigo. 51
mbito de bloque En los siguientes ejemplos veremos cmo podemos definir variables para usar solamente en el bloque en el que estn definidas. Los bloques de cdigo en los que podemos declarar variables son los bucles, ()or3 Do3 4"ile), y los bloques condicionales, (If3 Select). Por ejemplo, dentro de un procedimiento podemos tener varios de estos bloques y por tanto podemos definir variables "internas" a esos bloques: Dim n As Integer = 3 4 )or i As Integer = 1 1o 10 Dim # As Integer # B= 1 I8 # ( n 1hen 4/// n% I8 AeGt 4 I8 n ( 5 1hen Dim # As Integer = n O 3 n% I8 4 Do Dim # As Integer )or i As Integer = 1 1o n # B= i AeGt I8 # * 10 1hen Git Do 'oo2 52 La variable n estar disponible en todo el procedimiento, por tanto podemos acceder a ella desde cualquiera de los bloques. En el primer bucle )or, definimos la variable i como la variable a usar de contador, esta variable solamente estar accesible dentro de este bucle )or. Lo mismo ocurre con la variable (. En el primer If definimos otra variable (, pero esa solo ser accesible dentro de este bloque If y por tanto no tiene ninguna relacin con la definida en el bucle )or anterior. En el bucle Do volvemos a definir nuevamente una variable (, a esa variable la podemos acceder solo desde el propio bucle Do y cualquier otro bloque de cdigo interno, como es el caso del bucle )or, en el que nuevamente declaramos una variable llamada i, que nada tiene que ver con el resto de variables declaradas con el mismo nombre en los otros bloques. Lo nico que no podemos hacer en cualquiera de esos bloques, es declarar una variable llamada n, ya que al estar declarada en el procedimiento, el compilador de Visual Basic 2005 nos indicar que no podemos ocultar una variable previamente definida fuera del bloque, tal como podemos ver en la figura 1.12. Fi'ura $.$2. Error al ocultar una varia#le definida en un procedimiento Esta restriccin solo es aplicable a las variables declaradas en el procedimiento, ya que si declaramos una variable a nivel de mdulo, no habr ningn problema para usarla dentro de un bloque, esto es as porque en un procedimiento podemos declarar variables que se llamen de la misma forma que las declaradas a nivel de mdulo, aunque stas ocultarn a las del "nivel" superior.
mbito de procedimiento Las variables declaradas en un procedimiento tendrn un mbito o cobertura que ser el procedimiento en el que est declaradas, y como hemos visto, ese mbito incluye tambin cualquier bloque de cdigo declarado dentro del procedimiento. Estas variables ocultarn a las que se hayan declarado fuera del procedimiento, si bien, dependiendo del tipo de mdulo, podremos acceder a esas variables "externas" indicando el nombre completo del mdulo o bien usando la instruccin (e, tal como vimos en el cdigo del constructor parametrizado de la estructura 8unto. Pero mejor vemoslo con un ejemplo. En el siguiente cdigo, definimos una clase en la que tenemos un campo llamado Nom#re, tambin definimos un mtodo en el 53 que internamente se utiliza una variable llamada nombre, para acceder a la variable declarada en la clase, tendremos que usar la instruccin o palabra clave (e. ?!blic Class Cliente ?!blic Aombre As String = "@!an" )!nction Hostrar() As String Dim nombre As String = "?e2ita" "et!rn "Gterno= " P He/Aombre P ", interno= " P nombre n% )!nction n% Class En este ejemplo, el hecho de que una variable est declarada con la letra ene en mayscula o en minscula no implica ninguna diferencia, ya que Visual Basic 2005 al igual que VB6 no hace distinciones de este tipo, si bien, en Visual Basic 2005 no se cambia automticamente el "case" de las variables, salvo cuando estn en el mismo nivel de mbito; en cambio, si en VB6 declaramos una variable con el mismo nombre, aunque est en un mbito diferente, siempre se usar el estado de maysculas/minsculas de la ltima definicin.
mbito de mdulo Cuando hablamos de mdulos, nos estamos refiriendo a una clase, a una estructura o a cualquier otro tipo de datos que nos permita .NET. En estos casos, las variables declaradas dentro de un tipo de datos sern visibles desde cualquier parte de ese tipo, siempre teniendo en cuenta las restricciones mencionadas en los casos anteriores.
mbito de espacio de nombres Los espacios de nombres son los contenedores de tipos de datos de mayor nivel, y sirven para contener definiciones de clases, estructuras, enumeraciones y delegados. Cualquier tipo definido a nivel de espacio de nombres estar disponible para cualquier otro elemento definido en el mismo espacio de nombres. Al igual que ocurre en el resto de mbitos "inferiores", si definimos un tipo en un espacio de nombres, podemos usar ese mismo nombre para nombrar a un procedimiento o a una variable, en cada caso se aplicar el mbito correspondiente y, tal como vimos anteriormente, tendremos que usar nombres nicos para poder acceder a los nombres definidos en niveles diferentes.
54
'a palabra clave 5lobal En Visual Basic 2005 podemos definir espacios de nombres cuyos nombres sean los mismos que los definidos en el propio .NET Framework, para evitar conflictos de mbitos, podemos usar la palabra clave 1lobal para acceder a los que se han definido de forma "global" en .NET. Por ejemplo, si tenemos el siguiente cdigo en el que definimos una clase dentro de un espacio de nombres llamado System y queremos acceder a uno de los tipos definidos en el espacio de nombres System de .NET, tendremos un problema: Aames2ace S-stem Class Cliente ?!blic Aombre As String ?!blic %a% As S-stem/Int32 n% Class n% Aames2ace El problema es que el compilador de Visual Basic 2005 nos indicar que el tipo Int32 no est definido, ya que intentar buscarlo dentro del mbito que actualmente tiene, es decir, la declaracin que nosotros hemos hecho de System, por tanto para poder acceder al tipo Int32 definido en el espacio de nombres "global" System de .NET tendremos que usar la instruccin 1lobal, por suerte el IDE de Visual Basic 2005 Express reconoce este tipo de error y nos ofrece ayuda para poder solventar el conflicto, tal como vemos en la figura 1.13: Fi'ura $.$<. 4%uda del I/E en los conflictos de espacios nom#res 'lo#ales
Nota3 Afortunadamente este conflicto con los espacios de nombres no ser muy habitual para los desarrolladores que usemos el idioma de 55 Cervantes, por la sencilla razn de que los espacios de nombres de .NET Framework suelen estar definidos usando palabras en ingls.
Accesibilidad La accesibilidad es la caracterstica que podemos aplicar a cualquiera de los elementos que definamos en nuestro cdigo. Dependiendo de la accesibilidad declarada tendremos distintos tipos de accesos a esos elementos. Los modificadores de accesibilidad que podemos aplicar a los tipos y elementos definidos en nuestro cdigo pueden ser cualquiera de los mostrados en la siguiente lista: 8u#lic3 Acceso no restringido. Este es modificador de accesibilidad con mayor "cobertura", podemos acceder a cualquier miembro pblico desde cualquier parte de nuestro cdigo. Aunque, como veremos, este acceso no restringido puede verse reducido dependiendo de dnde lo usemos. 8rotected3 Acceso limitado a la clase contenedora o a los tipos derivados de esta clase. Este modificador solamente se usa con clases que se deriven de otras. Friend3 Acceso limitado al proyecto actual. Visual Basic 2005 aplica este modificador de forma predeterminada a los procedimientos declarados en las clases. 8rotected Friend3 Acceso limitado al proyecto actual o a los tipos derivados de la clase contenedora. Una mezcla de los dos modificadores anteriores. 8rivate3 Acceso limitado al tipo contenedor. Es el ms restrictivos de todos los modificadores de accesibilidad y en el caso de los campos declarados en las clases (Class) equivale a usar Dim. Estos modificadores de accesibilidad los podemos usar tanto en clases, estructuras, interfaces, enumeraciones, delegados, eventos, mtodos, propiedades y campos. Aunque no sern aplicables en espacios de nombres ($amespace) ni clases de tipo Module, en estos dos casos siempre tendrn cobertura pblica, si bien no se permite el uso de ningn modificador.
Accesibilidad de las variables en los procedimientos Las variables declaradas dentro de un procedimiento solo son accesibles dentro de ese procedimiento, en este caso solo se puede aplicar el mbito privado, aunque no podremos usar la instruccin +riate, sino Dim o Static.
56 Nota3 La palabra clave Static, tiene el mismo significado que en VB6, y nos permite definir una variable privada (o local) al procedimiento para que mantenga el valor entre diferentes llamadas a ese procedimiento; esto contrasta con el resto de variables declaradas en un procedimiento cuya duracin es la misma que la vida del propio procedimiento, por tanto, las variables no estticas pierden el valor al terminar la ejecucin del procedimiento.
'as accesibilidades predeterminadas La accesibilidad de una variable o procedimiento en la que no hemos indicado el modificador de accesibilidad depender del sitio en el que la hemos declarado. Por ejemplo, en las estructuras si definimos los campos usando Dim, estos tendrn un mbito igual que si le hubisemos aplicado el modificador +ublic; sin embargo, esa misma variable declarada en una clase (Class o (odule) tendr una accesibilidad +riate. As mismo, si el elemento que declaramos es un procedimiento y no indicamos el modificador de mbito, ste tendr un mbito de tipo +ublic si lo definimos en una estructura y si el lugar en el que lo declaramos es una clase (o (odule), ste ser )riend. En la siguiente tabla tenemos la accesibilidad predeterminada de cada tipo (clase, estructura, etc.), as como de las variables declaradas con Dim y de los procedimientos en los que no se indican el modificador de accesibilidad. Tipo del tipo de las varia#les declaradas con /im de los procedimientos Class Friend Private Friend Module Friend Private Friend Structure Friend Public Public Enum Public Friend N.A. (los miembros siempre son pblicos) N.A. Interface Friend N.A. (no se pueden declarar variables) Public (no se permite indicarlo) Ta#la $.<. *a accesi#ilidad predeterminada de los tipos Tal como podemos ver en la tabla 1.3, la accesibilidad predeterminada, (la que tienen cuando no se indica expresamente con un modificador), de todos los tipos es 57 )riend, es decir, accesible a todo el proyecto, aunque en el caso de las enumeraciones el modificador depende de dnde se declare dicha enumeracin, si est declarada a nivel de espacio de nombres ser )riend, en el resto de los casos ser +ublic. En la tercera columna tenemos la accesibilidad predeterminada cuando declaramos las variables con Dim, aunque en las interfaces y en las enumeraciones no se permiten declarar variables. La ltima columna es la correspondiente a los procedimientos, en el caso de las interfaces no se puede aplicar ningn modificador de accesibilidad y de forma predeterminada son pblicos.
En esta otra tabla tenemos la accesibilidad permitida en cada tipo as como las que podemos indicar en los miembros de esos tipos. Tipo del tipo de los miem#ros Class Public Friend Private Protected Protected Friend Public Friend Private Protected Protected Friend Module Public Friend Public Friend Private Structure Public Friend Private Public Friend Private Enum Public Friend Private N.A. Interface Public Friend Private Protected Protected Friend N.A. Siempre son pblicos Ta#la $.=. 4ccesi#ilidades permitidas en los tipos Algunos de los modificadores que podemos indicar en los tipos dependen de dnde declaremos esos tipos, por ejemplo, tan solo podremos indicar el modificador privado de las enumeraciones cuando estas se declaren dentro de un tipo. En el caso de las clases e interfaces, los modificadores +rotected y +rotected )riend solo podremos aplicarlos cuando estn declaradas dentro de una clase (Class).
58 Anidaci(n de tipos Tal como hemos comentado en el prrafo anterior, podemos declarar tipos dentro de otros tipos, por tanto el mbito y accesibilidad de esos tipos dependen del mbito y accesibilidad del tipo que los contiene. Por ejemplo, si declaramos una clase con acceso )riend, cualquier tipo que esta clase contenga siempre estar supeditado al mbito de esa clase, por tanto si declaramos otro tipo interno, aunque lo declaremos como +ublic, nunca estar ms accesible que la clase contenedora, aunque en estos casos no habr ningn tipo de confusin, ya que para acceder a los tipos declarados dentro de otros tipos siempre tendremos que indicar la clase que los contiene. En el siguiente cdigo podemos ver cmo declarar dos clases "anidadas". Tal como podemos comprobar, para acceder a la clase Salario debemos indicar la clase Cliente, ya que la nica forma de acceder a una clase anidada es mediante la clase contenedora. )rien% Class Cliente ?!blic Aombre As String ?!blic Class Salario ?!blic Im2orte As Decimal n% Class n% Class 4 ?ara !sar la clase Salario %ebemos %eclararla %e esta 8ormaD Dim s As AeJ Cliente/Salario s/Im2orte = 2200
Los tipos anidables Cualquiera de los tipos mostrados en la tabla 1.4, excepto las enumeraciones, pueden contener a su vez otros tipos. La excepcin es el tipo (odule que aunque puede contener a otros tipos, no puede usarse como tipo anidado. Una enumeracin siempre puede usarse como tipo anidado.
Nota3 Los espacios de nombres tambin pueden anidarse y contener a su vez cualquiera de los tipos mostrados en la tabla 1.4, incluso tipos Module. 59
l nombre completo de un tipo Tal como hemos visto, al poder declarar tipos dentro de otros tipos y estos a su vez pueden estar definidos en espacios de nombres, podemos decir que el nombre "completo" de un tipo cualquiera estar formado por el/los espacios de nombres y el/los tipos que los contiene, por ejemplo si la clase -liente definida anteriormente est a su vez dentro del espacio de nombres 4m#itos, el nombre completo ser: 4m#itos.-liente y el nombre completo de la clase Salario ser: 4m#itos.-liente.Salario. Aunque para acceder a la clase -liente no es necesario indicar el espacio de nombres, al menos si la queremos usar desde cualquier otro tipo declarado dentro de ese espacio de nombres, pero si nuestra intencin es usarla desde otro espacio de nombre externo a 4m#itos, en ese caso si que tendremos que usar el nombre completo. Por ejemplo, en el siguiente cdigo tenemos dos espacios de nombres que no estn anidados, cada uno de ellos declara una clase y desde una de ellas queremos acceder a la otra clase, para poder hacerlo debemos indicar el nombre completo, ya que en caso contrario, el compilador de Visual Basic 2005 sera incapaz de saber a que clase queremos acceder. Aames2ace Fno ?!blic Class Clase1 ?!blic Aombre As String n% Class n% Aames2ace Aames2ace Dos ?!blic Class Clase2 ?!blic Aombre As String S!b Hain() Dim c1 As AeJ Fno/Clase1 c1/Aombre = "?e2e" n% S!b n% Class n% Aames2ace 60 Esto mismo lo podemos aplicar en el caso de que tengamos dos clases con el mismo nombre en espacios de nombres distintos.
Nota3 En el mismo proyecto podemos tener ms de una declaracin de un espacio de nombres con el mismo nombre, en estos casos el compilador lo tomar como si todas las clases definidas estuvieran dentro del mismo espacio de nombres, aunque estos estn definidos en ficheros diferentes.
Importacin de espacios de nombres Tal como hemos comentado, los espacios de nombres pueden contener otros espacios de nombres y estos a su vez tambin pueden contener otros espacios de nombres o clases, y como hemos visto, para poder acceder a una clase que no est dentro del mismo espacio de nombres debemos indicar el "nombre completo". Para evitar estar escribiendo todos los espacios de nombres en los que est la clase que nos interesa declarar, podemos usar una especie de acceso directo o para que lo entendamos mejor, podemos crear una especie de "Path", de forma que al declarar una variable, si esta no est definida en el espacio de nombres actual, el compilador busque en todos los espacios de nombres incluidos en esas rutas (paths). Esto lo conseguimos usando la instruccin Imports seguida del espacio de nombres que queremos importar o incluir en el path de los espacios de nombres. Podemos usar tantas importaciones de espacios de nombres como necesitemos y estas siempre deben aparecer al principio del fichero, justo despus de las instrucciones Options. Por ejemplo, si tenemos el cdigo anterior y hacemos la importacin del espacio de nombres en el que est definida la clase Clase1: Im2orts Fno podremos acceder a esa clase de cualquiera de estas dos formas: Dim c1 As AeJ Fno/Clase1 Dim c1 As AeJ Clase1
61 Alias de espacios de nombres Si hacemos demasiadas importaciones de nombres, el problema con el que nos podemos encontrar es que el IntelliSense de Visual Basic 2005 no sea de gran ayuda, ya que mostrar una gran cantidad de clases, y seguramente nos resultar ms difcil encontrar la clase a la que queremos acceder, o tambin podemos encontrarnos en ocasiones en las que nos interese usar un nombre corto para acceder a las clases contenidas en un espacio de nombres, por ejemplo, si queremos indicar de forma explcita las clases de un espacio de nombres como el de (icrosoft.#isualBasic, podemos hacerlo de esta forma: Im2orts 5b = Hicroso8t/$is!al,asic De esta forma podemos usar el "alias" v# para acceder a las clases y dems tipos definidos en ese espacio de nombres. En las figuras 1.14 1.15 podemos ver las dos formas de acceder a las clases del espacio de ese espacio de nombres, en el primer caso sin usar un alias y en el segundo usando el alias v#. Fi'ura $.$=. *os miem#ros de un espacio de nom#res usando el nom#re completo
62 Fi'ura $.$5. 4cceder a los miem#ros de un espacio de nom#res usando un alias
8ropiedades Las propiedades son los miembros de los tipos que nos permiten acceder a los datos que dicho tipo manipula. Normalmente una propiedad est relacionada con un campo, de forma que el campo sea el que realmente contenga el valor y la propiedad simplemente sea una especie de mtodo a travs del cual podemos acceder a ese valor. Debido a que el uso de las propiedades realmente nos permite acceder a los valores de una clase (o tipo), se suelen confundir los campos con las propiedades, de hecho en VB6 si definimos una variable pblica, sta se convierte en una propiedad de la clase, en Visual Basic 2005 casi ocurre lo mismo, pero realmente un campo (o variable) pblico no es una propiedad, al menos en el sentido de que el propio .NET Framework no lo interpreta como tal, aunque en la prctica nos puede parecer que es as, ya que se utilizan de la misma forma. Pero no debemos dejarnos llevar por la comodidad y si no queremos perder funcionalidad, debemos diferenciar en nuestro cdigo las propiedades de los campos. Lo primero que debemos tener presente es que gracias a esta diferenciacin que hace .NET Framework, (realmente VB6 tambin la hace), podemos poner en prctica una de las caractersticas de la programacin orientada a objetos: la encapsulacin, de forma, que la manipulacin de los datos que una clase contiene siempre se deben hacer de forma "interna" o privada a la clase, dejando a las propiedades la posibilidad de que externamente se manipulen, de forma controlada, esos datos. De esta forma tendremos mayor control sobre cmo se acceden o se asignan los valores a esos datos, ya que al definir una propiedad, tal como hemos comentado, realmente estamos definiendo un procedimiento con el cual podemos 63 controlar cmo se acceden a esos datos. Este concepto no ha cambiado, al menos en el fondo, con respecto a como lo hacemos actualmente con VB6, ya que en VB6 tambin podemos declarar procedimientos del tipo propiedad, aunque la forma de hacerlo en Visual Basic 2005 si que ha cambiado un poco, tal como tendremos ocasin de ver a continuacin.
"efinir una propiedad Debido a que una propiedad realmente nos permite acceder a un dato que la clase (o estructura) manipula, siempre tendremos un campo relacionado con una propiedad. El campo ser el que contenga el valor y la propiedad ser la que nos permita manipular ese valor. En Visual Basic 2005, las propiedades las declaramos usando la instruccin +roperty y la definicin de la misma termina con %nd +roperty, esto es prcticamente igual (o casi) que en VB6, la diferencia principal es que en VB6 cuando definimos una propiedad, debemos hacerlo en partes, por ejemplo, si queremos definir cuando accedemos al valor, declaramos esa accin usando las instrucciones +roperty 1et y para definir la parte de la propiedad que asigna un nuevo valor, lo hacemos mediante +roperty Let. En Visual Basic 2005 esas dos acciones, la de lectura (1%T) y asignacin (L%T), las debemos indicar en dos bloques dentro de la propia declaracin de la propiedad, el bloque que nos permite acceder al valor de la propiedad estar indicado por la instruccin 1et y acaba con %nd 1et, por otra parte, el bloque usado para asignar un valor a la propiedad se define mediante la instruccin Set y acaba con %nd Set.
Nota3 En VB6 realmente disponemos de tres bloques o partes en la definicin de una propiedad: Property Get, que nos permite acceder al valor de la propiedad. Propery Let, que nos permite asignar un valor a la propiedad. Property Set, el cual lo usamos cuando la propiedad representa un objeto y queremos asignar dicho objeto a la propiedad. La diferencia entre Let y Set es que el primero se usar en valores de tipos por valor y el segundo en tipos por referencia, aunque el compilador de VB6 realmente usar uno u otro segn asignemos el valor directamente a la propiedad o usemos la instruccin Set para hacer esa asignacin.
Tal como indicamos en la nota anterior, en VB6 existen dos formas de asignar valores a una propiedad de una clase, pero en Visual Basic 2005 no existe ese "conflicto" en la forma de asignar los valores, ya que siempre que se asigna un valor a una propiedad se hace sin usar la instruccin Set, de hecho no se puede usar Set para realizar asignaciones a propiedades en VB2005. 64 Veamos como definir una propiedad en Visual Basic 2005: ?!blic Class Cliente ?ri5ate +nombre As String ?!blic ?ro2ert- Aombre() As String ;et "et!rn +nombre n% ;et Set(,-$al 5al!e As String) +nombre = 5al!e n% Set n% ?ro2ert- n% Class Como podemos comprobar tenemos dos bloques de cdigo, el bloque 1et que es el que se usa cuando queremos acceder al valor de la propiedad, por tanto devolvemos el valor del campo privado usado para almacenar ese dato. El bloque Set es el usado cuando asignamos un valor a la propiedad, este bloque tiene definido un parmetro (alue) que representa al valor que queremos asignar a la propiedad. Aunque en Visual Basic 2005 las definiciones para obtener o asignar el valor de la propiedad se hacen en bloques definidos dentro de un procedimiento del tipo +roperty, esta forma de definir las propiedades no se diferencia demasiado a como lo hacemos en VB6, y una vez que nos acostumbremos lo veremos como una forma ms "compacta" de hacerlo.
,ropiedades de solo lectura En ciertas ocasiones nos puede resultar interesante que una propiedad sea de solo lectura, de forma que el valor que representa no pueda ser cambiado. En VB6 para definir una propiedad de solo lectura bastaba con definir solo la parte 1et de la propiedad, en Visual Basic 2005 tambin se hace de esa forma, pero, aunque realmente es algo ..., debemos indicar expresamente que esa es nuestra intencin, por tanto no solo basta con definir solo el bloque 1et, sino que debemos usar el modificador 'eadOnly para que el compilador de Visual Basic 2005 acepte la declaracin: 65 ?!blic "ea%0nl- ?ro2ert- Ho-() As Date ;et "et!rn Date/AoJ n% ;et n% ?ro2ert-
,ropiedades de solo escritura De igual forma, si queremos definir una propiedad que sea de solo escritura, solo definiremos el bloque Set, pero al igual que ocurre con las propiedades de solo lectura, debemos indicar expresamente que esa es nuestra intencin, para ello usaremos la palabra clave 4riteOnly: ?!blic <rite0nl- ?ro2ert- ?assJor%() As String Set(,-$al 5al!e As String) I8 5al!e = "blablabla" 1hen 4 oQ n% I8 n% Set n% ?ro2ert-
"iferente accesibilidad para los blo6ues 5et y Set En las propiedades normales (de lectura y escritura), podemos definir diferentes niveles de accesibilidad a cada uno de los dos bloques que forman una propiedad. Por ejemplo, podramos definir el bloque 1et como pblico, (siempre accesible), y el bloque Set como +riate, de forma que solo se puedan realizar asignaciones desde dentro de la propia clase. Por ejemplo, el salario de un empleado podramos declararlo para que desde cualquier punto se pueda saber el importe, pero la asignacin de dicho importe solo estar accesible para los procedimientos definidos en la propia clase: ?!blic Class m2lea%o 66 ?ri5ate +salario As Decimal ?!blic ?ro2ert- Salario() As Decimal ;et "et!rn +salario n% ;et ?ri5ate Set(,-$al 5al!e As Decimal) +salario = 5al!e n% Set n% ?ro2ert- n% Class Para hacer que el bloque Set sea privado, lo indicamos con el modificador de accesibilidad +riate, al no indicar ningn modificador en el bloque 1et, ste ser el mismo que el de la propiedad. En VB6 podemos hacer esto mismo definiendo como pblico la declaracin de +roperty 1et y como privado la de la asignacin: +roperty Let.
Nota3 El nivel de accesibilidad de los bloques Get o Set debe ser igual o inferior que el de la propiedad, por tanto si la propiedad la declaramos como Private, no podemos definir como pblico los bloques Get o Set.
,ropiedades predeterminadas Una cosa que echamos en falta en Visual Basic 2005 son las propiedades predeterminadas, aunque existen no son exactamente igual que en VB6. En VB6 podemos definir como propiedad predeterminada cualquiera de las propiedades de la clase, aunque la forma de hacerlo es un poco "rebuscada" y poco intuitiva, al menos podemos definir como predeterminada la que ms nos interese. En Visual Basic 2005 no podemos definir como predeterminada cualquier propiedad, ya que debido a como se realizan las asignaciones de objetos en .NET (sin necesidad de usar Set), siempre debemos indicar la propiedad a la que queremos asignar el valor, porque en caso de que no se indique ninguna, el compilador interpretar que lo que queremos asignar es un objeto y no un valor a una propiedad. Para evitar conflictos o tener que usar alguna instruccin "extra" para que se sepa si lo que queremos asignar es un valor o un objeto, en Visual Basic 2005 las 67 propiedades predeterminadas siempre deben ser parametrizadas, es decir, tener como mnimo un parmetro. Para indicar que una propiedad es la propiedad por defecto lo debemos hacer usando la instruccin Default: De8a!lt ?!blic "ea%0nl- ?ro2ert- Item(,-$al in%eG As Integer) As m2lea%o ;et 4 /// n% ;et n% ?ro2ert- Como vemos en este ejemplo, una propiedad por defecto puede ser de solo lectura y tambin de solo escritura o de lectura/escritura. Para usar esta propiedad, al ser la propiedad por defecto, no es necesario indicar el nombre de la propiedad, aunque si as lo deseamos podemos indicarla, aunque en este caso no tendra mucha utilidad el haberla definido como propiedad por defecto: Dim e As AeJ m2lea%o Dim e1 As m2lea%o = e(2) 4 1ambiRn 2o%emos !sarla in%ican%o el nombre %e la 2ro2ie%a%D Dim e2 As m2lea%o = e/Item(2)
!obrecar"a de propiedades predeterminadas Debido a que las propiedades predeterminadas de Visual Basic 2005 deben recibir un parmetro, podemos crear sobrecargas de una propiedad predeterminada, aunque debemos recordar que para que esa sobrecarga pueda ser posible, el tipo o nmero de argumentos deben ser distintos entre las distintas sobrecargas, por ejemplo podramos tener una sobrecarga que reciba un parmetro de tipo entero y otra que lo reciba de tipo cadena: De8a!lt ?!blic "ea%0nl- ?ro2ert- Item(,-$al in%eG As Integer) As m2lea%o ;et 4 /// n% ;et n% ?ro2ert- 68 De8a!lt ?!blic ?ro2ert- Item(,-$al in%eG As String) As m2lea%o ;et 4 /// n% ;et Set(,-$al 5al!e As m2lea%o) 4 n% Set n% ?ro2ert- Incluso como vemos en este cdigo una de las sobrecargas puede ser de solo lectura y la otra de lectura/escritura. Lo que realmente importa es que el nmero o tipo de parmetros de cada sobrecarga sea diferente.
Las propiedades predeterminadas tienen sentido en Visual Basic 2005 cuando queremos que su uso sea parecido al de un array. Por tanto es habitual que las clases de tipo coleccin sean las ms indicadas para definir propiedades por defecto. Aunque no siempre el valor devuelto debe ser un elemento de una coleccin o array, ya que podemos usar las propiedades predeterminadas para acceder a los miembros de una clase "normal", de forma que se devuelva un valor segn el parmetro indicado, esto nos permitira, por ejemplo, acceder a los miembros de la clase desde un bucle )or. Si definimos una propiedad predeterminada como en el siguiente cdigo: ?!blic Class Artic!lo ?!blic Descri2ciSn As String ?!blic ?recio$enta As Decimal ?!blic Gistencias As Decimal De8a!lt ?!blic "ea%0nl- ?ro2ert- Item(,-$al in%eG As Integer) As String ;et Select Case in%eG Case 0 "et!rn Descri2ciSn Case 1 69 "et!rn ?recio$enta/1oString Case 2 "et!rn Gistencias/1oString Case lse "et!rn "" n% Select n% ;et n% ?ro2ert- n% Class La podemos usar de esta forma: )or i As Integer = 0 1o 2 Console/<rite'ine( art(i) ) AeGt
Resumiendo: Las propiedades predeterminadas en Visual Basic 2005 siempre deben tener un parmetro, para que su uso se asemeje a un array, es decir, se use como indizador de la clase. Por convencin, cuando se usan como indizador, el nombre de la propiedad predeterminada suele ser Item.
Interfaces Las interfaces son un elemento bastante importante en .NET Framework, ya que de hecho se utiliza con bastante frecuencia, en esta leccin veremos que son las interfaces y como utilizarlas en nuestros proyectos, tambin veremos que papel juegan en .NET y cmo aplicar algunas de las definidas en la biblioteca base.
78u0 es una interfa*9 Las interfaces son una forma especial de una clase, aunque la diferencia principal con las clases es que las interfaces no contienen cdigo ejecutable, solo definen los miembros. 70 Para entenderlo mejor, veamos las interfaces desde el punto de vista de Visual Basic 6.0. En Visual Basic 6.0, cuando definimos una clase, realmente estamos haciendo dos cosas: 1- Definiendo una interfaz con cada uno de los miembros que la clase contiene: mtodos, propiedades, eventos, etc. 2- Definiendo el cdigo a utilizar por cada uno de esos miembros. Desde ese punto de vista, podemos decir que una interfaz define cada uno de los miembros de una clase, es decir, que tipo de mtodo es, si los mtodos tienen parmetros, cuantos y de que tipos son, que propiedades o eventos define la clase, etc. Por tanto, podemos decir que la interfaz de una clase indica los miembros que dicha clase expone, y como hemos indicado anteriormente, cuando en VB6 definimos una clase, tambin estamos definiendo una interfaz, de hecho en Visual Basic 6.0 no hay forma de definir interfaces como algo independiente de una clase; cuando queremos definir una interfaz en VB6, lo ms que podemos hacer es definir una clase sin cdigo ejecutable. Pero Visual Basic 2005, va an ms lejos, ya que las interfaces las definimos de forma independiente de las clases. Es ms, cuando definimos una clase NO estamos definiendo una interfaz. Para definir una interfaz en VB2005 tenemos que usar la instruccin Interface seguida del nombre y acabar la declaracin con %nd Interface: ?!blic Inter8ace IAnimal 4/// n% Inter8ace
Nota3 Segn las indicaciones de nomenclatura de .NET Framework, se recomienda que todas las interfaces empiecen con una I mayscula seguida del nombre al que hacer referencia la interfaz.
71 78u0 contiene una interfa*9 Al principio de esta leccin hemos comentado que las interfaces no contienen cdigo, solo define los miembros que contiene. Esa definicin la haremos como cualquier otra, con la diferencia de que no incluimos ningn cdigo, solo la "firma" o el prototipo de cada uno de esos miembros. En el siguiente cdigo definimos una interfaz que contiene los cuatros tipos de miembros tpicos de cualquier clase: ?!blic Inter8ace I?r!eba S!b Hostrar() )!nction Sal!%o(,-$al nombre As String) As String ?ro2ert- Aombre() As String 5ent DatosCambia%os() n% Inter8ace El primer miembro de esta interfaz, es un mtodo de tipo Sub que no recibe parmetros. El siguiente mtodo es una funcin que devuelve un valor de tipo String y recibe un parmetro tambin de tipo cadena. A continuacin definimos una propiedad que devuelve una cadena. Por ltimo, definimos un evento. Como podemos observar, lo nico que tenemos que hacer es indicar el tipo de miembro y si recibe o no algn parmetro o argumento. Dos cosas importantes sobre las interfaces: 1- No se pueden definir campos. 2- Los miembros de las interfaces siempre son pblicos, tal como indicbamos en la tabla 1.3.
:na interfa* es un contrato Siempre que leemos sobre las interfaces, lo primero con lo que nos solemos encontrar es que una interfa. es un contrato. Veamos que nos quieren decir con esa frase. Tal como acabamos de ver, las interfaces solo definen los miembros, pero no el cdigo a usar en cada uno de ellos, esto es as precisamente porque el papel que juegan las interfaces es el de solo indicar que es lo que una clase o estructura puede, o mejor dicho, debe implementar. Si en una clase indicamos que queremos "implementar" una interfaz, esa clase debe definir cada uno de los miembros que la interfaz expone. De esta forma nos 72 aseguramos de que si una clase implementa una interfaz, tambin implementa todos los miembros definidos en dicha interfaz. Cuando una clase implementa una interfaz est firmando un contrato con el que se compromete a definir todos los miembros que la clase define, de hecho el propio compilador nos obliga a hacerlo.
'as interfaces y el polimorfismo Como comentamos anteriormente, el polimorfismo es una caracterstica que nos permite acceder a los miembros de un objeto sin necesidad de tener un conocimiento exacto de ese objeto (o de la clase a partir del que se ha instanciado), lo nico que tenemos que saber es que ese objeto tiene ciertos mtodos (u otros miembros) a los que podemos acceder. Tambin hemos comentado que las interfaces representan un contrato entre las clases que las implementan, por tanto las interfaces pueden ser, (de hecho lo son), un medio para poner en prctica esta caracterstica de la programacin orientada a objetos. Si una clase implementa una interfaz, esa clase tiene todos los miembros de la interfaz, por tanto podemos acceder a esa clase, que en principio pude sernos desconocida, desde un objeto del mismo tipo que la interfaz.
:sar una interfa* en una clase Para poder utilizar una interfaz en una clase, o dicho de otra forma: para "implementar" los miembros expuestos por una interfaz en una clase debemos hacerlo mediante la instruccin Implements seguida del nombre de la interfaz: ?!blic Class ?r!eba Im2lements I?r!eba Y como comentbamos, cualquier clase que implemente una interfaz debe definir cada uno de los miembros de esa interfaz, por eso es el propio Visual Basic el encargado de crear automticamente los mtodos y propiedades que la interfaz implementa, aunque solo inserta el "prototipo" de cada uno de esos miembros, dejando para nosotros el trabajo de escribir el cdigo. Usando la definicin de la interfaz I8rue#a que vimos antes, el cdigo que aadir VB ser el siguiente: ?!blic Class ?r!eba Im2lements I?r!eba 73 ?!blic 5ent DatosCambia%os() Im2lements I?r!eba/DatosCambia%os ?!blic S!b Hostrar() Im2lements I?r!eba/Hostrar n% S!b ?!blic ?ro2ert- Aombre() As String Im2lements I?r!eba/Aombre ;et n% ;et Set(,-$al 5al!e As String) n% Set n% ?ro2ert- ?!blic )!nction Sal!%o(,-$al nombre As String) As String + Im2lements I?r!eba/Sal!%o n% )!nction n% Class Como podemos apreciar, no solo ha aadido las definiciones de cada miembro de la interfaz, sino que tambin aade cdigo extra a cada uno de esos miembros: la instruccin Implements seguida del nombre de la interfaz y el miembro al que se har referencia.
Nota3 Si el lector antes ha utilizado las interfaces en VB6, esto no le resultar extrao, ya que en ese lenguaje, cuando implementamos una interfaz tambin se crean automticamente las definiciones de los miembros que contiene la interfaz, aunque el formato utilizado por VB6 es: <Nombre de la interfaz> <guin bajo> <nombre del mtodo>, por ejemplo: Private Sub IPrueba_Mostrar(). 74
La utilidad de que en cada uno de los miembros se indique expresamente el mtodo al que se hace referencia, es que podemos usar nombres diferentes al indicado en la interfaz. Por ejemplo, si implementamos esta interfaz en una clase que solo utilizar la impresora, al mtodo Mostrar lo podramos llamar Imprimir que sera ms adecuado, en ese caso simplemente cambiamos el nombre del mtodo de la clase para que implemente el mtodo Mostrar de la interfaz: ?!blic S!b Im2rimir() Im2lements I?r!eba/Hostrar n% S!b De esta forma, aunque en la clase se llame de forma diferente, realmente hace referencia al mtodo de la interfaz.
Acceder a los miembros implementados Una vez que tenemos implementada una interfaz en nuestra clase, podemos acceder a esos miembros de forma directa, es decir, usando un objeto creado a partir de la clase: Dim 2r!eba1 As AeJ ?r!eba 2r!eba1/Hostrar() O bien de forma indirecta, por medio de una variable del mismo tipo que la interfaz: Dim 2r!eba1 As AeJ ?r!eba Dim inter8a&1 As I?r!eba inter8a&1 = 2r!eba1 inter8a&1/Hostrar() Qu ha ocurre aqu? Como ya comentamos anteriormente, cuando asignamos variables por referencia, realmente lo que asignamos son referencias a los objetos creados en la memoria, por tanto la variable interfa@$ est haciendo referencia al mismo objeto que prue#a$, aunque esa variable solo tendr acceso a los miembros de la clase 75 Prueba que conoce, es decir, los miembros definidos en I8rue#a. Si la clase define otros miembros que no estn en la interfaz, la variable interfa@$ no podr acceder a ellos.
Saber si un obeto implementa una interfa* Si las interfaces sirven para acceder de forma annima a los mtodos de un objeto, es normal que en Visual Basic tengamos algn mecanismo para descubrir si un objeto implementa una interfaz. Para realizar esta comprobacin podemos usar en una expresin If5T"en la instruccin TypeOf... Is, de forma que si la variable indicada despus de TypeOf contiene el tipo especificado despus de Is, la condicin se cumple: I8 1-2e08 2r!eba1 Is I?r!eba 1hen inter8a&1 = 2r!eba1 inter8a&1/Hostrar() n% I8 De esta forma nos aseguramos de que el cdigo se ejecutar solamente si la variable prue#a$ contiene una definicin de la interfaz I8rue#a.
-mplementaci(n de m)ltiples interfaces En Visual Basic 2005, una misma clase puede implementar ms de una interfaz. Para indicar que implementamos ms de una interfaz podemos hacerlo de dos formas: 1- Usando nuevamente la instruccin Implements seguida del nombre de la interfaz: ?!blic Class ?r!eba Im2lements I?r!eba Im2lements ICom2arable 2- Indicando las otras interfaces en la misma instruccin Implements, pero separndolas con comas: ?!blic Class ?r!eba 76 Im2lements I?r!eba, ICom2arable De cualquiera de las dos formas es vlido implementar ms de una interfaz, aunque en ambos casos siempre debemos definir los miembros de cada una de esas interfaces.
%)ltiple implementaci(n de un mismo miembro Como acabamos de comprobar, una misma clase puede implementar ms de una interfaz, y esto nos puede causar una duda: Qu ocurre si dos interfaces definen un mtodo que es idntico en ambas? En principio, no habra problemas, ya que el propio Visual Basic creara dos mtodos con nombres diferentes y a cada uno le asignara la implementacin de ese mtodo definido en cada interfaz. Por ejemplo, si tenemos otra interfaz que define el mtodo Mostrar y la implementamos en la clase 8rue#a, la declaracin podra quedar de esta forma: ?!blic Inter8ace IHostrar S!b Hostrar() n% Inter8ace
?!blic S!b Hostrar1() Im2lements IHostrar/Hostrar n% S!b Aunque si ambos mtodos hacen lo mismo, en este ejemplo mostrar algo, podramos hacer que el mismo mtodo de la clase sirva para implementar el de las dos interfaces: ?!blic S!b Hostrar() Im2lements I?r!eba/Hostrar, IHostrar/Hostrar n% S!b Es decir, lo nico que tendramos que hacer es indicar la otra implementacin separndola con una coma.
77 7"(nde podemos implementar las interfaces9 Para ir acabando este tema nos queda por saber, entre otras cosas, dnde podemos implementar las interfaces, es decir, en que tipos de datos podemos usar Implements. La implementacin de interfaces la podemos hacer en las clases (Class), estructuras (Structure) y en otras interfaces (Interface). Debido a que una interfaz puede implementar otras interfaces, si en una clase implementamos una interfaz que a su vez implementa otras, esa clase tendr definidas cada una de las interfaces, lo mismo ocurre con una clase que "se derive" de otra clase que implementa alguna interfaz, la nueva clase tambin incorporar esa interfaz.
Nota3 Cuando una interfaz implementa otras interfaces, stas no se pueden indicar mediante Implements, en lugar de usar esa instruccin debemos usar Inherits. Public Interface IPrueba2 Inherits IMostrar
Si en una clase implementamos una interfaz que a su vez implementa otras interfaces, esa clase tendr definiciones de todos los miembros de todas las interfaces, por ejemplo, si tenemos la siguiente definicin de la interfaz I8rue#a2 que "implementa" la interfaz IMostrar: ?!blic Inter8ace I?r!eba2 Inherits IHostrar )!nction Sal!%o(,-$al nombre As String) As String ?ro2ert- Aombre() As String 5ent DatosCambia%os() n% Inter8ace Y la clase 8rue#a2 implementa I8rue#a2, la definicin de los miembros quedara de la siguiente forma: ?!blic Class ?r!eba2 78 Im2lements I?r!eba2 ?!blic S!b Hostrar() Im2lements IHostrar/Hostrar n% S!b ?!blic 5ent DatosCambia%os() Im2lements I?r!eba2/DatosCambia%os ?!blic ?ro2ert- Aombre() As String Im2lements I?r!eba2/Aombre ;et n% ;et Set(,-$al 5al!e As String) n% Set n% ?ro2ert- ?!blic )!nction Sal!%o(,-$al nombre As String) As String + Im2lements I?r!eba2/Sal!%o n% )!nction n% Class En este cdigo, el mtodo Mostrar se indica mediante la interfaz IMostrar, pero tambin se puede hacer por medio de I8rue#a2.Mostrar, ya que I8rue#a2 tambin lo implementa (o hereda). Si dejamos que Visual Basic cree los miembros, no tendremos problemas a la hora de definirlos. Pero si lo hacemos manualmente, aunque dentro del IDE de Visual Basic, ste nos ayuda indicndonos que interfaces implementamos y qu miembros son los que se adecuan a la declaracin que estamos usando, tal como podemos comprobar en la figura 1.02.16: 79 Fi'ura $.02.$" IntelliSense solo muestra los mHtodos &ue me(or se adecuan a la declaracin
:n eemplo pr&ctico usando una interfa* de .NET Tal como comentamos al principio, el propio .NET est "plagado" de interfaces, cada una de ellas tiene un fin concreto, por ejemplo, si queremos definir una clase que pueda ser clasificada por el propio .NET, esa clase debe implementar la interfaz IComparable, ya que el mtodo Sort, (de la clase que contiene los elementos del tipo definido por nosotros), que es el encargado de clasificar los elementos, har una llamada al mtodo IComparable.CompareTo de cada uno de los objetos que queremos clasificar, por tanto, si la clase no ha definido esa interfaz, no podremos clasificar los elementos que contenga. En el siguiente cdigo tenemos la definicin de una clase llamada Empleado que implementa la interfaz IComparable y en el mtodo CompareTo hace la comprobacin de que objeto es mayor o menor, si el de la propia clase o el indicado en el parmetro de esa funcin: ?!blic Class m2lea%o Im2lements ICom2arable ?!blic Aombre As String ?!blic S!b AeJ(,-$al nombre As String) He/Aombre = nombre n% S!b 4 Si el ob#eto es %el ti2o m2lea%o, com2aramos los nombres/ 4 Si no es %el ti2o m2lea%o, %e5ol5emos !n cero 4 I!e signi8ica I!e los %os ob#etos son ig!ales/ ?!blic )!nction Com2are1o(,-$al ob# As 0b#ect) As Integer + 80 Im2lements S-stem/ICom2arable/Com2are1o I8 1-2e08 ob# Is m2lea%o 1hen Dim e1 As m2lea%o = C1-2e(ob#, m2lea%o) "et!rn String/Com2are(He/Aombre, e1/Aombre) lse "et!rn 0 n% I8 n% )!nction n% Class En el mtodo CompareTo hacemos una comprobacin de que el objeto con el que debemos realizar la comparacin es del tipo Empleado, en ese caso convertimos el objeto pasado en uno del tipo Empleado y comparamos los nombres. Si el objeto que recibe el mtodo no es del tipo Empleado, devolvemos un cero, para indicar que no haga ninguna clasificacin, ya que ese valor indica que los dos objetos son iguales. Esta comparacin no es estrictamente necesaria, ya que si no indicamos el valor que debe devolver una funcin, devolver un valor cero, al menos en este caso, ya que el tipo a devolver es un nmero entero. Esta clase la podemos usar de esta forma: 4 Fna colecciSn %e %atos %el ti2o m2lea%o/ Dim em2lea%os As AeJ S-stem/Collections/;eneric/'ist(08 m2lea%o) 4 ATa%imos 5arios em2lea%os a la colecciSn/ em2lea%os/A%%(AeJ m2lea%o("?e2e")) em2lea%os/A%%(AeJ m2lea%o(",ernar%o")) em2lea%os/A%%(AeJ m2lea%o("@!an")) em2lea%os/A%%(AeJ m2lea%o("Ana")) 4 Clasi8icamos los em2lea%os %e la colecciSn/ em2lea%os/Sort() 81 4 Hostramos los %atos !na 5e& clasi8ica%os/ )or ach e1 As m2lea%o In em2lea%os Console/<rite'ine(e1/Aombre) AeGt
Interfaces Las interfaces son un elemento bastante importante en .NET Framework, ya que de hecho se utiliza con bastante frecuencia, en esta leccin veremos que son las interfaces y como utilizarlas en nuestros proyectos, tambin veremos que papel juegan en .NET y cmo aplicar algunas de las definidas en la biblioteca base.
78u0 es una interfa*9 Las interfaces son una forma especial de una clase, aunque la diferencia principal con las clases es que las interfaces no contienen cdigo ejecutable, solo definen los miembros. Para entenderlo mejor, veamos las interfaces desde el punto de vista de Visual Basic 6.0. En Visual Basic 6.0, cuando definimos una clase, realmente estamos haciendo dos cosas: 1- Definiendo una interfaz con cada uno de los miembros que la clase contiene: mtodos, propiedades, eventos, etc. 2- Definiendo el cdigo a utilizar por cada uno de esos miembros. Desde ese punto de vista, podemos decir que una interfaz define cada uno de los miembros de una clase, es decir, que tipo de mtodo es, si los mtodos tienen parmetros, cuantos y de que tipos son, que propiedades o eventos define la clase, etc. Por tanto, podemos decir que la interfaz de una clase indica los miembros que dicha clase expone, y como hemos indicado anteriormente, cuando en VB6 definimos una clase, tambin estamos definiendo una interfaz, de hecho en Visual Basic 6.0 no hay forma de definir interfaces como algo independiente de una clase; cuando queremos definir una interfaz en VB6, lo ms que podemos hacer es definir una clase sin cdigo ejecutable. Pero Visual Basic 2005, va an ms lejos, ya que las interfaces las definimos de forma independiente de las clases. Es ms, cuando definimos una clase NO estamos definiendo una interfaz. Para definir una interfaz en VB2005 tenemos que usar la instruccin Interface seguida del nombre y acabar la declaracin con %nd Interface: 82 ?!blic Inter8ace IAnimal 4/// n% Inter8ace
Nota3 Segn las indicaciones de nomenclatura de .NET Framework, se recomienda que todas las interfaces empiecen con una I mayscula seguida del nombre al que hacer referencia la interfaz.
78u0 contiene una interfa*9 Al principio de esta leccin hemos comentado que las interfaces no contienen cdigo, solo define los miembros que contiene. Esa definicin la haremos como cualquier otra, con la diferencia de que no incluimos ningn cdigo, solo la "firma" o el prototipo de cada uno de esos miembros. En el siguiente cdigo definimos una interfaz que contiene los cuatros tipos de miembros tpicos de cualquier clase: ?!blic Inter8ace I?r!eba S!b Hostrar() )!nction Sal!%o(,-$al nombre As String) As String ?ro2ert- Aombre() As String 5ent DatosCambia%os() n% Inter8ace El primer miembro de esta interfaz, es un mtodo de tipo Sub que no recibe parmetros. El siguiente mtodo es una funcin que devuelve un valor de tipo String y recibe un parmetro tambin de tipo cadena. A continuacin definimos una propiedad que devuelve una cadena. Por ltimo, definimos un evento. Como podemos observar, lo nico que tenemos que hacer es indicar el tipo de miembro y si recibe o no algn parmetro o argumento. 83 Dos cosas importantes sobre las interfaces: 1- No se pueden definir campos. 2- Los miembros de las interfaces siempre son pblicos, tal como indicbamos en la tabla 1.3.
:na interfa* es un contrato Siempre que leemos sobre las interfaces, lo primero con lo que nos solemos encontrar es que una interfa. es un contrato. Veamos que nos quieren decir con esa frase. Tal como acabamos de ver, las interfaces solo definen los miembros, pero no el cdigo a usar en cada uno de ellos, esto es as precisamente porque el papel que juegan las interfaces es el de solo indicar que es lo que una clase o estructura puede, o mejor dicho, debe implementar. Si en una clase indicamos que queremos "implementar" una interfaz, esa clase debe definir cada uno de los miembros que la interfaz expone. De esta forma nos aseguramos de que si una clase implementa una interfaz, tambin implementa todos los miembros definidos en dicha interfaz. Cuando una clase implementa una interfaz est firmando un contrato con el que se compromete a definir todos los miembros que la clase define, de hecho el propio compilador nos obliga a hacerlo.
'as interfaces y el polimorfismo Como comentamos anteriormente, el polimorfismo es una caracterstica que nos permite acceder a los miembros de un objeto sin necesidad de tener un conocimiento exacto de ese objeto (o de la clase a partir del que se ha instanciado), lo nico que tenemos que saber es que ese objeto tiene ciertos mtodos (u otros miembros) a los que podemos acceder. Tambin hemos comentado que las interfaces representan un contrato entre las clases que las implementan, por tanto las interfaces pueden ser, (de hecho lo son), un medio para poner en prctica esta caracterstica de la programacin orientada a objetos. Si una clase implementa una interfaz, esa clase tiene todos los miembros de la interfaz, por tanto podemos acceder a esa clase, que en principio pude sernos desconocida, desde un objeto del mismo tipo que la interfaz.
:sar una interfa* en una clase Para poder utilizar una interfaz en una clase, o dicho de otra forma: para "implementar" los miembros expuestos por una interfaz en una clase debemos hacerlo mediante la instruccin Implements seguida del nombre de la interfaz: 84 ?!blic Class ?r!eba Im2lements I?r!eba Y como comentbamos, cualquier clase que implemente una interfaz debe definir cada uno de los miembros de esa interfaz, por eso es el propio Visual Basic el encargado de crear automticamente los mtodos y propiedades que la interfaz implementa, aunque solo inserta el "prototipo" de cada uno de esos miembros, dejando para nosotros el trabajo de escribir el cdigo. Usando la definicin de la interfaz I8rue#a que vimos antes, el cdigo que aadir VB ser el siguiente: ?!blic Class ?r!eba Im2lements I?r!eba ?!blic 5ent DatosCambia%os() Im2lements I?r!eba/DatosCambia%os ?!blic S!b Hostrar() Im2lements I?r!eba/Hostrar n% S!b ?!blic ?ro2ert- Aombre() As String Im2lements I?r!eba/Aombre ;et n% ;et Set(,-$al 5al!e As String) n% Set n% ?ro2ert- ?!blic )!nction Sal!%o(,-$al nombre As String) As String + Im2lements I?r!eba/Sal!%o 85 n% )!nction n% Class Como podemos apreciar, no solo ha aadido las definiciones de cada miembro de la interfaz, sino que tambin aade cdigo extra a cada uno de esos miembros: la instruccin Implements seguida del nombre de la interfaz y el miembro al que se har referencia.
Nota3 Si el lector antes ha utilizado las interfaces en VB6, esto no le resultar extrao, ya que en ese lenguaje, cuando implementamos una interfaz tambin se crean automticamente las definiciones de los miembros que contiene la interfaz, aunque el formato utilizado por VB6 es: <Nombre de la interfaz> <guin bajo> <nombre del mtodo>, por ejemplo: Private Sub IPrueba_Mostrar().
La utilidad de que en cada uno de los miembros se indique expresamente el mtodo al que se hace referencia, es que podemos usar nombres diferentes al indicado en la interfaz. Por ejemplo, si implementamos esta interfaz en una clase que solo utilizar la impresora, al mtodo Mostrar lo podramos llamar Imprimir que sera ms adecuado, en ese caso simplemente cambiamos el nombre del mtodo de la clase para que implemente el mtodo Mostrar de la interfaz: ?!blic S!b Im2rimir() Im2lements I?r!eba/Hostrar n% S!b De esta forma, aunque en la clase se llame de forma diferente, realmente hace referencia al mtodo de la interfaz.
Acceder a los miembros implementados Una vez que tenemos implementada una interfaz en nuestra clase, podemos acceder a esos miembros de forma directa, es decir, usando un objeto creado a partir de la clase: Dim 2r!eba1 As AeJ ?r!eba 2r!eba1/Hostrar() 86 O bien de forma indirecta, por medio de una variable del mismo tipo que la interfaz: Dim 2r!eba1 As AeJ ?r!eba Dim inter8a&1 As I?r!eba inter8a&1 = 2r!eba1 inter8a&1/Hostrar() Qu ha ocurre aqu? Como ya comentamos anteriormente, cuando asignamos variables por referencia, realmente lo que asignamos son referencias a los objetos creados en la memoria, por tanto la variable interfa@$ est haciendo referencia al mismo objeto que prue#a$, aunque esa variable solo tendr acceso a los miembros de la clase Prueba que conoce, es decir, los miembros definidos en I8rue#a. Si la clase define otros miembros que no estn en la interfaz, la variable interfa@$ no podr acceder a ellos.
Saber si un obeto implementa una interfa* Si las interfaces sirven para acceder de forma annima a los mtodos de un objeto, es normal que en Visual Basic tengamos algn mecanismo para descubrir si un objeto implementa una interfaz. Para realizar esta comprobacin podemos usar en una expresin If5T"en la instruccin TypeOf... Is, de forma que si la variable indicada despus de TypeOf contiene el tipo especificado despus de Is, la condicin se cumple: I8 1-2e08 2r!eba1 Is I?r!eba 1hen inter8a&1 = 2r!eba1 inter8a&1/Hostrar() n% I8 De esta forma nos aseguramos de que el cdigo se ejecutar solamente si la variable prue#a$ contiene una definicin de la interfaz I8rue#a.
87 -mplementaci(n de m)ltiples interfaces En Visual Basic 2005, una misma clase puede implementar ms de una interfaz. Para indicar que implementamos ms de una interfaz podemos hacerlo de dos formas: 1- Usando nuevamente la instruccin Implements seguida del nombre de la interfaz: ?!blic Class ?r!eba Im2lements I?r!eba Im2lements ICom2arable 2- Indicando las otras interfaces en la misma instruccin Implements, pero separndolas con comas: ?!blic Class ?r!eba Im2lements I?r!eba, ICom2arable De cualquiera de las dos formas es vlido implementar ms de una interfaz, aunque en ambos casos siempre debemos definir los miembros de cada una de esas interfaces.
%)ltiple implementaci(n de un mismo miembro Como acabamos de comprobar, una misma clase puede implementar ms de una interfaz, y esto nos puede causar una duda: Qu ocurre si dos interfaces definen un mtodo que es idntico en ambas? En principio, no habra problemas, ya que el propio Visual Basic creara dos mtodos con nombres diferentes y a cada uno le asignara la implementacin de ese mtodo definido en cada interfaz. Por ejemplo, si tenemos otra interfaz que define el mtodo Mostrar y la implementamos en la clase 8rue#a, la declaracin podra quedar de esta forma: ?!blic Inter8ace IHostrar S!b Hostrar() n% Inter8ace
?!blic S!b Hostrar1() Im2lements IHostrar/Hostrar 88 n% S!b Aunque si ambos mtodos hacen lo mismo, en este ejemplo mostrar algo, podramos hacer que el mismo mtodo de la clase sirva para implementar el de las dos interfaces: ?!blic S!b Hostrar() Im2lements I?r!eba/Hostrar, IHostrar/Hostrar n% S!b Es decir, lo nico que tendramos que hacer es indicar la otra implementacin separndola con una coma.
7"(nde podemos implementar las interfaces9 Para ir acabando este tema nos queda por saber, entre otras cosas, dnde podemos implementar las interfaces, es decir, en que tipos de datos podemos usar Implements. La implementacin de interfaces la podemos hacer en las clases (Class), estructuras (Structure) y en otras interfaces (Interface). Debido a que una interfaz puede implementar otras interfaces, si en una clase implementamos una interfaz que a su vez implementa otras, esa clase tendr definidas cada una de las interfaces, lo mismo ocurre con una clase que "se derive" de otra clase que implementa alguna interfaz, la nueva clase tambin incorporar esa interfaz.
Nota3 Cuando una interfaz implementa otras interfaces, stas no se pueden indicar mediante Implements, en lugar de usar esa instruccin debemos usar Inherits. Public Interface IPrueba2 Inherits IMostrar
Si en una clase implementamos una interfaz que a su vez implementa otras interfaces, esa clase tendr definiciones de todos los miembros de todas las 89 interfaces, por ejemplo, si tenemos la siguiente definicin de la interfaz I8rue#a2 que "implementa" la interfaz IMostrar: ?!blic Inter8ace I?r!eba2 Inherits IHostrar )!nction Sal!%o(,-$al nombre As String) As String ?ro2ert- Aombre() As String 5ent DatosCambia%os() n% Inter8ace Y la clase 8rue#a2 implementa I8rue#a2, la definicin de los miembros quedara de la siguiente forma: ?!blic Class ?r!eba2 Im2lements I?r!eba2 ?!blic S!b Hostrar() Im2lements IHostrar/Hostrar n% S!b ?!blic 5ent DatosCambia%os() Im2lements I?r!eba2/DatosCambia%os ?!blic ?ro2ert- Aombre() As String Im2lements I?r!eba2/Aombre ;et n% ;et Set(,-$al 5al!e As String) n% Set n% ?ro2ert- 90 ?!blic )!nction Sal!%o(,-$al nombre As String) As String + Im2lements I?r!eba2/Sal!%o n% )!nction n% Class En este cdigo, el mtodo Mostrar se indica mediante la interfaz IMostrar, pero tambin se puede hacer por medio de I8rue#a2.Mostrar, ya que I8rue#a2 tambin lo implementa (o hereda). Si dejamos que Visual Basic cree los miembros, no tendremos problemas a la hora de definirlos. Pero si lo hacemos manualmente, aunque dentro del IDE de Visual Basic, ste nos ayuda indicndonos que interfaces implementamos y qu miembros son los que se adecuan a la declaracin que estamos usando, tal como podemos comprobar en la figura 1.02.16: Fi'ura $.02.$" IntelliSense solo muestra los mHtodos &ue me(or se adecuan a la declaracin
:n eemplo pr&ctico usando una interfa* de .NET Tal como comentamos al principio, el propio .NET est "plagado" de interfaces, cada una de ellas tiene un fin concreto, por ejemplo, si queremos definir una clase que pueda ser clasificada por el propio .NET, esa clase debe implementar la interfaz IComparable, ya que el mtodo Sort, (de la clase que contiene los elementos del tipo definido por nosotros), que es el encargado de clasificar los elementos, har una llamada al mtodo IComparable.CompareTo de cada uno de los objetos que queremos clasificar, por tanto, si la clase no ha definido esa interfaz, no podremos clasificar los elementos que contenga. En el siguiente cdigo tenemos la definicin de una clase llamada Empleado que implementa la interfaz IComparable y en el mtodo CompareTo hace la comprobacin de que objeto es mayor o menor, si el de la propia clase o el indicado en el parmetro de esa funcin: ?!blic Class m2lea%o Im2lements ICom2arable 91 ?!blic Aombre As String ?!blic S!b AeJ(,-$al nombre As String) He/Aombre = nombre n% S!b 4 Si el ob#eto es %el ti2o m2lea%o, com2aramos los nombres/ 4 Si no es %el ti2o m2lea%o, %e5ol5emos !n cero 4 I!e signi8ica I!e los %os ob#etos son ig!ales/ ?!blic )!nction Com2are1o(,-$al ob# As 0b#ect) As Integer + Im2lements S-stem/ICom2arable/Com2are1o I8 1-2e08 ob# Is m2lea%o 1hen Dim e1 As m2lea%o = C1-2e(ob#, m2lea%o) "et!rn String/Com2are(He/Aombre, e1/Aombre) lse "et!rn 0 n% I8 n% )!nction n% Class En el mtodo CompareTo hacemos una comprobacin de que el objeto con el que debemos realizar la comparacin es del tipo Empleado, en ese caso convertimos el objeto pasado en uno del tipo Empleado y comparamos los nombres. Si el objeto que recibe el mtodo no es del tipo Empleado, devolvemos un cero, para indicar que no haga ninguna clasificacin, ya que ese valor indica que los dos objetos son iguales. Esta comparacin no es estrictamente necesaria, ya que si no indicamos el valor que debe devolver una funcin, devolver un valor cero, al menos en este caso, ya que el tipo a devolver es un nmero entero. Esta clase la podemos usar de esta forma: 4 Fna colecciSn %e %atos %el ti2o m2lea%o/ 92 Dim em2lea%os As AeJ S-stem/Collections/;eneric/'ist(08 m2lea%o) 4 ATa%imos 5arios em2lea%os a la colecciSn/ em2lea%os/A%%(AeJ m2lea%o("?e2e")) em2lea%os/A%%(AeJ m2lea%o(",ernar%o")) em2lea%os/A%%(AeJ m2lea%o("@!an")) em2lea%os/A%%(AeJ m2lea%o("Ana")) 4 Clasi8icamos los em2lea%os %e la colecciSn/ em2lea%os/Sort() 4 Hostramos los %atos !na 5e& clasi8ica%os/ )or ach e1 As m2lea%o In em2lea%os Console/<rite'ine(e1/Aombre) AeGt
Mane(o de e;cepciones En Visual Basic 2005 el tratamiento de errores (excepciones) ha cambiado con respecto a como lo hacemos en Visual Basic 6.0, ahora podemos usar un tratamiento de excepciones estructurado, de esta forma podemos detectar los errores que se produzcan en nuestras aplicaciones de una forma ms "ordenada". En Visual Basic 6.0 la nica forma de detectar errores es usando On %rror. Esta forma no estructurada de tratar los errores se sigue soportando en Visual Basic 2005, pero a todas luces no es la forma recomendada, si bien es cierto que debemos adaptar nuestra mente al formato estructurado, ya que en un principio nos parecer que no es tan efectivo como On %rror. En esta leccin veremos cmo tratar los errores de forma estructurada, ya que el "viejo" On %rror sigue funcionando de la misma forma que en VB6.
%aneo de e;cepciones no estructuradas Como hemos comentado, en Visual Basic 2005 tambin podemos usar el "viejo" sistema de tratamientos de errores, es decir, el "clsico" On %rror... que ahora se 93 llama tratamiento de errores no estructurados. La forma de utilizar On %rror es la misma que en Visual Basic 6.0, por tanto no vamos a entrar en detalles de cmo usar esta forma de interceptar errores, solo aclarar un par de cosas que debemos tener en cuenta: La primera es: intentar no usar esta forma de detectar errores, es preferible, aunque al principio cueste adaptarse, utilizar los errores estructurados. La segunda es que no podemos usar los dos sistemas al mismo tiempo, por lo menos en un mismo mtodo o propiedad, o utilizamos On %rror o utilizamos Try5Catc". De todas formas, si utilizamos el IDE (entorno integrado) de Visual Basic, ser el propio compilador el que nos avise cuando mezclemos las dos formas de detectar los errores.
%aneo de e;cepciones estructuradas Las excepciones en Visual Basic 2005 las podemos controlar usando las instrucciones Try 5 Catc" 5 )inally. Estas instrucciones realmente son bloques de instrucciones, al estilo de If 5 %lse. Cuando queramos controlar una parte del cdigo que puede producir un error lo incluimos dentro del bloque Try, si se produce un error, ste lo podemos detectar en el bloque Catc", por ltimo, independientemente de que se produzca o no una excepcin, podemos ejecutar el cdigo que incluyamos en el bloque )inally, para indicar el final del bloque de control de excepciones lo haremos con %nd Try. Cuando creamos una estructura de control de excepciones no estamos obligados a usar los tres bloques, aunque el primero: Try si es necesario, ya que es el que le indica al compilador que tenemos intencin de controlar los errores que se produzcan. Por tanto podemos crear un "manejador" de excepciones usando los tres bloques, usando Try y Catc" o usando Try y )inally. Veamos ahora con ms detalle cada uno de estos bloques y que es lo que podemos hacer en cada uno de ellos. #loque $ry En este bloque incluiremos el cdigo en el que queremos comprobar los errores. El cdigo a usar ser un cdigo normal, es decir, no tenemos que hacer nada en especial, ya que en el momento que se produzca el error se usar (si hay) el cdigo del bloque Catc". #loque Catc% Si se produce una excepcin, sta la capturamos en un bloque Catc". En el bloque Catc" podemos indicar que tipo de excepcin queremos capturar, para ello usaremos una variable de tipo %&ception, la cual pude ser del tipo de error especfico que queremos controlar o de un tipo genrico. 94 Por ejemplo, si sabemos que nuestro cdigo puede producir un error al trabajar con ficheros, podemos usar un cdigo como este: 1r- 4 cS%igo 2ara traba#ar con 8icheros, etc/ Catch eG As S-stem/I0/I0Gce2tion 4 el cS%igo a e#ec!tar c!an%o se 2ro%!&ca ese error n% 1r- Si nuestra intencin es capturar todos los errores que se produzcan, es decir, no queremos hacer un filtro con errores especficos, podemos usar la clase %&ception como tipo de excepcin a capturar. La clase %&ception es la ms genrica de todas las clases para manejo de excepciones, por tanto capturar todas las excepciones que se produzcan. 1r- 4 cS%igo I!e I!eremos controlar Catch eG As Gce2tion 4 el cS%igo a e#ec!tar c!an%o se 2ro%!&ca c!alI!ier error n% 1r- Aunque si no vamos usar la variable indicada en el bloque Catc", pero queremos que no se detenga la aplicacin cuando se produzca un error, podemos hacerlo de esta forma: 1r- 4 cS%igo I!e I!eremos controlar Catch 4 el cS%igo a e#ec!tar c!an%o se 2ro%!&ca c!alI!ier error n% 1r-
&arias capturas de errores en un mismo bloque $ry'Catc% En un mismo Try podemos capturar diferentes tipos de errores, para ello podemos incluir varios bloques Catc", cada uno de ellos con un tipo de excepcin diferente. Es importante tener en cuenta que cuando se produce un error y usamos varios bloques Catc", Visual Basic buscar la captura que mejor se adapte al error que se 95 ha producido, pero siempre lo har examinando los diferentes bloques Catc" que hayamos indicado empezando por el indicado despus del bloque Try, por tanto deberamos poner las ms genricas al final, de forma que siempre nos aseguremos de que las capturas de errores ms especficas se intercepten antes que las genricas. valuacin condicional en un bloque Catc% Adems de indicar la excepcin que queremos controlar, en un bloque Catc" podemos aadir la clusula 4"en para evaluar una expresin. Si la evaluacin de la expresin indicada despus de 4"en devuelve un valor verdadero, se procesar el bloque Catc", en caso de que devuelva un valor falso, se ignorar esa captura de error. Esto nos permite poder indicar varios bloques Catc" que detecten el mismo error, pero cada una de ellas pueden tener diferentes expresiones indicadas con 4"en. En el siguiente ejemplo, se evala el bloque Catc" solo cuando el valor de la variable y es cero, en otro caso se utilizar el que no tiene la clusula 4"en: Dim G, -, r As Integer 1r- G = CInt(Console/"ea%'ine()) - = CInt(Console/"ea%'ine()) r = G U - Console/<rite'ine("l res!lta%o esD =0>", r) Catch eG As Gce2tion <hen - = 0 Console/<rite'ine("Ao se 2!e%e %i5i%ir 2or cero/") Catch eG As Gce2tion Console/<rite'ine(eG/Hessage) n% 1r-
#loque (inally En este bloque podemos indicar las instrucciones que queremos que se ejecuten, se produzca o no una excepcin. De esta forma nos aseguramos de que siempre se ejecutar un cdigo, por ejemplo para liberar recursos, se haya producido un error o no.
96 Nota3 Hay que tener en cuenta de que incluso si usamos Exit Try para salir del bloque de control de errores, se ejecutar el cdigo indicado en el bloque Finally.
Captura de errores no controlados Como es lgico, si no controlamos las excepciones que se puedan producir en nuestras aplicaciones, estas sern inicialmente controladas por el propio runtime de .NET, en estos casos la aplicacin se detiene y se muestra el error al usuario. Pero esto es algo que no deberamos consentir, por tanto siempre deberamos detectar todos los errores que se produzcan en nuestras aplicaciones, pero a pesar de que lo intentemos, es muy probable que no siempre podamos conseguirlo. Por suerte, en Visual Basic 2005 tenemos dos formas de interceptar los errores no controlados: La primera es iniciando nuestra aplicacin dentro de un bloque Try5Catc", de esta forma, cuando se produzca el error, se capturar en el bloque Catc". La segunda forma de interceptar los errores no controlados es mediante el evento: ,n"andled%&ception, disponible por medio del objeto (y.*pplication.
Nota3 De los eventos nos ocuparemos en la siguiente leccin, pero como el evento UnhandledException est directamente relacionado con la captura de errores, lo mostramos en esta, aunque recomendamos al lector que esta seccin la vuelva a leer despus de ver todo lo relacionado con los eventos.
Este evento se "dispara" cuando se produce un error que no hemos interceptado, por tanto podramos usarlo para prevenir que nuestra aplicacin se detenga o bien para guardar en un fichero .lo' la causa de dicho error para posteriormente actualizar el cdigo y prevenirlo. Ya que cuando se produce el evento ,n"andled%&ception, podemos averiguar el error que se ha producido e incluso evitar que la aplicacin finalice. Esa informacin la obtenemos mediante propiedades expuestas por el segundo parmetro del evento, en particular la propiedad %&ception nos indicar el error que se ha producido y por medio de la propiedad %&it*pplication podemos indicar si terminamos o no la aplicacin.
Nota3 Cuando ejecutamos una aplicacin desde el IDE (entorno de 97 desarrollo), los errores no controlados siempre se producen, independientemente de que tengamos o no definida la captura de errores desde el evento UnhandledException. Ese evento solo se producir cuando ejecutemos la aplicacin fuera del IDE de Visual Basic.
Introduccin La forma que tienen nuestras clases y estructuras de comunicar que algo est ocurriendo, es por medio de eventos. Los eventos no nos son desconocidos a los desarrolladores de Visual Basic 6.0, ya que tambin existen y podemos definir en ese lenguaje. En Visual Basic 2005 se siguen usando de la misma forma que en VB6, aunque seguramente siempre que hemos ledo sobre el tema aparece la palabra delegado. Y es que, aunque VB2005 nos oculte, o facilite, el trabajo con los eventos, stos siempre estn relacionados con los delegados. En esta leccin veremos que son los delegados y que relacin tienen con los eventos, tambin veremos que podemos tener mayor control sobre cmo se interceptan los eventos e incluso cmo y cuando se asocian los eventos en la aplicacin cliente, aunque primero empezaremos viendo cmo declarar y utilizar eventos en nuestros tipos de datos.
Eventos % dele'ados Eventos o Interceptar los eventos de los controles de un formulario Interceptar eventos en Visual Basic 6.0 Interceptar eventos en Visual Basic 2005 o Asociar un evento con un control o Formas de asociar los eventos con un control 1- Asociar el evento manualmente por medio de Handles 2- Asociar el evento desde la ventana de cdigo 3- Asociar el evento desde el diseador de formularios o Asociar varios eventos a un mismo procedimiento o Declarar una variable para asociar eventos con Handles "efinir y producir eventos en una clase o Definir eventos en una clase o Producir un evento en nuestra clase o Otra forma de asociar los eventos de una clase con un mtodo Asociar eventos mediante AddHandler Desasociar eventos mediante RemoveHandler "elegados o Qu ocurre cuando se asigna y se produce un evento? o Qu papel juegan los delegados en todo este proceso? 98 o Definicin "formal" de delegado o Utilizar un delegado para acceder a un mtodo "efinir un evento bien informado
Introduccin La forma que tienen nuestras clases y estructuras de comunicar que algo est ocurriendo, es por medio de eventos. Los eventos no nos son desconocidos a los desarrolladores de Visual Basic 6.0, ya que tambin existen y podemos definir en ese lenguaje. En Visual Basic 2005 se siguen usando de la misma forma que en VB6, aunque seguramente siempre que hemos ledo sobre el tema aparece la palabra delegado. Y es que, aunque VB2005 nos oculte, o facilite, el trabajo con los eventos, stos siempre estn relacionados con los delegados. En esta leccin veremos que son los delegados y que relacin tienen con los eventos, tambin veremos que podemos tener mayor control sobre cmo se interceptan los eventos e incluso cmo y cuando se asocian los eventos en la aplicacin cliente, aunque primero empezaremos viendo cmo declarar y utilizar eventos en nuestros tipos de datos.
Eventos % dele'ados Eventos o Interceptar los eventos de los controles de un formulario Interceptar eventos en Visual Basic 6.0 Interceptar eventos en Visual Basic 2005 o Asociar un evento con un control o Formas de asociar los eventos con un control 1- Asociar el evento manualmente por medio de Handles 2- Asociar el evento desde la ventana de cdigo 3- Asociar el evento desde el diseador de formularios o Asociar varios eventos a un mismo procedimiento o Declarar una variable para asociar eventos con Handles "efinir y producir eventos en una clase o Definir eventos en una clase o Producir un evento en nuestra clase o Otra forma de asociar los eventos de una clase con un mtodo Asociar eventos mediante AddHandler Desasociar eventos mediante RemoveHandler "elegados o Qu ocurre cuando se asigna y se produce un evento? o Qu papel juegan los delegados en todo este proceso? o Definicin "formal" de delegado 99 o Utilizar un delegado para acceder a un mtodo "efinir un evento bien informado
Eventos En Visual Basic 2005 podemos usar los eventos de la misma forma que actualmente lo hacemos en Visual Basic 6.0, al menos en lo que se refiere a la forma de declararlos en nuestras clases y cmo "lanzarlos", ya que para interceptarlos en una aplicacin esa forma ha cambiado un poco, pero como veremos, incluso puede ser ms fcil definir los mtodos o procedimientos que utilizamos para interceptarlos.
-nterceptar los eventos de los controles de un formulario Debido a que an no hemos visto el mdulo dedicado a las aplicaciones de Windows, en las que se utilizan los "clsicos" formularios, no vamos a entrar en detalles sobre cmo crear un formulario ni como aadir controles, etc., todo eso lo veremos en el siguiente mdulo. Para simplificar las cosas, veremos simplemente la diferencia de cmo se interceptan en un formulario de Visual Basic 6.0 y en otro de la nueva versin de Visual Basic, pero como comentamos, sin entrar en demasiados detalles. Tanto en una versin como en otra de Visual Basic, la forma ms sencilla de asociar el evento de un control con el cdigo que se usar, es haciendo doble-click en el control; por ejemplo, si en nuestro formulario tenemos un botn, al hacer doble pulsacin sobre l tendremos asociado el evento Clic6 del botn.
Interceptar eventos en &isual #asic )*+ En Visual Basic 6.0 el procedimiento que se crear tendr el siguiente aspecto: ?ri5ate S!b Comman%1+ClicQ()
n% S!b Es decir, la "liga" entre un evento y el cdigo se indica usando la siguiente nomenclatura: <Nombre del control> <guin bajo> <nombre del evento> De esta forma, el compilador sabe que asociamos ese evento del control al cdigo indicado, de forma que cada vez que el usuario haga click sobre el botn, se ejecutar dicho cdigo. 100
Interceptar eventos en &isual #asic ,++- En Visual Basic 2005 aunque la forma de asignar los eventos predeterminados de los controles es la misma, es decir, haciendo doble pulsacin en el control, la declaracin del cdigo usado para interceptar el evento difiere un poco de VB6, tal como podemos apreciar en el siguiente cdigo: ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As 0b#ect, ,-$al e As 5entArgs) + Han%les ,!tton1/ClicQ n% S!b Lo primero que podemos notar es que en Visual Basic 2005 utiliza dos argumentos, esto siempre es as en todos los eventos producidos por los controles. El primero indica el control que produce el evento, (en nuestro ejemplo sera una referencia al control Button1), y el segundo normalmente contiene informacin sobre el evento que se produce, si el evento en cuestin no proporciona informacin extra, como es el caso del evento Clic6, ese parmetro ser del tipo %ent*rgs. Sin embargo en otros eventos, por ejemplo, los relacionados con el ratn (mouse), el segundo argumento tendr informacin que nos puede resultar til, por ejemplo para saber que botn se ha usado o cual es la posicin del cursor, en la figura 1.04.1 podemos ver las propiedades relacionadas con el evento (ouse%ent*rgs: Fi'ura $.0=.0$ 8ropiedades relacionadas con un evento del ratn Como sabemos, en VB6, esa informacin se da usando argumentos extras en el mtodo del evento, tal como podemos ver en el siguiente cdigo del evento (ouse(oe de un botn llamado -ommand$: ?ri5ate S!b Comman%1+Ho!seHo5e(,!tton As Integer, Shi8t As Integer, L As Single, M As Single) 101 n% S!b
Asociar un evento con un control Siguiendo con el cdigo que intercepta el evento Clic6 de un botn, podemos apreciar que el IDE de Visual Basic 2005 aade al final de la declaracin del procedimiento de evento la instruccin 7andles seguida del control y el evento que queremos interceptar: 5andles Button$.-lic!, esta la forma habitual de hacerlo en VB2005, ya que, a diferencia de VB6, aqu no hay nombres "mgicos" para asociar un evento con un control o incluso con el propio formulario, sino que esa asociacin se hace de forma explcita y la forma que tiene Visual Basic 2005 de hacerlo es usando la clusula 7andles.
Nota3 En Visual Basic 2005 el nombre del procedimiento de evento no est relacionado con el evento, a diferencia de Visual Basic 6.0, en el que si que hay una relacin directa entre el evento a interceptar y el nombre del procedimiento. Aunque por defecto, el nombre usado sera el equivalente al de VB6, en la nueva versin puede tener cualquier nombre.
Tal como resaltamos en la nota anterior, en Visual Basic 2005, el nombre asociado a un evento puede ser el que queramos, lo realmente importante es que indiquemos la instruccin 7andles seguida del evento que queremos interceptar.
<ormas de asociar los eventos con un control Cuando estamos trabajando con el diseador de formularios, tenemos tres formas de asociar un evento con el cdigo correspondiente: La forma ms sencilla es la expuesta anteriormente, es decir, haciendo doble click en el control, esto har que se muestre el evento predeterminado del control. En el caso del control Button, el evento predeterminado es el evento Clic6. Si queremos escribir cdigo para otros eventos podemos hacerlo de tres formas, aunque la primera que explicaremos no ser la ms habitual, ya que debemos saber exactamente qu parmetros utiliza el evento. 102
./ Asociar el evento manualmente por medio de 0andles Con esta forma, simplemente escribimos el nombre del procedimiento (puede ser cualquier nombre), indicamos los dos argumentos que recibe: el primero siempre es de tipo Object y el segundo depender del tipo de evento, y finalmente por medio de la clusula 7andles lo asociamos con el control y el evento en cuestin.
,/ Asociar el evento desde la ventana de cdi"o Al igual que en VB6, en Visual Basic 2005 tambin podemos seleccionar los eventos disponibles de una lista desplegable. Esto lo haremos desde la ventana de cdigo. En la parte superior derecha tenemos una la lista con los controles que hemos aadido al formulario, seleccionamos el control que nos interese y en la lista que hay a su izquierda tenemos los eventos que ese control produce. Por tanto podemos seleccionar de esa lista de eventos el que nos interese interceptar, tal como podemos ver en la figura 1.04.02 Fi'ura $.0=.02 *ista de eventos de un control De esta forma el propio IDE ser el que cree el "esqueleto" del procedimiento de evento usando los parmetros adecuados.
1/ Asociar el evento desde el dise2ador de formularios La tercera forma de asociar un evento con un control, es hacerlo desde el diseador de formularios. En la ventana de propiedades del control, (tal como podemos apreciar en la figura 1.04.3), tenemos una lista con los eventos ms importantes de cada control, seleccionando el evento de esa lista y haciendo doble-click en el que nos interese, conseguiremos exactamente el mismo resultado que con el paso anterior. 103 Fi'ura $.0=.0< Ventana de propiedades con los eventos del control seleccionado
Asociar varios eventos a un mismo procedimiento Cuando utilizamos 7andles para asociar eventos, podemos indicar que un mismo procedimiento sirva para interceptar varios eventos. Veamos un caso prctico en el que tenemos varios controles de tipo Te&tBo& y queremos que cuando reciban el foco siempre se ejecute el mismo cdigo, por ejemplo, que se seleccione todo el texto que contiene, podemos hacerlo indicando despus de la clusula 7andles todos los controles que queremos asociar con ese procedimiento. En el siguiente cdigo indicamos tres controles Te&tBo&: ?ri5ate S!b 1eGt,oG1+nter( + ,-$al sen%er As S-stem/0b#ect, + ,-$al e As S-stem/5entArgs) + Han%les 1eGt,oG1/nter, 1eGt,oG2/nter, 1eGt,oG3/nter n% S!b Esta asociacin la podemos hacer manualmente, simplemente indicando en la clusula 7andles cada uno de los eventos a continuacin del anterior separndolos por comas. O bien desde el diseador de formularios. En este segundo caso, cuando seleccionamos un control y desde la ventana de propiedades, seleccionamos un 104 evento, nos muestra los procedimientos que tenemos definidos en nuestro cdigo que utilizan los mismos parmetros que el evento en cuestin, si seleccionamos uno de esos procedimientos, el propio IDE aadir ese control a la lista 7andles. Como vemos en la figura 1.04.04 podemos usar el procedimiento Te;tBo;$MEnter para asociarlo al evento %nter del control Te;tBo;2: Fi'ura $.0=.0= *ista de procedimientos Acompati#lesA con un evento
"eclarar una variable para asociar eventos con =andles Para que podamos usar la instruccin 7andles para asociar manualmente un procedimiento con un evento, o para que el diseador de Visual Basic 2005 pueda hacerlo, la variable del control o clase que tiene los eventos que queremos interceptar tenemos que declararla con la instruccin 4it"%ents. Esto es exactamente lo mismo que en Visual Basic 6.0, lo que ocurre es que cuando trabajamos con formularios desde VB6, no nos tenemos que preocupar de este detalle, en Visual Basic 2005 tampoco debemos preocuparnos, ya que el propio diseador de formularios lo hace por nosotros. Aunque a diferencia de VB6, hay que hacerlo de forma explcita, ya que en la versin anterior de Visual Basic no tenamos ningn control a como se definan o declaraban los controles a usar en nuestro formulario, mientras que en esta nueva versin siempre tendremos acceso a esa informacin. No vamos a entrar en ms detalles, simplemente mostraremos cmo se declara el control Button1 para que exponga los eventos: )rien% <ith5ents ,!tton1 As S-stem/<in%oJs/)orms/,!tton Si en lugar de estar trabajando con formularios y controles, lo hacemos con clases "normales", la forma de declarar una variable que tiene eventos es por medio de la instruccin 4it"%ents. Por ejemplo, en esta declaracin indicamos que tenemos intencin de usar los eventos que la clase Empleado exponga: 105 ?ri5ate <ith5ents !nm2lea%o As m2lea%o Y posteriormente podremos definir los mtodos de eventos usando la instruccin 7andles: ?ri5ate S!b !nm2lea%o+DatosCambia%os() Han%les !nm2lea%o/DatosCambia%os n% S!b
Nota3 Usar WithEvents y Handles es la forma ms sencilla de declarar y usar una variable que accede a una clase que produce eventos, pero no es la nica forma que tenemos de hacerlo en Visual Basic 2005, tal como tendremos oportunidad de comprobar.
/efinir % producir eventos en una clase Como comentbamos anteriormente, la forma de definir un evento en una clase de Visual Basic 2005 es idntica a como lo hacemos actualmente con VB6, lo mismo ocurre cuando queremos "lanzar" o producir el evento: en Visual Basic 2005 se hace igual que en Visual Basic 6.0.
"efinir eventos en una clase Para definir un evento en una clase usamos la instruccin %ent seguida del nombre del evento y opcionalmente indicamos los argumentos que dicho evento recibir. En el siguiente trozo de cdigo definimos un evento llamado /atosModificados que no utiliza ningn argumento: ?!blic 5ent DatosHo%i8ica%os() Esto es todo lo que necesitamos hacer en Visual Basic 2005 para definir un evento, que, como podemos comprobar, es exactamente igual que en VB6.
106 ,roducir un evento en nuestra clase Para producir un evento en nuestra clase, y de esta forma notificar a quin quiera interceptarlo, simplemente usaremos la instruccin 'aise%ent seguida del evento que queremos producir. Esto tampoco ha cambiado en Visual Basic 2005 con respecto a VB6. Incluso cuando escribimos esa instruccin en el IDE, nos mostrar los distintos eventos que podemos producir, tal como vemos en la figura 1.04.05: Fi'ura $.0=.05 *ista de eventos &ue podemos producir
Esta es la forma ms sencilla de definir y lanzar eventos en Visual Basic 2005, que como podemos comprobar es exactamente igual que en Visual Basic 6.0.
$tra forma de asociar los eventos de una clase con un m0todo Tal como hemos estado comentando, la forma ms sencilla de declarar una variable para interceptar eventos es declarndola usando 4it"%ents y para interceptar los eventos lo hacemos por medio de la instruccin 7andles. Esta forma, es a todas luces la ms recomendada, no solo por la facilidad de hacerlo, sino porque tambin tenemos la ventaja de que todas las variables declaradas con 4it"%ents se muestran en la lista desplegable de la ventan de cdigo, tal como podemos apreciar en la figura 1.04.06: Fi'ura $.0=.0" *ista de o#(etos &ue producen eventos Y de esta forma podemos seleccionar la variable y posteriormente elegir el evento a interceptar, tal como vimos en la figura 1.04.02.
107 Asociar eventos mediante Add0andler Pero Visual Basic 2005 tambin proporciona otra forma de asociar un procedimiento con un evento. Aunque en este caso es algo ms manual que todo lo que hemos visto y, de alguna forma est ms ligado con los delegados, y como los delegados los veremos dentro de poco, ahora solamente mostraremos la forma de hacerlo y despus veremos con algo de ms detalle cmo funciona. La forma de de asociar eventos con su correspondiente mtodo es por medio de la instruccin *dd7andler. A esta instruccin le pasamos dos argumentos, el primero es el evento a asociar y el segundo es el procedimiento que usaremos cuando se produzca dicho evento. Este ltimo parmetro tendremos que indicarlo mediante la instruccin *ddressOf, que al igual que en VB6 sirve para pasar una referencia a una funcin o procedimiento, y precisamente eso es lo que queremos hacer: indicarle que procedimiento debe usar cuando se produzca el evento: A%%Han%ler !nm2lea%o/DatosCambia%os, A%%ress08 !nm2lea%o+DatosCambia%os En este caso, el uso de *ddressOf es una forma "fcil" que tiene Visual Basic 2005 de asociar un procedimiento de evento con el evento. Aunque por el fondo, (y sin que nos enteremos), realmente lo que estamos usando es un constructor a un delegado. La ventaja de usar esta forma de asociar eventos con el procedimiento, es que podemos hacerlo con variables que no estn declaradas con 4it"%ents, realmente esta sera la nica forma de asociar un procedimiento de evento con una variable que no hemos declarado con 4it"%ents.
Desasociar eventos mediante 3emove0andler De la misma forma que por medio de *dd7andler podemos asociar un procedimiento con un evento, usando la instruccin 'emoe7andler podemos hacer el proceso contrario: desligar un procedimiento del evento al que previamente estaba asociado. Los parmetros a usar con 'emoe7andler son los mismos que con *dd7andler. Podemos usar 'emoe7andler tanto con variables y eventos definidos con *dd7andler como con variables declaradas con 4it"%ents y ligadas por medio de 7andles. Esto ltimo es as porque cuando nosotros definimos los procedimientos de eventos usando la instruccin 7andles, es el propio Visual Basic el que internamente utiliza *dd7andler para ligar ese procedimiento con el evento en cuestin. Saber esto nos facilitar comprender mejor cmo funciona la declaracin de eventos mediante la instruccin Custom, aunque de este detalle nos ocuparemos despus de ver que son los delegados.
108 /ele'ados Como hemos comentado anteriormente, los eventos son acciones que una clase puede producir cuando ocurre algo. De esta forma podemos notificar a las aplicaciones que hayan decidido interceptar esos mensajes para que tomen las acciones que crean conveniente. Visual Basic 2005 esconde al desarrollador prcticamente todo lo que ocurre cada vez que decidimos interceptar un evento, nosotros solo vemos una pequea parte de todo el trabajo que en realidad se produce, y el que no lo veamos no quiere decir que no est ocurriendo nada. Tambin es cierto que no debe preocuparnos demasiado si no sabemos lo que est pasando, pero si tenemos conciencia de que es lo que ocurre, puede que nos ayude a comprender mejor todo lo relacionado con los eventos.
78u0 ocurre cuando se asigna y se produce un evento9 Intentemos ver de forma sencilla lo que ocurre "por dentro" cada vez que definimos un mtodo que intercepta un evento y cmo hace el Visual Basic para comunicarse con el receptor de dicho evento. 1. Cuando Visual Basic se encuentra con el cdigo que le indica que un mtodo debe interceptar un evento, ya sea mediante *dd7andler o mediante el uso de 7andles, lo que hace es aadir la direccin de memoria de ese mtodo a una especie de array. En la figura 1.04.07 podemos ver un diagrama en el que un mismo evento lo interceptan tres clientes, cuando decimos que un cliente intercepta un evento, realmente nos referimos a que hay un mtodo que lo intercepta y el evento realmente guarda la direccin de memoria de ese mtodo. 109 Fi'ura $.0=.0D El evento 'uarda la direccin de memoria de cada mHtodo &ue lo intercepta 2. Cuando usamos la instruccin 'aise%ent para producir el evento, se examina esa lista de direcciones y se manda el mensaje a cada uno de los mtodos que tenemos en el "array". En este caso, lo que realmente ocurre es que se hace una llamada a cada uno de los mtodos, de forma que se ejecute el cdigo al que tenemos acceso mediante la direccin de memoria almacenada en la lista. 3. Cuando usamos la instruccin 'emoe7andler, le estamos indicando al evento que elimine de la lista el mtodo indicado en esa instruccin, de esta forma, la prxima vez que se produzca el evento, solo se llamar a los mtodos que actualmente estn en la lista. Tanto el agregar nuevos mtodos a esa lista como quitarlos, lo podemos hacer en tiempo de ejecucin, por medio de *dd7andler y 'emoe7andler respectivamente. Ya que la instruccin 7andles solo la podemos usar en tiempo de diseo. Es ms, podemos incluso indicar que un mismo evento procese ms de un mtodo en una misma aplicacin o que un mismo mtodo sea llamado por ms de un evento. Ya que lo que realmente necesita cada evento es que exista un mtodo que tenga una "firma" concreta: la indicada al declarar el evento.
78u0 papel uegan los delegados en todo este proceso9 Veamos primero que papel tienen los delegados en todo este proceso y despus veremos con ms detalle lo que "realmente" es un delegado. 1. Cuando definimos un evento, realmente estamos definiendo un delegado, (que en el fondo es una clase con un tratamiento especial), y un mtodo del mismo tipo que el delegado 2. Cuando indicamos que un mtodo intercepte un evento, realmente estamos llamando al constructor del delegado, al que le pasamos la direccin de memoria del mtodo. El delegado almacena cada una de esas direcciones de memoria para posteriormente usarlas 3. Cuando se produce el evento, (por medio de 'aise%ent), realmente estamos llamando al delegado para que acceda a todas las "direcciones" de memoria que tiene almacenadas y ejecute el cdigo que hayamos definido en cada uno de esos mtodos Como podemos comprobar, y para decirlo de forma simple, un delegado realmente es la forma que tiene .NET para definir un puntero. La diferencia principal es que los punteros, (no vamos a entrar en demasiados detalles sobre los punteros, ya que no estamos en un curso de C/C++), no tienen forma de comprobar si estn accediendo a una direccin de memoria correcta o, para decirlo de otra forma, a una direccin de memoria "adecuada". En .NET, los "punteros" solo se pueden usar mediante delegados, y stos solamente pueden acceder a direcciones de memoria que tienen la misma "firma" con el que se han definido. Para que lo entendamos un poco mejor, es como si los delegados solo pudieran acceder a sitios en la memoria que 110 contienen un mtodo con la misma "interfaz" que el que ha definido el propio delegado. Seguramente es difcil de entender, y la principal razn es que hemos empezado la casa por el techo. Por tanto, veamos a continuacin una definicin "formal" de qu es un delegado y veamos varios ejemplos para que lo comprendamos mejor.
"efinici(n >formal> de delegado Veamos que nos dice la documentacin de Visual Basic 2005 sobre los delegados: 8,n delegado es una clase 9ue puede contener una referencia a un m:todo. * diferencia de otras clases3 los delegados tienen un prototipo /firma0 y pueden guardar referencias ;nicamente a los m:todos 9ue coinciden con su prototipo.8 Esta definicin, al menos en lo que respecta a su relacin con los eventos, viene a decir que los delegados definen la forma en que debemos declarar los mtodos que queramos usar para interceptar un evento. Por ejemplo, el evento /atos-am#iados definido anteriormente, tambin lo podramos definir de la siguiente forma: ?!blic Delegate S!b DatosCambia%os5entHan%ler() ?!blic 5ent DatosCambia%os As DatosCambia%os5entHan%ler Es decir, el mtodo que intercepte este evento debe ser del tipo Sub y no recibir ningn parmetro. Si nuestro evento utiliza, por ejemplo, un parmetro de tipo String, la definicin del delegado quedara de la siguiente forma: ?!blic Delegate S!b AombreCambia%o5entHan%ler(,-$al n!e5oAombre As String) Y la definicin del evento quedara de esta otra: ?!blic 5ent AombreCambia%o As AombreCambia%o5entHan%ler Como vemos al definir el evento ya no tenemos que indicar si recibe o no algn parmetro, ya que esa definicin la hemos hecho en el delegado. 111 Si nos decidimos a definir este evento de la forma "normal" de Visual Basic, lo haramos as: ?!blic 5ent AombreCambia%o(,-$al n!e5oAombre As String) Como podemos comprobar, Visual Basic 2005 nos permite definir los eventos de dos formas distintas: definiendo un delegado y un evento que sea del tipo de ese delegado o definiendo el evento con los argumentos que debemos usar. Declaremos como declaremos los eventos, los podemos seguir usando de la misma forma, tanto para producirlo mediante 'aise%ent como para definir el mtodo que reciba ese evento.
:tili*ar un delegado para acceder a un m0todo Ahora veamos brevemente cmo usar los delegados, en este caso sin necesidad de que defina un evento. Como hemos comentado, un delegado realmente es una clase que puede contener una referencia a un mtodo, adems define el prototipo del mtodo que podemos usar como referencia. Sabiendo esto, podemos declarar una variable del tipo del delegado y por medio de esa variable acceder al mtodo que indiquemos, siempre que ese mtodo tenga la misma "firma" que el delegado. Parece complicado verdad? Y no solo lo parece, es que realmente lo es. Comprobemos esta "complicacin" por medio de un ejemplo. En este cdigo, que iremos mostrando poco a poco, vamos a definir un delegado, un mtodo con la misma firma para que podamos usarlo desde una variable definida con el mismo tipo del delegado. Definimos un delegado de tipo Sub que recibe un valor de tipo cadena: Delegate S!b Sal!%o(,-$al nombre As String) Definimos un mtodo con la misma firma del delegado: ?ri5ate S!b mostrarSal!%o(,-$al elAombre As String) Console/<rite'ine("Hola, " P elAombre) n% S!b Ahora vamos a declarar una variable para que acceda a ese mtodo. Para ello debemos declararla con el mismo tipo del delegado: Dim sal!%an%o As Sal!%o La variable saludando es del mismo tipo que el delegado Saludo. La cuestin es cmo o que asignamos a esta variable? 112 Primer intento: Como hemos comentado, los delegados realmente son clases, por tanto podemos usar Ne Saludo y, segn parece, deberamos pasarle un nombre como argumento. Algo as: sal!%an%o = AeJ Sal!%o("?e2e") Pero esto no funciona, entre otras cosas, porque hemos comentado que un delegado contiene (o puede contener) una referencia a un mtodo, y A8epeA no es un mtodo ni una referencia a un mtodo. Segundo intento: Por lgica y, sobre todo, por sentido comn, mxime cuando hemos declarado un mtodo con la misma "firma" que el delegado, deberamos pensar que lo que debemos pasar a esa variable es el mtodo, ya que un delegado puede contener una referencia a un mtodo. sal!%an%o = AeJ Sal!%o(mostrarSal!%o) Esto tampoco funciona, seguramente porque le falta el parmetro? sal!%an%o = AeJ Sal!%o(mostrarSal!%o("?e2e")) Pues tampoco. Para usar un delegado debemos indicarle la direccin de memoria de un mtodo, a eso se refiere la definicin que vimos antes, una referencia a un mtodo no es ni ms ni menos que la direccin de memoria de ese mtodo. Y en Visual Basic, desde la versin 5.0, tenemos una instruccin para obtener la direccin de memoria de cualquier mtodo: *ddressOf. Por tanto, para indicarle al delegado dnde est ese mtodo tendremos que usar cualquiera de estas dos formas: sal!%an%o = AeJ Sal!%o(A%%ress08 mostrarSal!%o) Es decir, le pasamos al constructor la direccin de memoria del mtodo que queremos "asociar" al delegado. En Visual Basic esa misma asignacin la podemos simplificar de esta forma: sal!%an%o = A%%ress08 mostrarSal!%o Ya que el compilador "sabe" que saludando es una variable de tipo delegado y lo que esa variable puede contener es una referencia a un mtodo que tenga la misma firma que la definicin del delegado, en nuestro caso, que sea de tipo Sub y reciba una cadena. 113 Si queremos, tambin podemos declarar la variable y asignarle directamente el mtodo al que har referencia: Dim sal!%an%o As AeJ Sal!%o(A%%ress08 mostrarSal!%o) Y ahora... cmo podemos usar esa variable? La variable saludando realmente est apuntando a un mtodo y ese mtodo recibe un valor de tipo cadena, por tanto si queremos llamar a ese mtodo (para que se ejecute el cdigo que contiene), tendremos que indicarle el valor del argumento, sabiendo esto, la llamada podra ser de esta forma: sal!%an%o("?e2e") Y efectivamente, as se mostrara por la consola el saludo (Hola) y el valor indicado como argumento. Realmente lo que hacemos con esa llamada es acceder al mtodo al que apunta la variable y como ese mtodo recibe un argumento, debemos pasrselo, en cuanto lo hacemos, el runtime de .NET se encarga de localizar el mtodo y pasarle el argumento, de forma que se ejecute de la misma forma que si lo llamsemos directamente: mostrarSal!%o("?e2e") Con la diferencia de que la variable "saludando" no tiene porqu saber a qu mtodo est llamando, y lo ms importante, no sabe dnde est definido ese mtodo, solo sabe que el mtodo recibe un parmetro de tipo cadena y aparte de esa informacin, no tiene porqu saber nada ms.
As es como funcionan los eventos, un evento solo tiene la direccin de memoria de un mtodo, ese mtodo recibe los mismos parmetros que los definidos por el evento (realmente por el delegado), cuando producimos el evento con 'aise%ent es como si llamramos a cada uno de los mtodos que se han ido agregando al delegado, si es que se ha agregado alguno, ya que en caso de que no haya ningn mtodo asociado a ese evento, ste no se producir, por la sencilla razn de que no habr ningn cdigo al que llamar.
Realmente es complicado y, salvo que lo necesitemos para casos especiales, no ser muy habitual que usemos los delegados de esta forma, aunque no est de ms saberlo, sobre todo si de as comprendemos mejor cmo funcionan los eventos.
114 /efinir un evento #ien informado Para terminar con esta leccin sobre los eventos y los delegados, vamos a ver otra forma de definir un evento. Esta es exclusiva de Visual Basic 2005 y por medio de esta declaracin, tal como indicamos en el ttulo de la seccin, tendremos mayor informacin sobre cmo se declara el evento, cmo se destruye e incluso cmo se produce, es lo que la documentacin de Visual Basic llama evento personalizado (Custom %ent). Cuando declaramos un evento usando la instruccin Custom estamos definiendo tres bloques de cdigo que nos permite interceptar el momento en que se produce cualquiera de las tres acciones posibles con un evento: 1. Cuando se "liga" el evento con un mtodo, ya sea por medio de *dd7andler o mediante 7andles 2. Cuando se desliga el evento de un mtodo, por medio de 'emoe7andler 3. Cuando se produce el evento, al llamar a 'aise%ent Para declarar este tipo de evento, siempre debemos hacerlo por medio de un delegado. Veamos un ejemplo de una declaracin de un evento usando Custom %ent: ?!blic Delegate S!b A2elli%osCambia%os5entHan%ler(,-$al n!e5o As String) ?!blic C!stom 5ent A2elli%osCambia%os As A2elli%osCambia%os5entHan%ler A%%Han%ler(,-$al 5al!e As A2elli%osCambia%os5entHan%ler) 4 este bloI!e se e#ec!tar6 ca%a 5e& I!e asignemos !n mRto%o al e5ento n% A%%Han%ler
"emo5eHan%ler(,-$al 5al!e As A2elli%osCambia%os5entHan%ler) 4 este bloI!e se e#ec!tar6 c!an%o se %eslig!e el e5ento %e !n mRto%o n% "emo5eHan%ler
"aise5ent(,-$al n!e5o As String) 4 este bloI!e se e#ec!tar6 ca%a 5e& I!e se 2ro%!&ca el e5ento 115 n% "aise5ent n% 5ent Como podemos apreciar, debemos definir un delegado con la "firma" del mtodo a usar con el evento. Despus definimos el evento por medio de las instrucciones Custom %ent, utilizando el mismo formato que al definir un evento con un delegado. Dentro de la definicin tenemos tres bloques, cada uno de los cuales realizar la accin que ya hemos indicado en la lista numerada.
Nota3 Los eventos Custom Event solamente podemos definirlos e interceptarlos en el mismo ensamblado.
Introduccin Esta es la definicin que nos da la ayuda de Visual Basic sobre lo que es un atributo. Los atributos son eti9uetas descriptias 9ue proporcionan informaci<n adicional sobre elementos de programaci<n como tipos3 campos3 m:todos y propiedades. Otras aplicaciones3 como el compilador de #isual Basic3 pueden "acer referencia a la informaci<n adicional en atributos para determinar c<mo pueden utili.arse estos elementos. En esta leccin veremos algunos ejemplos de cmo usarlos en nuestras propias aplicaciones y, aunque sea de forma general, cmo usar y aplicar algunos de los atributos definidos en el propio .NET Framework, al menos los que ms directamente nos pueden interesar a los desarrolladores de Visual Basic.
4tri#utos Atributos o Atributos para representar informacin de nuestra aplicacin Mostrar los ficheros ocultos del proyecto o Tipos de atributos que podemos usar en una aplicacin Atributos globales a la aplicacin Atributos particulares a las clases o miembros de las clases o Atributos personalizados Acceder a los atributos personalizados en tiempo de ejecucin o Atributos especficos de Visual Basic o Marcar ciertos miembros de una clase como obsoletos 116
4tri#utos Como hemos comentado en la introduccin, los atributos son etiquetas que podemos aplicar a nuestro cdigo para que el compilador y, por extensin, el propio .NET Framework los pueda usar para realizar ciertas tareas o para obtener informacin extra sobre nuestro cdigo. De hecho en cualquier aplicacin que creemos con Visual Basic 2005 estaremos tratando con atributos, aunque nosotros ni nos enteremos, ya que el propio compilador los utiliza para generar los metadatos del ensamblado, es decir, la informacin sobre todo lo que contiene el ejecutable o librera que hemos creado con Visual Basic 2005. Por otra parte, el uso de los atributos nos sirve para ofrecer cierta funcionalidad extra a nuestro cdigo, por ejemplo, cuando creamos nuestros propios controles, mediante atributos podemos indicarle al diseador de formularios si debe mostrar ciertos miembros del control en la ventana de propiedades, etc.
Atributos para representar informaci(n de nuestra aplicaci(n De forma ms genrica podemos usar los atributos para indicar ciertas caractersticas de nuestra aplicacin, por ejemplo, el ttulo, la versin, etc. Todos estos atributos los indicaremos como "caractersticas" de nuestra aplicacin, y lo haremos sin ser demasiados conscientes de que realmente estamos usando atributos, ya que el propio Visual Basic los controla mediante propiedades de la aplicacin. Por ejemplo, si mostramos la ventana de propiedades de nuestro proyecto, ver figura 1.05.01: 117 Fi'ura $.05.0$ 8ropiedades de la aplicacin Tendremos acceso a las propiedades de la aplicacin, como el nombre del ensamblado, el espacio de nombres, etc. Si queremos agregar informacin extra, como la versin, el copyright, etc. podemos pulsar en el botn "Assembly Information", al hacerlo, se mostrar una nueva ventana en la que podemos escribir esa informacin, tal como mostramos en la figura 1.05.02: 118 Fi'ura $.05.02 Informacin del ensam#lado Esa informacin realmente est definida en un fichero del proyecto llamado AssembluInfo.vb, el cual de forma predeterminada est oculto, si lo mostramos, veremos que esa informacin la contiene en formato de atributos. Parte del cdigo de ese fichero lo podemos ver en la figura 1.05.03: 119 Fi'ura $.05.0< -ontenido del fic?ero 4ssem#l%Info En este cdigo podemos resaltar tres cosas: La primera es que tenemos una importacin al espacio de nombres S%stem.Beflection, este espacio de nombres contiene la definicin de las clases/atributos utilizados para indicar los atributos de la aplicacin, como el ttulo, etc. La segunda es la forma de usar los atributos, estos deben ir encerrados entre signos de menor y mayor: >4ssem#l%3 -omVisi#le9False:6. La tercera es que, en este caso, los atributos estn definidos a nivel de ensamblado, para ellos se aade la instruccin 4ssem#l%3 al atributo. Como veremos a continuacin, los atributos tambin pueden definirse a nivel local, es decir, solo aplicable al elemento en el que se utiliza, por ejemplo, una clase o un mtodo, etc.
120 Mostrar los fic%eros ocultos del proyecto Como acabamos de comentar, el fichero AssemblyInfo.vb que es el que contiene la informacin sobre la aplicacin (o ensamblado), est oculto. Para mostrar los ficheros ocultos, debemos hacer lo siguiente: En la ventana del explorador de soluciones, pulsamos en el segundo botn, (si pasamos el cursor por encima, mostrar un mensaje que indica "Mostrar todos los ficheros"), de esta forma tendremos a la vista todos los ficheros de la aplicacin, incluso el de los directorios en el que se crea el ejecutable, tal como podemos apreciar en la figura 1.05.04: Fi'ura $.05.0= Mostrar todos los fic?eros de la solucin
Tipos de atributos 6ue podemos usar en una aplicaci(n Como hemos comentado, existen atributos que son globales a toda la aplicacin y otros que podremos aplicar a elementos particulares, como una clase o un mtodo. Atributos "lobales a la aplicacin Estos se indican usando *ssembly= en el atributo y los podremos usar en cualquier parte de nuestro cdigo, aunque lo habitual es usarlos en el fichero AssemblyInfo.vb.
Nota3 La palabra o instruccin Assembly: lo que indica es que el atributo tiene un mbito de ensamblado. 121
Atributos particulares a las clases o miembros de las clases Estos atributos solo se aplican a la clase o al miembro de la clase que creamos conveniente, el formato es parecido a los atributos globales, ya que se utilizan los signos de menor y mayor para encerrarlo, con la diferencia de que en este tipo de atributos no debemos usar *ssembly=, ya que esta instruccin indica que el atributo es a nivel del ensamblado. Cuando aplicamos un atributo "particular", este debe estar en la misma lnea del elemento al que se aplica, aunque si queremos darle mayor legibilidad al cdigo podemos usar un guin bajo para que el cdigo contine en otra lnea: (Hicroso8t/$is!al,asic/Hi%eHo%!leAame()* + Ho%!le H-"eso!rces
Atributos personali*ados Adems de los atributos que ya estn predefinidos en el propio .NET o Visual Basic, podemos crear nuestros propios atributos, de forma que en tiempo de ejecucin podamos acceder a ellos mediante las clases del espacio de nombres 'eflection, aunque debido a que este tema se sale un poco de la intencin de este curso, simplemente indicar que los atributos personalizados son clases que se derivan de la clase System.*ttribute y que podemos definir las propiedades que creamos conveniente utilizar en ese atributo para indicar cierta informacin a la que podemos acceder en tiempo de ejecucin. En el siguiente cdigo tenemos la declaracin de una clase que se utilizar como atributo personalizado, notamos que, por definicin las clases para usarlas como atributos deben acabar con la palabra *ttribute despus del nombre "real" de la clase, que como veremos en el cdigo que utiliza ese atributo, esa "extensin" al nombre de la clase no se utiliza. Veamos primero el cdigo del atributo personalizado: (Attrib!teFsage(Attrib!te1argets/All)* + ?!blic Class A!torAttrib!te Inherits S-stem/Attrib!te 4 ?ri5ate +Ho%i8ica%o?or As String ?ri5ate +$ersion As String 122 ?ri5ate +)echa As String 4 ?!blic ?ro2ert- Ho%i8ica%o?or() As String ;et "et!rn +Ho%i8ica%o?or n% ;et Set(,-$al 5al!e As String) +Ho%i8ica%o?or = 5al!e n% Set n% ?ro2ert- 4 ?!blic ?ro2ert- $ersion() As String ;et "et!rn +$ersion n% ;et Set(,-$al 5al!e As String) +$ersion = 5al!e n% Set n% ?ro2ert- 4 ?!blic ?ro2ert- )echa() As String ;et "et!rn +)echa n% ;et Set(,-$al 5al!e As String) +)echa = 5al!e n% Set n% ?ro2ert- n% Class 123 Para usar este atributo lo podemos hacer de la siguiente forma: (A!tor(Ho%i8ica%o?orD=";!illermo 4g!ille4", + $ersionD="1/0/0/0", )echaD="13VAbrV2005")* + ?!blic Class ?r!ebaAtrib!tos
Nota3 Cuando utilizamos el atributo, en el constructor que de forma predeterminada crea Visual Basic, los parmetros se indican por el orden alfabtico de las propiedades, pero que nosotros podemos alterar usando directamente los nombres de las propiedades, tal como podemos ver en el cdigo de ejemplo anterior.
Acceder a los atributos personali4ados en tiempo de ejecucin Para acceder a los atributos personalizados podemos hacer algo como esto, (suponiendo que tenemos la clase 4utor4ttri#ute y una clase llamada 8rue#a4tri#utos a la que hemos aplicado ese atributo personalizado): S!b Hain() Dim ti2o As 1-2e ti2o = ;et1-2e(?r!ebaAtrib!tos) Dim atrib!tos() As Attrib!te atrib!tos = Attrib!te/;etC!stomAttrib!tes(ti2o) )or ach atr As Attrib!te In atrib!tos I8 1-2e08 atr Is A!torAttrib!te 1hen Dim a!t As A!torAttrib!te a!t = C1-2e(atr, A!torAttrib!te) Console/<rite'ine("Ho%i8ica%o 2orD " P a!t/Ho%i8ica%o?or) 124 Console/<rite'ine(")echaD " P a!t/)echa) Console/<rite'ine("$ersiSnD " P a!t/$ersion) n% I8 AeGt n% S!b
Atributos espec!ficos de Visual .asic Visual Basic utiliza una serie de atributos para indicar ciertas caractersticas de nuestro cdigo, en particular son tres: -7M-lass4ttri#ute o Este atributo se utiliza para simplificar la creacin de componentes COM desde Visual Basic. VBFi;edStrin'4ttri#ute o Este atributo se utiliza para crear cadenas de longitud fija en Visual Basic 2005. Habitualmente se aplica a campos o miembros de una estructura, principalmente cuando queremos acceder al API de Windows o cuando queremos usar esa estructura para guardar informacin en un fichero, pero utilizando una cantidad fija de caracteres. VBFi;ed4rra%4ttri#ute o Este atributo, al igual que el anterior, lo podremos usar para declarar arrays de tamao fijo, al menos si las declaramos en una estructura, ya que por defecto, los arrays de Visual Basic son de tamao variable.
%arcar ciertos miembros de una clase como obsoletos En ocasiones nos encontraremos que escribimos cierto cdigo que posteriormente no queremos que se utilice, por ejemplo porque hemos creado una versin optimizada. Si ese cdigo lo hemos declarado en una interfaz, no deberamos eliminarlo de ella, ya que as romperamos el contrato contrado por las clases que implementan esa interfaz. En estos casos nos puede venir muy bien el uso del atributo >Obsolete?, ya que as podemos informar al usuario de que ese atributo no debera usarlo. En el constructor de este atributo podemos indicar la cadena que se mostrar al usuario. En el siguiente cdigo se declara un mtodo con el atributo Obsolete: ?!blic Inter8ace I?r!eba0bsoleto 125 (0bsolete("ste mRto%o -a est6 obsoleto, !tili&a el mRto%o Hostrar")* + S!b HostrarAombre() S!b Hostrar() n% Inter8ace Si trabajamos con el IDE de Visual Basic, ese mensaje se mostrar al compilar o utilizar los atributos marcados como obsoletos, tal como podemos apreciar en la figura 1.05.05: Fi'ura $.05.05 Mensa(e de &ue un mHtodo est. o#soleto /ES4BB7**7 %(dulo 1 ? -ntroducci(n En este mdulo, conoceremos las partes generales y ms importantes del entorno de desarrollo rpido Visual Basic 2005 Express para la programacin de aplicaciones Visual Basic 2005. Veremos no slo los principales controles del entorno, sino tambin, los cambios ms destacables respecto a Visual Basic 6. Adems, veremos como desarrollar nuestros propios controles Windows, aprenderemos a trabajar con imgenes y grficos con Visual Basic .NET y finalmente, conoceremos como desplegar nuestras aplicaciones desarrolladas en Visual Basic 2005. F4N3 Qu tipo de aplicaciones puedo desarrollar con Visual Basic 2005 Express? +odr@ desarrollar principalmente3 Aplicaciones para Windows3 i!liotecas de "lases y Aplicaciones de "onsola. *dem@s3 podr@ aAadir sus propias plantillas para obtener un mayor rendimiento en el desarrollo de aplicaciones. Si su inter:s es el desarrollo de aplicaciones 4eb3 puede utili.ar #isual We! De$eloper 2005 %&press. Productos Visual Studio Express 126 Las partes que forman parte de este mdulo son las siguientes: Cap!tulo @
Uso del diseador de Visual Basic 2005 Express Cap!tulo 1
Controles de Windows Forms Cap!tulo A
Desarrollo de controles Cap!tulo B
Trabajo con imgenes y grficos Cap!tulo 3
Despliegue de aplicaciones
-ntroducci(n Cuando nos encontramos con Visual Basic 2005 Express por primera vez, saltan a la vista, algunos de los cambios ms importantes entre Visual Basic 6 y este novedoso entorno de desarrollo de aplicaciones Windows. En primer lugar, tenemos que saber que Visual Basic 2005 Express es un entorno monoprogramacin, es decir, es un entorno con el que podremos desarrollar aplicaciones Windows con Visual Basic 2005. Adems, tambin podremos reutilizar controles y libreras escritas en otros lenguajes de la plataforma .NET. Para un desarrollador, familiarizarse con el entorno de Visual Basic 2005 Express es una tarea que no debe entraar una complejidad excesivamente grande. Como nos ocurre a todos los que nos encontramos delante de un nuevo entorno de trabajo, lo nico que se requiere es constancia y prctica, mucha prctica. Sin embargo, si usted es ya un desarrollador habitual de Visual Basic 6, notar que sus avances van a ser significativos en muy poco tiempo. De todos los modos, con el fin de facilitar su comprensin con el nuevo entorno, veremos tambin aquellas diferencias y similitudes ms destacables para el programador entre los entornos de Visual Basic 2005 Express y el de Visual Basic 6, para que el desarrollador que se encuentre por primera vez con este entorno, se familiarice lo antes posible con l. %(dulo 1 ? Cap!tulo @
1. Cuadro de herramientas 2. Explorador de base de datos 127 3. Explorador de soluciones 4. Propiedades 5. Menus y barra de botones 6. Otras consideraciones %(dulo 1 ? Cap!tulo @ @. Cuadro de #erramientas El cuadro o barra de herramientas de Visual Basic 2005 Express, vara ligeramente con respecto al cuadro de herramientas de Visual Basic 6. Como en Visual Basic 6, esta barra aparece por defecto en el margen izquierdo. A diferencia de Visual Basic 6, en Visual Basic 2005 Express tenemos una gran cantidad de controles dispuestos en diferentes categoras. Una diferencia visual de las barras de herramientas entre Visual Basic 6 y Visual Basic 2005 Express, es la que se aprecia en figura 1. &isual #asic )*+ &isual #asic ,++- 5press Figura 1 El Cuadro de "erramientas, lo localizar en la parte izquierda del entorno #isual Basic 2BBC %&press. Cuando iniciamos un nuevo proyecto con Visual Basic 2005 Express, el cuadro de herramientas queda rellenado con los controles que podemos utilizar en el proyecto. Si abrimos un formulario Windows, los controles quedan habilitados para que los podamos insertar en el formulario Windows. En la figura 2 se muestra la barra de herramientas con los controles preparados para ser insertados en el formulario Windows. 128 $oolbo5 de &isual #asic ,++- 5press con controles 6indo7s preparados para ser insertados en el formulario 6indo7s Figura 2 Nota3 Para insertar un control en un formulario Windows, se requiere que el formulario Windows sobre el que deseamos insertar un control, est abierto. Una vez que est abierto, bastar con realizar una de las tres siguientes acciones para insertar un control al formulario: D 7acer doble clic sobre un control del cuadro de "erramientas D 7acer clic sobre un control del cuadro de "erramientas3 y sin soltar el bot<n del rat<n3 arrastrarlo sobre el formulario D 7acer clic sobre un control del cuadro de "erramientas3 y luego "acer clic sobre el formulario y arrastrar para marcar una .ona 9ue cubrir@ nuestro control y soltar el rat<n El control quedar entonces insertado dentro del formulario.
129
%(dulo 1 ? Cap!tulo @ 1. E;plorador de base de datos Si ha sido lo suficientemente observador cuando se explicaban los detalles del cuadro o barra de herramientas, y ha prestado especial atencin a las figuras o a las ventanas del entorno de desarrollo de Visual Basic 2005 Express, quizs haya notado que en la parte izquierda adems de la solapa cuadro de "erramientas, aparece otra solapa de nombre e&plorador de base de datos. Esta solapa es nueva para los desarrolladores de Visual Basic 6, y en ella, un programador puede acceder a diferentes recursos del sistema. El principal y ms importante recurso, es el que tiene que ver con las conexiones con bases de datos, ya sean Microsoft Access, Microsoft SQL Server o cualquier otra fuente de datos. En la figura 1 puede observar la solapa %&plorador de base de datos extendida con parte de sus opciones. La solapa del 5plorador de base de datos desple"ada de &isual #asic ,++- 5press Figura 1 Conectando con una base de datos %icrosoft Access a trav0s de $'E ". Para muestra un botn, y dado el carcter prctico de este tutorial, aprender a crear una conexin con cualquier base de datos, en nuestro caso de ejemplo una base de datos Microsoft Access, para poder utilizarla fcilmente en nuestra aplicacin Windows. Haga clic sobre el botn representado por la siguiente imagen . En este instante, se abrir una nueva ventana como la que se muestra en la figura 2. 130 &entana A"re"ar cone5in8 para establecer las propiedades de cone5in con una fuente de datos Figura 2 Por defecto, la ventana A're'ar cone&i(n queda preparada para establecer una conexin con una fuente de datos de origen de datos OL% DB, por lo que si nuestra intencin es establecer una conexin con otra fuente de datos, entonces deberemos hacer clic sobre el botn "a)!iar*** que se indica en la figura 3. #otn Cambiar*** para seleccionar otro proveedor de ori"en de datos Figura 3 De esta manera, podemos indicar el origen de acceso a datos que necesitamos para establecer la conexin con nuestra fuente de datos, y que en nuestro ejemplo, no ser un proveedor de SQL Server, por lo que el origen de datos OLE DB es vlido para nosotros. Una vez que hemos hecho clic sobre el botn "a)!iar***, nos aseguramos por lo tanto, que nuestro origen de datos es base de datos de (icrosoft *ccess /OL% DB0, como se indica en la figura 4. 131 &entana de seleccin del proveedor u ori"en de datos Figura 4 F4N3 Puedo utilizar el proveedor OLE DB en lugar del proveedor de SQL Server para conectar con una base de datos SQL Server? Con OL% DB3 puede acceder a fuentes de datos SEL Serer u otras fuentes de datos como (icrosoft *ccess3 sin embargo3 si utili.a SEL Serer F.B3 SEL Serer 2BBB < SEL Serer 2BBC3 se recomienda el uso del proeedor de SEL Serer3 9ue es un proeedor de acceso a datos natio 9ue aumenta el rendimiento de nuestras aplicaciones con SEL Serer. S<lo si utili.a una ersi<n de SEL Serer anterior a SEL Serer F.B3 deber@ utili.ar necesariamente el proeedor de acceso a datos OL% DB. Una vez que hemos seleccionado el proveedor de acceso a datos, nos centraremos en la opcin +o)!re de arc,i$o !ase de datos como se muestra en la figura 5. 132 n este lu"ar indicaremos el fic%ero de base de datos con el que estableceremos la cone5in Figura 5 Para agregar el fichero de base de datos a la conexin, pulsaremos el botn E;aminar... y seleccionaremos el fichero de base de datos de nuestro disco duro. De esta manera, la base de datos quedar indicada en la conexin y tan slo deberemos probar nuestra conexin pulsando el botn 8ro#ar cone;in como se indica en la figura 6. 133 s recomendable probar la cone5in antes de a"re"arla al entorno Figura 6 Si la prueba de conexin se ha realizado satisfactoriamente, recibiremos un mensaje en pantalla afirmativo como el que se indica en la figura 7. 9rueba de la cone5in reali4ada con 5ito Figura 7 4 tener en cuenta3 En este ejemplo, la conexin con la base de datos Microsoft Access, no tiene ningn tipo de usuario y contrasea. Tenga en cuenta que en la parte identificada como -one;in con la #ase de datos, podramos indicar el usuario y contrasea si fuera necesario. En este punto, tan slo deberemos pulsar sobre el botn 4ceptar para que la base de datos con la que hemos establecido la conexin, quede ahora insertada en la ventana del %&plorador de !ase de datos como se muestra en la figura 8. #ase de datos Microsoft Access insertada en la ventana del 5plorador de base de datos Figura 8 J4dvertenciaK3 Tener demasiadas conexiones activas en el entorno o configuradas en l, puede incidir negativamente en el rendimiento de Visual Basic 2005 Express cuando se trabaja con l. Tenga configuradas slamente, aquellas conexiones que va a utilizar, o aquellas conexiones de uso ms habitual y frecuente. 134
%(dulo 1 ? Cap!tulo @ A. E;plorador de soluciones El %&plorador de soluciones sera equivalente al %&plorador de +royecto en #isual Basic G, y como en este ltimo, lo podemos encontrar en la parte derecha de nuestro entorno de desarrollo. Cabe destacar que en .NET hay una ligera diferencia con respecto a Visual Basic 6 que conviene conocer. Un proyecto en #isual Basic 2BBC %&press equivale a una solucin. Es quizs una caracterstica chocante al principio, pero en seguida nos acostumbraremos a ello. Una solucin se compone de proyectos y stos, de recursos y objetos. Por lo general, una solucin contendr un proyecto, que equivaldra al mismo valor en Visual Basic 6, pero podemos encontrarnos con ms de un proyecto dentro de una misma solucin. Sin embargo, estos conceptos son muy sencillos de comprender y controlar, y para nada debe hacernos pensar que esto es algo complejo que nos costar mucho tiempo dominar. De hecho, la similitud con Visual Basic 6 es enorme. En la figura 1, podemos observar la diferencia del explorador de soluciones entre Visual Basic 6 y #isual Basic 2BBC %&press. 135 5plorador de proyectos en &isual #asic ) La opcin 5plorador de soluciones desple"ada en &isual #asic ,++- 5press Figura 1 Una caracterstica distinta en #isual Basic 2BBC %&press respecto a Visual Basic 6, es que para agregar un nuevo formulario, clase, etc., en Visual Basic 6 bastaba con hacer clic con el botn derecho del ratn sobre cualquier recurso del %&plorador de soluciones, mientras que en el caso de #isual Basic 2BBC %&press, debemos hacer clic con el botn derecho sobre la solucin. Adems, en #isual Basic 2BBC %&press, tenemos ms opciones a aadir al proyecto que en Visual Basic 6.0. Nota3 +ara abrir un recurso de la soluci<n3 basta con situarnos en el recurso determinado3 por ejemplo un formulario 4indo!s de nombre )ormH.b y "acer doble clic sobre :l. %l recurso se abrir@ autom@ticamente en #isual Basic 2BBC %&press. *dem@s3 en #isual Basic 2BBC %&press sabremos en todo momento sobre 9u: recurso estamos trabajando en un momento dado3 algo 9ue al trabajar con proyectos grandes en #isual Basic G3 nos costaba un gran esfuer.o. 136
%(dulo 1 ? Cap!tulo @ B. ,ropiedades Esta es la parte del entorno de desarrollo que ms se parece a Visual Basic 6. Esta ventana la encontraremos en la parte derecha y ms abajo de la ventana %&plorador de soluciones en nuestro entorno de desarrollo. Como en Visual Basic 6, esta ventana nos permitir acceder a las propiedades de los objetos insertados en nuestros formularios Windows, como se muestra en la figura 1. &entana de 9ropiedades de &isual #asic ) &entana de 9ropiedades de &isual #asic ,++- 5press 137 Figura 1 Para acceder a las propiedades de un determinado control, deberemos seleccionar el control en el formulario Windows y acudir a la ventana -ropiedades, o bien como en Visual Basic 6, seleccionar el control en el formulario Windows y pulsar el botn F=.
%(dulo 1 ? Cap!tulo @ 3. %en)s y barra de botones Respecto a los mens y barra de botones, tanto el entorno de Visual Basic 6 como el entorno de #isual Basic 2BBC %&press, mantienen una similitud muy caracterstica, aunque tambin hay algunas pequeas diferencias. La variacin ms destacable es la accin de compilacin. Mientras en Visual Basic 6, la opcin de compilacin la encontrbamos en el men 4rc?ivo 6 Oenerar, en #isual Basic 2BBC %&press, esta opcin no slo cambia de lugar, sino que su filosofa tambin. En este entorno de desarrollo, encontraremos esta opcin en el men Oenerar, y dentro de este men, encontraremos diferentes opciones de compilacin y accin, siempre sobre la Soluci<n o sobre el proyecto abierto. Por otro lado, existe una manera de acceder a las partes ms importantes de nuestra solucin de manera rpida. Desde el men 8ro%ecto 6 8ropiedades, podemos acceder a las propiedades del proyecto o solucin. Existen diferentes apartados dentro de esta ventana que nos pueden resultar especialmente tiles. Uno de estos y relacionado con la opcin de compilacin es el apartado "o)pilar como se muestra en la figura 1. 138 9ropiedades de la solucin sobre la que se trabaja en &isual #asic ,++- 5press Figura 1 Como vemos en la figura 1, existen sin embargo multitud de opciones y apartados diferentes relacionados todos ellos con nuestra solucin. Otro de los apartados destacables, es el apartado denominado 8u#licar. An as, ste es el corazn o parte fundamental que debemos controlar a la hora de desarrollar una aplicacin o a la hora de gestionar una solucin, porque dentro de esta ventana, se resume buena parte de los mens y barra de botones del entorno de #isual Basic 2BBC %&press. De todos los modos, tendremos la oportunidad de ver ms adelante, algunos usos de algunas de las opciones de la barra de botones del entorno. 139
%(dulo 1 ? Cap!tulo @ C. $tras consideraciones El desarrollador de Visual Basic 6, encontrar muy interesantes algunos de los cambios incorporados en #isual Basic 2BBC %&press. Al principio, quizs se encuentre un poco desorientado, pero rpidamente y gracias a su experiencia en el entorno Visual Basic 6, se acostumbrar al cambio. Entre algunos de estos cambios, destacara los siguientes: En #isual Basic 2BBC %&press, acceder a los objetos de nuestra aplicacin es mucho ms fcil. Dentro del entorno, observaremos que se van creando diferentes solapas que nos permite acceder y localizar los recursos con los que estamos trabajando de forma rpida. En la figura 1 podemos observar justamente esto que comento. !olapas de los objetos abiertos en &isual #asic ,++- 5press Figura 1 #isual Basic 2BBC %&press permite como en Visual Basic 6, hacer un .top / 0o de nuestras aplicaciones, es decir, pausar la ejecucin de una aplicacin en modo depuracin y modificar los valores o propiedades que deseemos y continuar ejecutndola. Esta opcin que los programadores de Visual Basic 6 utilizan con mucha frecuencia en el desarrollo de sus aplicaciones, se ha mantenido en #isual Basic 2BBC %&press, pero no en Visual Studio .NET 2002 y Visual Studio .NET 2003. Si por alguna razn, debe trabajar con alguno de estos entornos, debe saber que esta opcin no est disponible para las versiones comentadas. 140 Otra caracterstica que debemos conocer de nuestro entorno de desarrollo, es la capacidad de anclar o fijar una ventana de las comentadas anteriormente o de permitir que se haga visible cuando acercamos el puntero del ratn sobre ella. Esta opcin es la que puede verse en la figura 2. :pcin de ocultar o mostrar la ventana seleccionada en &isual #asic ,++- 5press Figura 2 Ntese que al pulsar sobre el icono indicado en la figura 2, haremos que esta ventana quede fija en el entorno de desarrollo. Cuando pulsamos este icono, la ventana queda fija y queda representado por un icono como el que se muestra en la figura 3. Icono para ocultar o mostrar la ventana seleccionada cuando se encuentra en modo anclado Figura 3 Algo que oculta el entorno de #isual Basic 2BBC %&press por defecto, son las denominadas clases parciales. Se trata de una nueva caracterstica aadida a .NET 2.0 y por lo tanto a Visual Basic 2005, que permite separar o partir una clase en varias porciones de cdigo. La explicacin ruda de esto, es que el programador puede tener dos ficheros de cdigo fuente independientes, que posean el mismo nombre de clase. Para indicar que pertenece a la misma clase, sta debe tener la palabra clave -artial como parte de su definicin para indicar que es una clase parcial. Un ejemplo que aclare esto es el siguiente: ?!blic Class Class1 ?!blic )!nction Accion1() As Integer "et!rn 1 n% )!nction n% Class ?artial ?!blic Class Class1 ?!blic )!nction Accion2() As Integer 141 "et!rn 2 n% )!nction n% Class El comportamiento de la clase es el de una nica clase, por lo que su declaracin y uso es como el de cualquier clase normal, tal y como se indica en el siguiente cdigo: ?!blic Class )orm1 ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ Dim HiClase As AeJ Class1 Hessage,oG/ShoJ(HiClase/Accion2/1oString() P 5bCr'8 P HiClase/Accion1/1oString()) n% S!b n% Class De todas las maneras, el entorno nos oculta muchas veces las clases parciales de un aplicacin. Para ello, pulsaremos sobre la opcin Mostrar todos los arc?ivos de la ventana %&plorador de soluciones como se indica en la figura 4. Icono u opcin para mostrar todos los arc%ivos del proyecto Figura 4 De esta manera, podremos acceder a los archivos y recursos del proyecto, incluidas las clases parciales como se indica en la figura 5. 142 Clase parcial mostrada en los arc%ivos del proyecto Figura 5 4 tener en cuenta3 Cuando se genera un proyecto con #isual Basic 2BBC %&press3 el entorno genera diferentes clases parciales3 como por ejemplo la 9ue se genera para un formulario.
%(dulo 1 ? Cap!tulo 1 @. "atos El grupo Datos corresponde con el grupo que tiene relacin directa con los componentes de acceso a datos, como se muestra en la figura 1. 143 Controles Datos en &isual #asic ,++- 5press Figura 1 Para el desarrollador de Visual Basic 6, los controles, componentes y mtodos de acceso a datos, contiene dentro de s un especial misterio, es como el Santo 1rial de la programacin. Casi siempre nos atascamos ah, siempre en el mismo sitio. Pero no se preocupe ni lo ms mnimo por ello, aprenderemos a utilizarlos a base de prctica, y lo que es ms importante, los dominaremos rpidamente gracias a nuestra experiencia en Visual Basic 6. Slo como curiosidad y por ahora, le presentar uno de los componentes ms destacables en #isual Basic 2BBC %&press, por su semejanza con otro muy utilizado en Visual Basic 6 y versiones anteriores hasta Visual Basic 3, estoy hablando del control y componente indin'+a$i'ator que usaremos frecuentemente en nuestras aplicaciones con acceso a fuentes de datos. Este control insertado en un formulario Windows, es el que se puede ver en la figura 2. Control #indin";avi"ator insertado en un formulario 6indo7s en &isual #asic ,++- 5press Figura 2 Como puede observar, este control, tiene un aspecto muy similar al del famoso 'ecordset de Visual Basic 6 tan utilizado por los desarrolladores de Visual Basic. Lgicamente, este control tiene un aspecto mucho ms vistoso y moderno, pero es uno de los controles estrella de #isual Basic 2BBC %&press, ya que inexplicablemente en un primer momento por la Comunidad de desarrolladores que dieron el salto a Visual Basic .NET, en Visual Studio .NET 2002 y Visual Studio .NET 2003 no exista este control en el entorno. 144 Visual Studio 2005 y por lo tanto, Visual Basic 2005, s que nos trae sin embargo, la novedad del control indin'+a$i'ator. -omunidad dotNet3 #isual Studio 2BBC le proporciona un amplio conjunto de controles y componentes asI como un no menos completo conjunto de clases 9ue le facilita al desarrollador las tareas de programaci<n re9ueridas. Sin embargo3 e&isten contribuciones gratuitas y otras de pago3 9ue el programador puede utili.ar seg;n lo re9uiera. * continuaci<n le indico el 9ue a mi modo de er es el lugar m@s representatio de este tipo de contribuciones a la Comunidad de desarrolladores .$%T. Microsoft GotDotNet
%(dulo 1 ? Cap!tulo 1 1. Componentes #isual Basic 2BBC %&press, incluye un conjunto de componentes muy nutrido y variado. Algunos de estos componentes, han sido mejorados y otros ampliados. En la figura 1 podemos observar estos componentes. 145 Componentes de &isual #asic ,++- 5press Figura 1 Los componentes son igual que los controles no visibles de Visual Basic 6. Para que lo entienda mejor, el control Timer de Visual Basic 6 queda insertado en el formulario como se indica en la figura 2. Control $imer insertado en un formulario de &isual #asic ) Figura 2 En Visual Basic 2005, el comportamiento de los componentes, es ligeramente diferente, como por ejemplo el mismo componente Timer de Visual Basic 2005. Si hacemos doble clic sobre el componente Timer para insertarlo en el formulario, ste quedar dispuesto en la parte inferior del formulario como se indica en la figura 3. Control $imer insertado en un formulario de &isual #asic ,++- Figura 3 Como en Visual Basic 6, este tipo de componentes no son visibles en tiempo de ejecucin. 146
%(dulo 1 ? Cap!tulo 1 A. Controles comunes Con este nombre, se aglutinan los controles ms generales y variados que podemos utilizar en nuestras aplicaciones Windows. Sera algo as, como el resto de controles y componentes no contenidos en ninguna de las secciones que hemos visto anteriormente, aunque esto no es siempre as. Por esa razn, si encuentra dos controles o componentes iguales en dos o ms secciones, no lo tenga en consideracin. Digamos que en esta solapa se aglutinan por lo tanto, los controles que utilizaremos con ms frecuencia. En la figura 1 podemos observar algunos de los controles y componentes citados. 147 Controles 6indo7s (orms en &isual #asic ,++- 5press Figura 1 La cantidad de controles y componentes de esta seccin es de ms de 50. Una cifra ms que suficiente para el desarrollador y enormemente superior al nmero de controles y componentes que podamos utilizar en Visual Basic 6. Truco3 Como puede obserar3 a eces cuesta locali.ar un control debido a la enorme cantidad de controles 9ue "ay. +ara ordenarlos3 puede arrastrar y soltar los controles y componentes en la barra de "erramientas o bien3 si 9uiere "acer una ordenaci<n por orden alfab:tico3 puede "acer clic con el bot<n derec"o del rat<n sobre una determinada secci<n de controles y seleccionar la opci<n 1rdenar ele)entos alfa!2tica)ente como se indica en la siguiente figura siguiente= 148 Los controles y componentes de esa secci<n 9uedar@n ordenados alfab:ticamente. Lo ms destacable para el desarrollador de Visual Basic 6, es que aqu veremos una gran cantidad de controles que nos resultarn muy familiares. Controles como el control *a#el, 8ictureBo;, Te;tBo;, Frame que ahora pasa a llamarse OroupBo;, -ommandButton que ahora pasa a llamarse Button, -?ec!Bo;, 7ptionButton que ahora pasa a llamarse BadioButton, -om#oBo;, *istBo;, 5ScrollBar, VScrollBar, Timer, etc. Sin embargo, hay otro conjunto de controles nuevos para el desarrollador de Visual Basic 6, y que proporciona nuevas y ventajosas caractersticas a la hora de desarrollar aplicaciones con Visual Basic 2005. Entre estos controles nuevos, podemos encontrar el control 8rint/ocument, Error8rovider, Pe#Broser, 8rint8revie-ontrol, FolderBroser/ialo', ToolTip que ahora debe insertarse para aportar tooltips a nuestros controles, Trac!Bar, NumericGp/on, Split-ontainer, Mont?-alendar, /ateTime8ic!er, etc. Cada uno de los controles, tiene unas caractersticas y cualidades determinadas. Slo a base de prctica, aprenderemos a utilizarlos y lo nico que debemos saber, es cul de ellos utilizar en un momento dado. El abanico de controles y componentes es lo suficientemente amplio como para poder abordar con ellos, cualquier tipo de proyecto y aplicacin Windows que nos sea demandada. 149
%(dulo 1 ? Cap!tulo 1 B. 5eneral Esta seccin es como el cajn desastre, un lugar dnde podemos insertar otros controles o componentes desarrollados por terceros por ejemplo. !eccin <eneral en &isual #asic ,++- 5press Figura 1 Esta seccin de todos los modos, la puede utilizar un desarrollador en muchos casos. Por ejemplo, los desarrolladores que desean arrastrar y soltar aqu los controles y componentes que ms utiliza o los que utiliza en un determinado proyecto. Otro caso de ejemplo es cuando se trabaja con controles o componentes similares desarrollados por dos empresas diferentes que queremos tener localizados o separados para no mezclarlos. 150 En otras circunstancias, tampoco es raro encontrarse con controles o componentes con iconos similares, por lo que aclararse cul es el que nos interesa puede ser una tarea obligada. An as, otra de las posibilidades con la que nos podemos encontrar para utilizar esta seccin es la de tener que utilizar un control o componente circunstancialmente en un momento dado, y por eso, que no deseemos aadir este control o componente a otra seccin como la de Controles comunes por ejemplo. Utilice por lo tanto esta seccin como lo considere oportuno.
%(dulo 1 ? Cap!tulo 1 3. $tras consideraciones La seccin 1eneral nos indica un repositorio de mbito y carcter general, sin embargo, el desarrollador puede querer ordenar su propio repositorio o seccin de controles y componentes.
%anipulando el Cuadro de #erramientas Para ello, nos posicionaremos en la barra de herramientas y pulsaremos el botn derecho del ratn sobre la parte gris de la barra de herramientas desplegada y seleccionaremos la opcin *gregar fic"a del men emergente, como se muestra en la figura 1. 151 :pcin de personali4acin de nuestros propios "rupos de controles y componentes Figura 1 Cuando seleccionamos esta opcin, aparecer una caja de texto en la barra de herramientas dnde podremos escribir el nombre que consideremos oportuno, como se muestra en la figura 2. 152 9ersonali4acin de un "rupo de controles y componentes en &isual #asic ,++- 5press Figura 2 Si se hace la siguiente pregunta, cmo cambiar el nombre de una seccin ya creada o una existente?, sepa que deber realizar los siguiente pasos. Haga clic con el botn derecho del ratn sobre la seccin sobre la que desea cambiar el nombre y seleccione la opcin "a)!iar no)!re de fic,a como se muestra en la figura 3. Figura 3 De igual forma, puede cambiar tambin el nombre de los controles o componentes insertados. Para hacer eso, haga clic con el botn derecho del ratn sobre un control o componente y seleccione la opcin "a)!iar no)!re de ele)ento como se muestra en la figura 4. 153 Figura 4 Visual Basic 2005, nos proporciona un amplio conjunto de opciones de personalizacin del entorno de trabajo, para que se ajuste a las exigencias de los desarrolladores. A diferencia de Visual Basic 6, el nmero de opciones y posibilidades de Visual Basic 2005, es mucho ms alto como hemos podido ver. F4N3 Qu ocurre si me equivoco personalizando mi barra de herramientas? #isual Basic 2BBC %&press nos proporciona la posibilidad de resetear o restaurar el estado inicial de la barra de "erramientas en el entorno de desarrollo. +ara "acer esto3 "aremos clic con el bot<n derec"o del rat<n la barra de "erramientas y seleccionaremos la opci<n 3esta!lecer cuadro de ,erra)ientas del men; emergente3 como se muestra en la siguiente figura. 154 J7(oK, al seleccionar esta opcin, perderemos todas las modificaciones que hayamos realizado sobre la barra de herramientas.
$tros controles a tener en cuenta Dentro del entorno de #isual Basic 2BBC %&press y en .NET en general, se han aadido una serie de controles nuevos que conviene comentar. Uno de estos controles, se llama 4ebBro!ser, tal y como se indica en la figura 5. Control 6eb#ro7ser en el Cuadro de %erramientas Figura 5 Este control es la representacin de un control especfico para mostrar contenido XML o contenido HTML, como si de una pgina web se tratara. 155 Sirva el siguiente ejemplo de cdigo fuente para demostrar como usar el control y como se muestra dicho control en una aplicacin Windows. El cdigo de la aplicacin quedara como se detalla a continuacin: ?!blic Class )orm1 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As 0b#ect, ,-$al e As S-stem/5entArgs) Han%les He/'oa% <eb,roJser1/Aa5igate("htt2DVVlocalhostV,ien5eni%o/as2") n% S!b n% Class Nuestro ejemplo en ejecucin es el que se muestra en la figura 6. Control 6eb#ro7ser en ejecucin Figura 6 Hay ms controles que representan una novedad para el desarrollador de .NET, pero no tanto para el desarrollador de Visual Basic 6, como puede ser por ejmplo, el control Mas!edTe;tBo;, como se muestra en la figura 7. Control Mas=ed$e5t#o5 en &isual #asic ,++- Figura 7 156 Sin embargo, hay otros controles clsicamente demandados por los desarrolladores, como los controles de accesos a puertos COM y puertos serie, como es el caso del control Serial8ort que se muestra en la figura 8. Control !erial9ort en &isual #asic ,++- Figura 8 No es cuestin de repasar cada uno de los controles que el programador puede encontrar en #isual Basic 2BBC %&press, sin embargo, no me gustara dejar de comentar, uno de los controles ms usados y tiles para las aplicaciones Windows, que tiene a su vez su equivalente para el desarrollo de aplicaciones Web en ASP.NET. Me refiero al control Mont?-alendar que se muestra en la figura 9. Control Mont%Calendar en &isual #asic ,++- Figura 9 Este control, que se muestra en la figura 10 cuando lo insertamos en un formulario, es un control que nos facilita la entrada de fechas en el sistema y permite asegurarnos, que la fecha seleccionada es una fecha vlida. Control Mont%Calendar insertado en un formulario 6indo7s Figura 10 157
-ntroducci(n Hasta ahora, hemos aprendido a identificar las partes ms importantes del entorno de desarrollo de #isual Basic 2BBC %&press, hemos visto igualmente como se separan los controles y componentes, y hemos visto tambin que existe un grupo de solapas dnde podramos aadir nuestros propios controles y componentes, incluso hemos aprendido a crear nuestro propio grupo o seccin de controles y componentes, dnde podemos tambin incluir los controles y componentes que por ejemplo hayamos desarrollado nosotros mismos, sin embargo, para poder insertar ah un control o componente desarrollado por nosotros, deberamos aprender a crearlos. Eso es justamente lo que veremos a continuacin adems de ver otras tcnicas que conviene repasar antes de aprender a desarrollar nuestros propios controles y componentes. Por eso, lo primero que haremos ser aprender a desenvolvernos adecuadamente en el entorno de desarrollo con los controles que ya conocemos. %(dulo 1 ? Cap!tulo A
1. Dominando los controles en el entorno de trabajo 2. Creacin de controles en tiempo de ejecucin 3. Creacin de una matriz de controles 4. Creacin de controles nuevos 5. Otras consideraciones
158
%(dulo 1 ? Cap!tulo A @. "ominando los controles en el entorno de trabao Si queremos aprender a acrear nuestros propios controles para poder distribuirlos y utilizarlos en nuestro entorno, lo mejor es dominar el uso de los controles en nuestro entorno de desarrollo. Ya hemos visto anteriormente como insertar un control a nuestro formulario, pero quizs no sepamos como manejarlos de forma eficiente en nuestro formulario. Inserte en un formulario Windows por ejemplo, tres controles Button como se muestra en la figura 1. 159 Controles #utton insertados en un formulario 6indo7s Figura 1 Como podemos observar, los controles estn dispuestos de una forma desordenada, ya que al insertarlos por ejemplo haciendo doble clic tres veces sobre un control Button, stos quedan dispuestos anrquicamente en el formulario. Separe los controles Button de forma que queden ahora esparcidos en el formulario de alguna manera tal y como se muestra en la figura 2. Controles #utton separados en el formulario Figura 2 160 Seleccione todos los controles Button como se mostraba en la figura 2 y seleccione del men las opciones Formato 6 4linear 6 *ados i@&uierdos como se indica en la figura 3. Los controles #utton quedar>n ordenados correctamente dentro del formulario Figura 3 Sin embargo, podemos extender el uso de las propiedades especiales para alinear los controles en un formulario. Por ejemplo, ahora que tenemos los controles Button alienados correctamente, podemos hacer uso de la opcin de men Formato 6 Espaciado vertical 6 Nuitar como se indica en la figura 4. :tras opciones especiales8 nos permiten alinear o trabajar con los controles de forma r>pida y se"ura Figura 4 En este caso, los controles quedarn dispuestos en el formulario como se indica en la figura 5. 161 Controles alineados y espaciados se"?n la eleccin de opciones del entorno Figura 5 Como podemos apreciar, alinear los controles en el entorno es realmente sencillo. Visual Basic 2005 Express nos proporciona una gran cantidad de opciones para llevar a cabo este tipo de tareas. Incluso si nos encontramos con un controles de diferente tamao entre s como se muestra en la figura 6, podemos hacer uso de la opcin del men Formato 6 I'ualar tamaQo permitindonos cambiar el tamao de los controles seleccionados dentro del formulario Windows. 162 Controles de diferentes tama2os dispuestos en el formulario 6indo7s Figura 6 El men que nos permite cambiar los tamaos de los controles insertados en un formulario posee diferentes posibilidades. En nuestro caso, seleccionaremos del men, la opcin Formato 6 I'ualar tamaQo 6 4m#os tal y como se muestra en la figura 7. Cambiando los tama2os anc%o y alto de los controles seleccionados de un formulario Figura 7 Una vez seleccionada esta opcin, los controles se modificarn con el mismo tamao tal y como se muestra en la figura 8. 163 Controles del formulario con su tama2o modificado Figura 8 Una vez seleccionada esta opcin, los controles se modificarn con el mismo tamao tal y como se muestra en la figura 8. Truco3 Suponiendo 9ue tengamos tres controles utton de diferentes tamaAos y 9ue 9ueramos 9ue todos tengan el mismo tamaAo 9ue el segundo de sus controles3 seleccionaremos sie)pre como primer control3 el control 9ue 9ueremos como base de tamaAo para el resto de controles3 y posteriormente con la tecla "trl seleccionaremos uno a uno el resto de controles. Sin embargo, para alinear los controles en un formulario tenemos ms opciones. Hemos visto algunas de ellas, quizs las ms habituales, pero como podemos deducir del men Formato, podremos alinear los controles, espaciarlos entre s horizontal o verticalmente, modificar su tamao, centrarlos en el formulario horizontal o verticalmente, etc. Por otro lado, Visual Basic 2005 Express, nos proporciona una utilidad en tiempo de diseo muy til. Se trata de las guas de representacin que veremos en tono azul claro, y que aparecen cuando movemos un control en el formulario. Estas guas indican la situacin y posicin de un control respecto a otro prximo. La representacin de estas guas que se muestran en la figura 9, nos facilita enormemente la disposicin de los controles en un formulario. <u@as o re"las de direccin o separacin entre controles Figura 9
164
%(dulo 1 ? Cap!tulo A 1. Creaci(n de controles en tiempo de eecuci(n Prcticamente todo en .NET son objetos. Los controles tambin, as que para aadir un control a un formulario en tiempo de ejecucin, deberemos hacerlo tratndolo como un objeto. La declaracin principal de un objeto, se realiza con la clasula Ne. El siguiente ejemplo de cdigo, crea un control Button en tiempo de ejecucin. ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 Declaramos el ob#eto ,!tton Dim HiControl As AeJ ,!tton 4 Declaramos !n nombre al control (si I!eremos) HiControl/Aame = "btn1" 4 Cambiamos alg!nas %e s!s 2ro2ie%a%es HiControl/1eGt = "#em2lo %e ,otSn" HiControl/1o2 = 50 HiControl/'e8t = 50 HiControl/<i%th = 200 165 HiControl/Height = 50 4 ATa%imos el control al )orm!lario He/Controls/A%%(HiControl) n% S!b En la figura 1 podemos ver el resultado en ejecucin del cdigo escrito anteriormente. Para ejecutar nuestra aplicacin, pulsaremos el botn F5 como lo hemos hecho siempre en Visual Basic 6. Creacin de un control en tiempo de ejecucin en &isual #asic ,++- Figura 1 Otra de las caractersticas de los controles tanto para los programadores de Visual Basic 6 como para los programadores que comienzan con Visual Basic 2005, es la posibilidad de manipular los controles en tiempo de ejecucin. Sin embargo, en nuestro caso, vamos a modificar la propiedad Te;t del control Button que hemos insertado en tiempo de ejecucin. Para hacer esto, lo ms habitual es poner el nombre del control, su propiedad y el valor correspondiente. En nuestro caso, el cdigo quedara como el que se indica a continuacin: ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 Declaramos el ob#eto ,!tton Dim HiControl As AeJ ,!tton 4 Declaramos !n nombre al control (si I!eremos) HiControl/Aame = "btn1" 4 Cambiamos alg!nas %e s!s 2ro2ie%a%es 166 HiControl/1eGt = "#em2lo %e ,otSn" HiControl/1o2 = 50 HiControl/'e8t = 50 HiControl/<i%th = 200 HiControl/Height = 50 4 ATa%imos el control al )orm!lario He/Controls/A%%(HiControl) 4 Ho%i8icamos la 2ro2ie%a% 1eGt %el control inserta%o btn1/1eGt = "0tro teGto" n% S!b Analizando este cdigo, parece estar bien escrito, pero al pulsar F5 para ejecutar nuestro proyecto, nos encontramos con que Visual Basic 2005 nos muestra un error. Nos indica que !tn1 no est declarado. Qu ocurre?. Al buscar la clase de ensamblados de la aplicacin, el control Button de nombre !tn1 no existe, por lo que Visual Basic 2005 detecta que debemos declarar el control, sin embargo y en nuestro caso, esta declaracin la hacemos en tiempo de ejecucin. Cmo acceder a la propiedad Te;t del control Button creado en tiempo de ejecucin?. El siguiente cdigo resuelve esta duda: ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 Declaramos el ob#eto ,!tton Dim HiControl As AeJ ,!tton 4 Declaramos !n nombre al control (si I!eremos) HiControl/Aame = "btn1" 4 Cambiamos alg!nas %e s!s 2ro2ie%a%es HiControl/1eGt = "#em2lo %e ,otSn" HiControl/1o2 = 50 HiControl/'e8t = 50 HiControl/<i%th = 200 HiControl/Height = 50 4 ATa%imos el control al )orm!lario He/Controls/A%%(HiControl) 167 4 Ho%i8icamos la 2ro2ie%a% 1eGt %el control inserta%o C1-2e(He/)in%)orm/Controls("btn1"), ,!tton)/1eGt = "0tro teGto" n% S!b Bsicamente, utilizamos la funcin -T%pe para buscar en el formulario principal, el control Button de nombre !tn1, para poder as, cambiar la propiedad Te;t de este control. An as, tambin podramos haber accedido a la propiedad Te;t del control mediante otra accin complementaria, como se muestra en el siguiente cdigo: 4 Declaramos el ob#eto ,!tton Dim HiControl As AeJ ,!tton ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 Declaramos !n nombre al control HiControl/Aame = "btn1" 4 Cambiamos alg!nas %e s!s 2ro2ie%a%es HiControl/1eGt = "#em2lo %e ,otSn" HiControl/'ocation = AeJ ?oint(50, 50) HiControl/Si&e = AeJ Si&e(200, 50) 4 ATa%imos el control al )orm!lario He/Controls/A%%(HiControl) n% S!b ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ HiControl/1eGt = "0tro teGto" n% S!b J7(oK3 Tenga en cuenta 9ue esta acci<n se puede reali.ar si la declaraci<n del objeto utton est@ dentro del mismo @mbito de llamada de la propiedad 4e&t. +or esa ra.<n3 "emos sacado la declaraci<n (iControl del objeto utton fuera del procedimiento de creaci<n din@mica del control3 pero tenga en cuenta tambi:n3 9ue en este caso3 tendremos 168 declarada siempre en memoria la ariable (iControl. %l uso de la funci<n "45pe es siempre m@s seguro. El caso anterior utilizando nicamente la funcin -T%pe quedara como se detalla a continuacin (para este ejemplo, he aadido adems un control Button al formulario 4indo!s, desde el cul cambiaremos la propiedad Te;t del control Button creado en tiempo de ejecucin): ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 Declaramos el ob#eto ,!tton Dim HiControl As AeJ ,!tton 4 Declaramos !n nombre al control (si I!eremos) HiControl/Aame = "btn1" 4 Cambiamos alg!nas %e s!s 2ro2ie%a%es HiControl/1eGt = "#em2lo %e ,otSn" HiControl/'ocation = AeJ ?oint(50, 50) HiControl/Si&e = AeJ Si&e(200, 50) 4 ATa%imos el control al )orm!lario He/Controls/A%%(HiControl) n% S!b ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ C1-2e(He/)in%)orm/Controls("btn1"), ,!tton)/1eGt = "0tro teGto" n% S!b Este ejemplo en ejecucin es el que se muestra en la figura 2. 169 jecucin del ejemplo anterior en &isual #asic ,++- Figura 2 Antes de continuar con el siguiente apartado en el que aprederemos a crear un array de controles, comentar que a la hora de posicionar un determinado control en un formulario 4indo!s, lo podemos hacer con las propiedades Top y *eft, o bien, utilizando la propiedad *ocation. Lo mismo ocurre con las propiedades Pidt? y 5ei'?t que pueden ser sustituidas por la propiedad Si@e. El mismo ejemplo de creacin de controles en tiempo de ejecucin utilizando el mtodo *ocation y Si@e, quedara como se detalla a continuacin: ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 Declaramos el ob#eto ,!tton Dim HiControl As AeJ ,!tton 4 Declaramos !n nombre al control (si I!eremos) HiControl/Aame = "btn1" 4 Cambiamos alg!nas %e s!s 2ro2ie%a%es HiControl/1eGt = "#em2lo %e ,otSn" 4 ?ro2ie%a% 'ocation HiControl/'ocation = AeJ ?oint(50, 50) 4 ?ro2ie%a% Si&e HiControl/Si&e = AeJ Si&e(200, 50) 4 ATa%imos el control al )orm!lario 170 He/Controls/A%%(HiControl) n% S!b Hasta ahora hemos visto como crear controles en tiempo de ejecucin, pero en muchas ocasiones, nos es til no slo crear un control en tiempo de ejecucin, sino relacionarlo con un evento. En nuestro ejemplo del control Button, cuando hacemos clic sobre el control, no ocurre nada, y sera interesante mostrar un mensaje o realizar una accin determinada. Para hacer esto, deberemos utilizar el mtodo Add6andler para asignar un evento al control creado dinmicamente. El cdigo de nuestro ejemplo mejorado es el que se detalla a continuacin: ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 Declaramos el ob#eto ,!tton Dim HiControl As AeJ ,!tton 4 Declaramos !n nombre al control (si I!eremos) HiControl/Aame = "btn1" 4 Cambiamos alg!nas %e s!s 2ro2ie%a%es HiControl/1eGt = "#em2lo %e ,otSn" HiControl/'ocation = AeJ ?oint(50, 50) HiControl/Si&e = AeJ Si&e(200, 50) 4 ATa%imos el control al )orm!lario He/Controls/A%%(HiControl) 4 ATa%imos el e5ento ClicQ al control crea%o %in6micamente A%%Han%ler HiControl/ClicQ, A%%ress08 btn1ClicQ n% S!b ?!blic S!b btn1ClicQ(,-$al Sen%er As 0b#ect, ,-$al e As S-stem/5entArgs) 4 Hostramos !n Hensa#e Hessage,oG/ShoJ("So- el Control ,!tton con teGtoD 4" B C1-2e(C1-2e(Sen%er, ,!tton)/1eGt, String) B "4") n% S!b Nuestro ejemplo en ejecucin es el que se muestra en la figura 3. 171 jecucin del ejemplo con asociacin de evento desarrollado en &isual #asic ,++- Figura 3
%(dulo 1 ? Cap!tulo A A. Creaci(n de una matri* de controles En el captulo anterior, hemos visto como crear controles en tiempo de ejecucin e incluso como asociar un evento a un control creado dinmicamente en Visual Basic 2005, pero, qu ocurre si queremos crear un array o matriz de controles como hacamos en Visual Basic 6?. La respuesta en este caso no puede ser peor. No se puede. 172 Bueno, en realidad s se puede, pero emulando o simulndolo. El hecho de que no se pueda no es porque Visual Basic 6 sea mejor que Visual Basic 2005, sino precisamente todo lo contrario. Visual Basic 6 tena una carencia en el tratamiento de los objetos (hay que recordar que Visual Basic 6 no es un lenguaje de programacin orientado a objetos) y en muchos casos, simulaba acciones de orientacin a objetos que chocaban con la autntica naturaleza del entorno de Visual Basic. Esa carencia est superada por Visual Basic 2005 y obviamente y por eso, algunas cosas y modos de trabajar cambian como estamos viendo. Recordemos que en Visual Basic 6 podamos crear una matriz de controles gracias al uso de la propiedad Inde;. De esa manera, podamos tener por ejemplo un control de nombre 4e&t1708 y otro control de nombre 4e&t1718. En Visual Basic 2005 la propiedad Inde; no existe. Esto significa que para usar una matriz de controles en Visual Basic 2005 deberemos simular la accin de Inde;. Podemos simular una matriz de controles de muchas formas. Yo en este ejemplo, aplicar la siguiente forma de llevar a cabo esta tarea: ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 Declaramos la 5ariable conta%or %el b!cle ?ara Dim I As ,-te 4 Declaramos la 5ariable conta%or %el nWmero %e controles a crear Dim intA!mControles As ,-te = 5 4 Iniciamos el b!cle ?ara )or I = 0 1o intA!mControles C 1 4 Declaramos el ob#eto 1eGt,oG Dim HiControl As AeJ 1eGt,oG 4 'e asignamos !n nombre al control HiControl/Aame = "tGt1" 4 Ftili&amos la 2ro2ie%a% 1ag 2ara almacenar ahK el 5alor %el control %e la matri& 5irt!al HiControl/1ag = I 4 'e asignamos !n tamaTo en el )orm!lario <in%oJs HiControl/Si&e = AeJ Si&e(100, 20) 4 'e asignamos !na 2osiciSn en el 8orm!lario <in%oJs HiControl/'ocation = AeJ ?oint(50, 22 O (I B 1)) 4 'e cambiamos la 2ro2ie%a% 1eGt HiControl/1eGt = HiControl/Aame B "(" B I/1oString() B ")" 173 4 ATa%imos el control al )orm!lario He/Controls/A%%(HiControl) 4 ATa%imos el e5ento ClicQ al control crea%o %in6micamente A%%Han%ler HiControl/ClicQ, A%%ress08 tGt1ClicQ AeGt n% S!b ?!blic S!b tGt1ClicQ(,-$al Sen%er As 0b#ect, ,-$al e As S-stem/5entArgs) 4 Hostramos !n Hensa#e Hessage,oG/ShoJ("Control " B C1-2e(Sen%er, 1eGt,oG)/1ag/1oString()) n% S!b Nuestro ejemplo de demostracin en ejecucin es el que se puede ver en la figura 1. jecucin de la simulacin de una matri4 de controles Figura 1 Obviamente, existen diferentes formas y tcnicas de simular un array o matriz de controles. Sirva esta que hemos visto, como ejemplo de lo que se puede hacer, pero para nada se trata de una norma o regla fija. Debemos destacar que en .NET no existe el concepto de array o matriz de controles como en Visual Basic 6, por lo que sta, es una caracterstica no propia del lenguaje, pero que para los desarrolladores que vienen de Visual Basic 6 anterior, conviene conocer. 174
%(dulo 1 ? Cap!tulo A B. Creaci(n de controles nuevos Ya hemos visto la diferencia ms genrica entre un componente y un control, pero an no sabemos como desarrollar nuestros propios controles en Visual Basic 2005. En primer lugar y antes de adentrarnos en la creacin de nuestros propios controles con Visual Basic 2005, debo indicarle que debemos obviar todo lo relacionado con los ActiveX OCX y ActiveX en general. En Visual Basic 2005, la palabra ActiveX ya no existe. El modelo de programacin ha cambiado y por eso, los componentes y controles se generan ahora siguiendo otras normas que aprenderemos a utilizar de forma inmediata. Iniciaremos #isual Basic 2BBC %&press y seleccionaremos un proyecto de tipo Bi#lioteca de clases. En el nombre de proyecto, podemos indicarle el nombre que deseemos tal y como se muestra en la figura 1, y a continuacin pulsaremos el botn 7R. 175 !eleccin de nuevo proyecto #iblioteca de clases en &isual #asic ,++- Figura 1 La diferencia mayor que reside entre el desarrollo de componentes y controles en .NET, es que en lugar de heredar de la clase -omponent como en el caso de la creacin de los componentes, se ha de heredar de la clase -ontrol o S%stem.Pindos.Forms.Gser-ontrol. El tipo de proyecto seleccionado no posee por defecto como en Visual Basic 6, la superficie contenedora sobre la cul podremos insertar otros controles o realizar las acciones que consideremos pertinentes para crear as nuestro control personalizado. En este caso, la superficie contenedora la deberemos crear aadiendo las referencias necesarias a nuestro programa. Haga clic con el botn derecho del ratn sobre el proyecto o solucin de la ventana E;plorador de soluciones y a continuacin, seleccione la opcin 8ropiedades del men emergente. A continuacin, agrege las referencias a las libreras de clases S%stem./rain' y S%stem.Pindos.Forms. Por ltimo, escriba las siguientes instrucciones bsicas. Im2orts S-stem/Com2onentHo%el Im2orts S-stem/<in%oJs/)orms Im2orts S-stem/DraJing 176 ?!blic Class Class1 Inherits FserControl n% Class En este punto, nuestra clase habr sido transformada en la clase contenedora de un control que es la que puede verse en la figura 2. !uperficie contenedora por defecto de un control Figura 2 An as, sino quiere realizar esta accin, otra forma de tener lista la superficie contenedora del control, es la de eliminar la clase del proyecto y pulsar el botn derecho del ratn sobre la ventana del %&plorador de soluciones y seleccionar la opcin de 4're'ar 6 Nuevo elemento... del men emergente. De las opciones que salen, deberamos seleccionar entonces la plantilla -ontrol de usuario. Sin ms dilacin, y teniendo en cuenta que nuestro control posee una clase principal y una clase parcial, aunque podramos aglutinar todo el cdigo en la clase principal, aadiremos sobre la superficie del control contenedor, dos controles Label y dos controles Te&tBo&. A continuacin, escribiremos el siguiente cdigo: Im2orts S-stem/Com2onentHo%el Im2orts S-stem/<in%oJs/)orms ?!blic Class HiControl ?ri5ate +Acceso As ,oolean 177 (Categor-("Acceso"), Descri2tion("In%ica si se 2ermite o no el acceso"), De8a!lt$al!e(")alse"), 9"ea%0nl-:(1r!e)* + ?!blic ?ro2ert- Acceso() As ,oolean ;et "et!rn +Acceso n% ;et Set(,-$al $al As ,oolean) +Acceso = $al n% Set n% ?ro2ert- ?ri5ate S!b FserControl1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% +Acceso = )alse n% S!b ?!blic S!b $ali%ar() I8 1eGt,oG1/1eGt = "e#em2lo" An% 1eGt,oG2/1eGt = "e#em2lo" 1hen +Acceso = 1r!e lse +Acceso = )alse n% I8 n% S!b n% Class Observando el cdigo, vemos que hemos creado una propiedad 4cceso que nos permitir saber si un usuario y contrasea han sido validadas o no. Ahora nuestro control, est listo ya para ser compilado y probado en una aplicacin Windows. Para compilar nuestro control, haga clic en el men Oenerar 6 Oenerar -lass*i#rar%$ como se muestra en la figura 3. 178 :pcin para compilar nuestro control Figura 3 A continuacin aada un nuevo proyecto 4plicacin para Pindos a la solucin y establzcalo como proyecto inicial de la solucin. Acuda a la barra de herramientas y busque el control que hemos creado y compilado para insertarlo en el formulario Windows. Sino aparece en la barra de herramientas, deber aadirlo de la siguiente manera. Haga clic sobre la barra de herramientas y seleccione la opcin Ele'ir Elementos... del men emergente que aparece en la figura 4. :pcin para a2adir un control o componente a la barra de %erramientas Figura 4 Aparecer una ventana para buscar el control ya compilado en el disco duro. Pulsaremos el botn E;aminar... y buscaremos nuestro control para seleccionarlo. Una vez hecho esto, tal y como se indica en la figura 5, haremos clic sobre el botn 7R. 179 !eleccin del control compilado anteriormente Figura 5 Nuestro control quedar insertado en en la barra de herramientas como se muestra en la figura 6. Control insertado en la barra de %erramientas 180 Figura 6 Para insertar el control en el formulario, haremos doble clic sobre l. Este quedar dispuesto en el formulario Windows como se indica en la figura 7. Control insertado en el formulario 6indo7s de prueba Figura 7 Nuestro formulario Windows de prueba en ejecucin con el control insertado en l, es el que puede verse en la figura 8. (ormulario 6indo7s de prueba en ejecucin con el control insertado Figura 8 Como ha podido comprobar, la creacin de controles en Visual Basic 2005, tampoco requiere de una gran habilidad o destreza, y su similitud con la creacin de componentes es enorme. De hecho, todo se reduce en el uso y programacin de una clase con la salvedad de que dentro de esa clase, indicamos si se trata de una clase como tal, la clase de un componente o la clase de un control.
181
-ntroducci(n Trabajar con grficos e imgenes es una de las particularidades aadidas a Visual Basic 2005 y que ms ha cambiado respecto a Visual Basic 6. En este captulo veremos las partes ms generales en el uso y generacin de imgenes y grficos con Visual Basic 2005. Pasaremos de puntillas sobre el uso de DirectX para la generacin de grficos 3D y nos adentraremos un poco ms profundamente, en el desarrollo de grficos e imgenes 2D con GDI+. %(dulo 1 ? Cap!tulo B
1. Grficos 3D 2. Grficos 2D 3. Dibujando lneas con GDI+ 4. Dibujando curvas con GDI+ 5. Dibujando cadenas de texto con GDI+ 6. Otras consideraciones 182
%(dulo 1 ? Cap!tulo B @. 5r&ficos A" Para trabajar con imgenes y grficos en 3D, deberemos utilizar DirectX, ya que dentro de la plataforma .NET de Microsoft, no tenemos la posibilidad de crear representaciones 3D. Esto significa por otra parte, que si desarrollamos una aplicacin con representacin 3D con DirectX, deberemos distribuir tambin las libreras DirectX en la mquina en la que se ejecute nuestra aplicacin. Es decir, no bastar con distribuir Microsoft .NET Framework. Pero DirectX no nos permite slo crear grficos e imgenes en 3D, tambin podemos crear imgenes en 2D, pero lo ms normal en este ltimo caso y salvo que no requeramos un uso continuado de DirectX, ser utilizar en su lugar GDI+, como veremos ms adelante. Para trabajar con DirectX en Visual Basic 2005, deberemos aadir una referencia al proyecto con la librera o libreras COM de DirectX, eso si trabajamos con DirectX 8 o anterior, ya que a partir de DirectX 9, Microsoft ha proporcionado un conjunto de clases y libreras que interactan con .NET directamente, permitindonos ejecutar aplicaciones .NET con DirectX administrado. En nuestro caso, lo mejor es realizar un ejemplo, no sin antes recordar, que debera descargar Microsoft DirectX SDK 9 e instalarlo en su sistema. El SDK contiene ficheros de ayuda y documentacin que le ensear a crear aplicaciones con DirectX 9 en Visual Basic 2005. F4N3 Dnde puedo descargar Microsoft DirectX 9.0 SDK? La ;ltima ersi<n de DirectJ SDK la encontrar@ en la p@gina !eb de (SD$ de DirectJ3 especialmente creada para desarrolladores de 183 aplicaciones y juegos. Descargas de Microsoft DirectX Una vez que tenemos instalado en nuestro sistema las libreras de desarrollo de DirectX para .NET, iniciaremos una nueva aplicacin Windows, aadiremos las referencias a las libreras (icrosoft.DirectJ y (icrosoft.DirectJ.Direct3D tal y como se muestra en la figura 1 y las cules encontraremos normalmente en el directorio c=L!indo!sLsystem32L. 3eferencias a DirectA a2adidas a nuestro proyecto Figura 1 A continuacin, escribiremos el siguiente cdigo: Im2orts Hicroso8t/DirectL Im2orts Hicroso8t/DirectL/Direct3D ?!blic Class )orm1 ?!blic S!b CreateDe5ice() Dim 22 As AeJ ?resent?arameters() 22/<in%oJe% = 1r!e 22/SJa288ect = SJa288ect/Discar% Dim %5 As AeJ De5ice(0, De5ice1-2e/Har%Jare, He, Create)lags/So8tJare$erteG?rocessing, 22) 184 Dim 5ertices(6) As C!stom$erteG/1rans8orme%Colore% 5ertices(0)/Set?osition(AeJ $ector.(He/<i%th V 2/0), N0/0), 0/5), 1/0))) 5ertices(1)/Set?osition(AeJ $ector.(He/<i%th C (He/<i%th V 5/0)), He/Height C (He/Height V 5/0)), 0/5), 1/0))) 5ertices(2)/Set?osition(AeJ $ector.(He/<i%th V 5/0), He/Height C (He/Height V 5/0)), 0/5), 1/0))) 5ertices(3)/Set?osition(AeJ $ector.(He/<i%th V 2/0), 50/0), 0/5), 1/0))) 5ertices(.)/Set?osition(AeJ $ector.(He/<i%th C (He/<i%th V 5/0)), He/Height C (He/Height V 5/0)), 0/5), 1/0))) 5ertices(5)/Set?osition(AeJ $ector.(He/<i%th V 5/0), He/Height C (He/Height V 5/0)), 0/5), 1/0))) %5/Clear(Clear)lags/1arget, S-stem/DraJing/Color/,l!e$iolet, 2/0), 0) %5/,eginScene() %5/$erteG)ormat = $erteG)ormats/1rans8orme% %5/DraJFser?rimiti5es(?rimiti5e1-2e/1riangle'ist, 2, 5ertices) %5/n%Scene() %5/?resent() n% S!b ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) CreateDe5ice() n% S!b n% Class Este pequeo ejemplo demostrativo en ejecucin del uso de DirectX desde nuestras aplicaciones Visual Basic 2005, es el que puede verse en la figura 2 185 jemplo de DirectA con &isual #asic ,++- en ejecucin Figura 2
%(dulo 1 ? Cap!tulo B 1. 5r&ficos 1" Las APIs de GDI+ corresponden a la evolucin natural de las APIs y libreras GDI que se utilizaban en otros lenguajes de desarrollo. GDI+ no es otra cosa que un conjunto 186 de clases desarrolladas para el entorno .NET y por esa razn, podemos entonces dibujar y crear representaciones grficas en Visual Basic 2005. Todas las clases GDI+, pueden ser localizadas a travs del nombre de espacio S%stem./rain'. As, podemos acceder a todas las posibilidades que nos ofrece GDI+ y las cuales veremos ms adelante. GDI+, no accede al hardware directamente, interacta con los driers de los dispositivos grficos. Como particularidad, debemos saber que GDI+ est soportado por Win32 y Win64. Respecto a los sistemas operativos y el soporte de estos para GDI+, Windows XP contiene la librera 'diplus*dll que encontraremos normalmente en c=L!indo!sLsystem32L y la cul nos permite trabajar con GDI+. Microsoft .NET por otra parte, nos proporciona el acceso directo a esta librera para poder desarrollar nuestras aplicaciones de forma mucho ms cmoda y sencilla. F4N3 Dispone mi sistema operativo de GDI+? (icrosoft .$%T )rame!or6 instala autom@ticamente en su sistema las librerIas 1DIM para 9ue las pueda utili.ar en sus aplicaciones. Descargas de Microsoft DirectX
%(dulo 1 ? Cap!tulo B A. "ibuando l!neas con 5"-D Lo primero que aprenderemos a representar con GDI+ y Visual Basic 2005, son lneas muy sencillas. '!neas simples 187 Cuando representamos lneas, debemos tener en cuenta varios aspecto, similares por otra parte, a Visual Basic 6. Una lnea est representada por dos puntos. Cuando la representamos en un plano, La representacin de una lnea tiene dos pares de puntos (esto me recuerda a mis tiempos de estudiante con el lgebra y el clculo). Para representar una lnea por lo tanto, debemos indicar un par de puntos (x,y) cuyas coordenadas (horizontal,vertical), representa en este orden, el lugar o punto de inicio indicado por el margen superior del formulario o superficie sobre la cul deseamos dibujar nuestra lnea, y un segundo par de puntos que representan la direccin final de nuestra lnea. Un ejemplo prctico de este tipo de representacin es la que se detalla en el siguiente cdigo fuente de ejemplo: Im2orts S-stem/DraJing ?!blic Class )orm1 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) e/;ra2hics/DraJ'ine(AeJ S-stem/DraJing/?en(Color/DarQ,l!e, 2), 1, 1, 50, 50) n% S!b n% Class Atendiendo al cdigo, observamos que hemos representado la grfica indicando que queremos dibujar una lnea e.1rap"ics.Dra!Line indicando posteriormente una serie de atributos como el color del lpiz y tamao o grosor de ste +en/Color.Dar6Blue3 20 y un conjunto de parmetros (x,y) que representan las coordenadas de la lnea (1,1) y (50,50). Otra representacin similar sera e.1rap"ics.Dra!Line/+ens.Dar6Blue3 H3 H3 CB3 CB0, con la salvedad de que en este caso, el grosor del lpiz es el grosor por defecto, que es 1. De todos los modos, la declaracin e.1rap"ics.Dra!Line/$e! System.Dra!ing.+en/Color.Dar6Blue3 203 H3 H3 CB3 CB0 y e.1rap"ics.Dra!Line/$e! System.Dra!ing.+en/Color.Dar6Blue3 203 CB3 CB3 H3 H0 en el caso de la representacin de lneas es igualmente compatible. Este ejemplo de prueba en ejecucin es el que se puede ver en la figura 1. 188 jemplo de dibujo con <DIB de una l@nea recta en un formulario 6indo7s Figura 1
'!neas personali*adas Sin embargo, la representacin de lneas con GDI+ tiene diferentes particularidades que nos permiten sacar un alto grado de personalizacin a la hora de pintar o representar las imgenes y grficos en nuestras aplicaciones. La representacin de lneas con GDI+, nos permite entre otras cosas, personalizar no slo el color y el grosor de una lnea, sino otras caractersticas de sta como por ejemplo los extremos a dibujar. As, podemos dibujar unos extremos ms o menos redondeados, puntiagudos, o personalizados. Esto lo conseguimos hacer mediante las propiedades .tart"ap y %nd"ap de la clase -en que es lo que vulgarmente he denominado como lpiz. A estas propiedades, las podemos dar diferentes valores. Sirva el siguiente ejemplo de muestra de lo que podemos llegar a hacer. Im2orts S-stem/DraJing ?!blic Class )orm1 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) Dim 'a2i& As AeJ ?en(Color/DarQ,l!e, 10) 'a2i&/StartCa2 = DraJing2D/'ineCa2/Diamon%Anchor 'a2i&/n%Ca2 = DraJing2D/'ineCa2/ArroJAnchor e/;ra2hics/DraJ'ine('a2i&, 10, 10, 1.0, 1.0) n% S!b 189 n% Class Demostracin de como representar diferentes e5tremos en una l@nea con <DIB Figura 2
Tra*ando caminos o rutas de l!neas Otra posibilidad de GDI+ es la de crear lneas entrelazadas sin llegar a cerrarlas. Esto se hace con el mtodo Add9ine. De esta manera, podemos dibujar diferentes lneas para representarlas en el marco de trabajo. El siguiente ejemplo, nos ensea a utilizar el mtodo Add9ine. Im2orts S-stem/DraJing ?!blic Class )orm1 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) Dim "!ta As AeJ DraJing2D/;ra2hics?ath() "!ta/Start)ig!re() "!ta/A%%'ine(AeJ ?oint)(10, 10), AeJ ?oint)(100, 10)) "!ta/A%%'ine(AeJ ?oint)(10, 10), AeJ ?oint)(1N0, 100)) "!ta/A%%'ine(AeJ ?oint)(1N0, 100), AeJ ?oint)(130, 50)) Dim 'a2i& As AeJ ?en(Color/DarQ,l!e, .) e/;ra2hics/DraJ?ath('a2i&, "!ta) 190 n% S!b n% Class En la figura 3 podemos ver el resultado de crear diferentes lneas en Visual Basic 2005 con GDI+. jecucin del ejemplo de uso del mtodo AddLine con &isual #asic ,++- Figura 3 Observando el cdigo, vemos que hemos declarado el mtodo .tart:i'ure y el mtodo Add9ine de la clase 0rap,ics-at,. Finalmente y para representar la grfica correspondiente, hemos llamado al mtodo Draw-at,.
'!neas con te;turas Otra de las caractersticas de la representacin grfica de lneas con Visual Basic 2005 y GDI+, es la posibilidad de representar lneas aplicando a esas lneas una determinada textura. El siguiente ejemplo, aplica una textura de la bandera de la Comunidad Econmica Europea a un camino de lneas. Im2orts S-stem/DraJing ?!blic Class )orm1 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) Dim Imagen As AeJ ,itma2("cDU)lag/bm2") Dim Ce2illo As AeJ 1eGt!re,r!sh(Imagen) 191 Dim 1eGt!ra?incel As AeJ ?en(Ce2illo, 20) Dim "!ta As AeJ DraJing2D/;ra2hics?ath() "!ta/Start)ig!re() "!ta/A%%'ine(AeJ ?oint)(10, 10), AeJ ?oint)(1X0, 50)) "!ta/A%%'ine(AeJ ?oint)(10, 100), AeJ ?oint)(1X0, 1.0)) e/;ra2hics/DraJ?ath(1eGt!ra?incel, "!ta) n% S!b n% Class Como podemos observar, lo primero que hacemos es cargar una imagen que ser la textura que utilizaremos, dentro de un objeto it)ap, para posteriormente, preparar el trazo con su textura y tamao. Luego creamos una ruta o camino que marcaremos para dibujarla en el formulario Windows en nuestro caso, tal y como puede verse en la figura 4. jemplo de un tra4o de l@nea aplicando te5turas Figura 4
192
%(dulo 1 ? Cap!tulo B B. "ibuando curvas con 5"-D La representacin de curvas con GDI+ tiene cierta similitud con la representacin de lneas. A continuacin veremos las partes ms destacables en la creacin de trazos curvos con Visual Basic 2005 y GDI+.
Tra*ando curvas simples Lo ms sencillo de todo es siempre el trazo de una lnea curva genrica con Visual Basic 2005. Esto lo conseguiremos con el siguiente cdigo: Im2orts S-stem/DraJing ?!blic Class )orm1 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) Dim ?!ntos As ?oint)() = =AeJ ?oint)(10, Hath/Sin(1) O 100), + 193 AeJ ?oint)(60, Hath/Sin(0) O 100), + AeJ ?oint)(110, Hath/Sin(1) O 100), + AeJ ?oint)(160, Hath/Sin(0) O 100), + AeJ ?oint)(210, Hath/Sin(1) O 100)> e/;ra2hics/DraJC!r5e(AeJ ?en(Color/DarQ0range, .), ?!ntos, 2/0)) n% S!b n% Class Si observamos este cdigo, veremos que lo que hacemos es crear un conjunto de puntos para representar los trazos o lneas rectar que unan los puntos. Esto lo conseguimos utilizando la clase -oint:. Posteriormente utilizamos el mtodo Draw"ur$e con la salvedad de que el tercer parmetro, indica la tensin de la curva. Si este valor recibe un B.B), la curva no tendr tensin y por lo tanto, la representacin ser en trazos rectos, mientras que si ese valor aumenta, la tensin aumenta producindose el efecto curvo que deseamos conseguir. El ejemplo anterior en ejecucin es el que se muestra en la figura 1 jemplo en ejecucin de la representacin "r>fica de tra4os curvos Figura 1
Curvas de .0*ier Otra posibilidad que nos ofrece GDI+ es la representacin de curvas de Bzier, algo que conseguiremos gracias al mtodo Drawe;ier de GDI+. La representacin de las curvas de Bzier pueden hacerse mediante dos pares de puntos (x,y) o mediante cuatro coordenadas de puntos que definen la asignacin de las curvas a representar. El siguiente ejemplo nos muestra como representar una curva de Bzier con Visual Basic 2005 y GDI+. 194 Im2orts S-stem/DraJing ?!blic Class )orm1 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) e/;ra2hics/DraJ,e&ier(AeJ ?en(Color/DarQ,l!e, 3), + AeJ ?oint)(10, 10), + AeJ ?oint)(110, N0), + AeJ ?oint)(160, 30), + AeJ ?oint)(210, 110)) n% S!b n% Class Este cdigo en ejecucin es el que puede verse en la figura 2. 3epresentacin de las curvas de #4ier con <DIB en &isual #asic ,++- Figura 2
Eellenando curvas En algunas ocasiones, nos podemos ver con la necesidad de crear curvas y de rellenar su interior para destacarlo de alguna manera. Esto es lo que veremos a continuacin. 195 El mtodo AddArc nos permite aadir una serie de puntos para representar una circunferencia. En realidad, se representan puntos, los cuales son el punto (x,y) inicial, el ancho y el alto de la representacin, el ngulo de comienzo de representacin, y el ngulo final de la representacin. En ejemplo que veremos a continuacin, utilizaremos tambin los mtodos :ill-at, y Draw-at,, para representar la curva en el formulario. El cdigo del ejemplo es el que se detalla a continuacin: Im2orts S-stem/DraJing ?!blic Class )orm1 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) He/,acQColor = Color/'ight,l!e Dim Camino As AeJ DraJing2D/;ra2hics?ath() Camino/A%%Arc(50, 0, 150, 150, 0, 1X0) e/;ra2hics/)ill?ath(,r!shes/<hite, Camino) e/;ra2hics/DraJ?ath(?ens/,lacQ, Camino) n% S!b n% Class En la figura 3, podemos ver el ejemplo en ejecucin. jemplo de un dibujo de curvas cerradas rellenando su interior 196 Figura 3
"ibuando tartas Otra posibilidad que nos ofrece GDI+ es la representacin de las conocidas tartas. Todas estas representaciones son representaciones 2D, pero siempre se puede emular una representacin 3D, superponiendo tartas una encima de otra. El siguiente ejemplo, utiliza los mtodos :ill-ie y Draw-ie para generar un grfico de tarta y rellenarlo de un determinado color. El cdigo de nuestro ejemplo, quedara de la siguiente manera: Im2orts S-stem/DraJing ?!blic Class )orm1 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) e/;ra2hics/)ill?ie(,r!shes/'ight,l!e, 50, 20, 120/0), 120/0), 115/0), 1X0/0)) e/;ra2hics/DraJ?ie(?ens/DarQ,l!e, 50, 20, 120/0), 120/0), 115/0), 1X0/0)) e/;ra2hics/)ill?ie(,r!shes/<hite, 50, 20, 120/0), 120/0), 0/0), 115/0)) e/;ra2hics/DraJ?ie(?ens/DarQ0range, 50, 20, 120/0), 120/0), 0/0), 115/0)) n% S!b n% Class Nuestro ejemplo en ejecucin, es el que se muesta en la figura 4. 197 Demostracin de cmo crear "r>ficos de tartas y como rellenar su interior Figura 4 -onse(o3 Cuando represente gr@ficas de tartas por ejemplo y desee rellenar una parte de esta de un color y marcar el borde en otro color3 use primero el m:todo )ill+ie y despu:s el m:todo Dra!+ie3 en caso contrario3 la representaci<n gr@fica perder@ nitide. como se indica en la siguiente imagen= 198
%(dulo 1 ? Cap!tulo B 3. "ibuando cadenas de te;to con 5"-D Como hemos visto ya en el captulo referente a la creacin de nuestros propios controles, desde Visual Basic 2005, podemos utilizar GDI+ para crear cadenas de texto y manipularlas como deseemos. En los siguientes apartados veremos como representar cadenas de texto desde Visual Basic 2005
"ibuando cadenas de te;to El mtodo Draw.trin' nos permite representar cadenas de texto de forma grfica. El funcionamiento en Visual Basic 2005 de estas instrucciones es realmente simple. El siguiente ejemplo, ilustra en cmo abordar un pequeo proyecto para representar una cadena de texto en pantalla. Im2orts S-stem/DraJing ?!blic Class )orm1 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) 199 Dim Hi)!ente As AeJ )ont("$er%ana", 2., )ontSt-le/,ol%) Dim ,rocha As AeJ Soli%,r!sh(Color/,!rl-<oo%) e/;ra2hics/DraJString("#em2lo ;DIB", Hi)!ente, ,rocha, 10, 10) n% S!b n% Class Este ejemplo en ejecucin es el que puede verse en la figura 1. jemplo de cmo insertar una cadena de te5to "r>ficamente en un formulario Figura 1
"ibuando cadenas de te;to con te;tura Otra particularidad que a estas alturas ya no lo es tanto, es la posibilidad de trabajar con texturas dentro de cadenas de texto. Como si estuviramos dibujando una cadena de texto, la nica variacin es que asignamos como brocha de dibujo, una textura determinada. El siguiente cdigo, aclarar suficientemente esto que comento. Im2orts S-stem/DraJing ?!blic Class )orm1 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) 200 Dim Imagen As AeJ ,itma2("cDU)lag/bm2") Dim 1eGt!raDe)on%o As AeJ 1eGt!re,r!sh(Imagen) Dim Hi)!ente As AeJ )ont("Arial", 30, )ontSt-le/,ol%) e/;ra2hics/DraJString("#em2lo ;DIB", Hi)!ente, 1eGt!raDe)on%o, ., 10) n% S!b n% Class El resultado de este ejemplo, es el que puede verse en la figura 2. Demostracin del efecto de a2adir una te5tura a la representacin de una cadena de te5to Figura 2
201 %(dulo 1 ? Cap!tulo B C. $tras consideraciones :so de degradados con 5"-D GDI+ proporciona un variadsimo juego de brochas que nos permiten dibujar degradados en un formulario o control que permita el trabajo con grficos. El siguiente ejemplo de cdigo, nos muestra como dibujar un degradado en un formulario Windows. Im2orts S-stem/DraJing ?!blic Class )orm1 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) Dim )orma As AeJ "ectangle(AeJ ?oint(0, 0), He/ClientSi&e) Dim ;ra%iente As AeJ DraJing2D/'inear;ra%ient,r!sh()orma, + Color/C-an, + Color/DarQ,l!e, + DraJing2D/'inear;ra%ientHo%e/)orJar%Diagonal) e/;ra2hics/)ill"egion(;ra%iente, AeJ "egion()orma)) n% S!b n% Class La figura 1 representa el ejemplo anterior en ejecucin 202 3epresentacin "r>fica de un de"radado en una ventana 6indo7s Figura 1 Evidentemente, podemos jugar con la clase 9inear0radientrus, y con la lista enumerada 9inear0radient<ode para dar una aspecto o un toque ligeramente diferente al degradado, como el que se indica en la figura 2. :tro ejemplo de representacin de"radada en un formulario 6indo7s Figura 2
-nsertando y trabaando con im&genes con System."raFing Sirva como detalle general, que GDI+ nos permite tambin, trabajar con imgenes. Para cargar una imagen en un formulario con GDI+, utilizaremos el mtodo DrawI)a'e. Un ejemplo del uso de este mtodo para cargar una imagen en un formulario es el siguiente: Im2orts S-stem/DraJing 203 ?!blic Class )orm1 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) e/;ra2hics/DraJImage(AeJ ,itma2("cDU15E/#2g"), 1, 1) n% S!b n% Class La figura 3 nos muestra el resultado final de insertar una imagen en el formulario Windows. Ima"en insertada con <DIB dentro del formulario 6indo7s Figura 3
Aplicando transparencias a una imagen Otra de las caractersticas que nos ofrece GDI+, es el trabajo con imgenes aplicando transparencias. Para realizar esto, deberemos indicar el color o colores que queremos utilizar para provocar en l un efecto transparente. El siguiente ejemplo, nos muestra como hacer esto utilizando para ello el mtodo <a=e4ransparent. Im2orts S-stem/DraJing ?!blic Class )orm1 204 ?rotecte% 05erri%es S!b 0n?aint(,-$al e As S-stem/<in%oJs/)orms/?aint5entArgs) H-,ase/0n?aint(e) Dim Imagen As AeJ ,itma2("cDU)lag/bm2") e/;ra2hics/DraJImage(AeJ ,itma2(Imagen), 1, 1) Imagen/HaQe1rans2arent(Color/)romArgb(255, 0, 51, 153)) e/;ra2hics/DraJImage(AeJ ,itma2(Imagen), 100, 1) n% S!b n% Class Ima"en insertada con <DIB dentro del formulario 6indo7s Figura 4 205
-ntroducci(n En este captulo, aprenderemos lo que son los ensamblados o assemblies, un trmino completamente nuevo para los desarrolladores de Visual Basic 6, y aprenderemos a desplegar o instalar nuestras aplicaciones. Siempre que terminamos de desarrollar un proyecto, nos aborda la duda y pregunta casi obligada de Ny a"ora 9u:O. Eso es lo que veremos en este captulo... el 9u:. %(dulo 1 ? Cap!tulo 3
1. Desmitificando los ensamblados 2. Desplegando con XCOPY 3. GAC y Strong Names 4. Creando un paquete de instalacin 5. Otras consideraciones
206
%(dulo 1 ? Cap!tulo 3 @. "esmitificando los ensamblados Un concepto completamente nuevo para un desarrollador de Visual Basic 6, es la palabra Asse)!l5, denominada ensamblado. Los ensamblados, son para entenderlo muy rpidamente, como los ejecutables de una aplicacin. La nica diferencia notable, es que en .NET, un proyecto o aplicacin, se compila en cdigo intermedio, el conocido como Intermediate Language o lenguaje intermedio, que luego interpretar el CLR o Common Language 'untime para ejecutarla en el sistema operativo correspondiente. Ese cdigo intermedio, es el ensamblado de nuestra aplicacin que a su vez puede contener uno o ms ficheros, y a su vez, un proyecto, puede estar contenido por uno o ms ensamblados. Por lo tanto, dentro de un ensamblado, se encierran algunas partes importantes que debemos conocer. Lo mejor para entender bien lo que hay en un ensamblado y que contiene es que abramos Visual Basic 2005 Express y seleccionemos una plantilla de proyecto de tipo "onsole Application como se muestra en la figura 1. 207 !eleccionando un proyecto de tipo Console Application Figura 1 A continuacin, escribiremos un ejemplo simple de consola, para que estudiemos el resultado de ste. El cdigo fuente de nuestra pequea aplicacin de ejemplo, es el que se detalla a continuacin: Ho%!le Ho%!le1 S!b Hain() Console/<rite'ine("#em2lo %e consola") Console/<rite'ine("") Console/<rite("(?!lse nter 2ara terminar*") Console/"ea%'ine() n% S!b 208 n% Ho%!le Nuestro ejemplo de prueba en ejecucin, es el que puede verse en la figura 2. jecucin del ejemplo de Consola Figura 2 Ahora bien, lo que tenemos una vez compilamos nuestra aplicacin de consola, no es un ejecutable como tal o como lo entendramos en Visual Basic 6. En Visual Basic 6, generbamos un ejecutable nativo al sistema en el cul compilbamos el proyecto y si ese ejecutable lo llevbamos a otra mquina con otro sistema operativo diferente a Windows, ese programa no iba a funcionar. Con .NET y en nuestro caso con Visual Basic 2005, este concepto ha cambiado. El proyecto que hemos desarrollado no se compila a un ejecutable nativo, sino que el sistema .NET lo compila a un lenguaje intermedio, que luego el "93 de la mquina en la cul lanzamos nuestra aplicacin, ser capaz de interpretar adecuadamente. Puede que estos conceptos le puedan desorientar un poco, pero es muy fcil de comprender. Adicionalmente a sto, lo suyo es destripar un poco lo que hay dentro del ensamblado que hemos convertido a cdigo intermedio. Para llevar a cabo nuestra tarea, haremos uso de una herramienta externa de Microsoft y del entorno Visual Basic 2005 Express, que nos permite analizar el ensamblado de un proyecto convertido a cdigo intermedio. 4claracin3 ,n ensamblado o el c<digo intermedio3 /I9 a partir de a"ora03 no es ni el c<digo fuente de nuestra aplicaci<n ni el programa ejecutable. %l I9 se puede anali.ar con una "erramienta de (icrosoft denominada ildas). 209 Cuando abrimos el fichero ejecutable de nuestra aplicacin con la herramienta ildasm.e&e, observamos que esta tiene una gran cantidad de informacin, como se indica en la figura 3. ildasm con el fic%ero ejecutable de nuestra aplicacin de ejemplo abierto Figura 3 Antes de adentrarnos ms en los entresijos de un ensamblado, piense en l como si fuera una coleccin de elementos. Esos elementos, pueden ser recursos, tipos, funcionalidades, que todas juntas, forman nuestra aplicacin. As, lo primero que vemos en la figura 3, es la palabra M4NIFIEST. Referencias a las libreras utilizadas que el -*B deber interpretar posteriormente. Luego encontramos otras el mbito de la aplicacin y las clases de sta, con sus mtodo tanto estticos como no estticos. 210 No es cuestin de entrar mucho ms en detalle del cdigo I* que contiene un proyecto compilado en .NET como ste, pero es conveniente a verlo para entender los conceptos que estamos tratando. Los ensamblados como podemos ver, contiene ms informacin de la que propiamente tendra un ejecutable normal como lo conocemos los desarrolladores de Visual Basic 6. Un ensamblado en .NET, contiene tambin como hemos podido ver, datos e informacin que encontraramos en cualquier tipo de librera, lo cul representa adems, toda la informacin necesaria del -*B en cualquier momento. Con todo esto, podemos resumir por lo tanto, que un ensamblado contiene cdigo, recursos y metadatos. El cdigo en I* es el cdigo que ser ejecutado por el -*B. Adems, cualquier recurso (imgenes, metadatos, etc) son accesibles en el ensamblado. Los metadatos por su parte, contiene informacin sobre las clases, interfases, mtodos y propiedades, que posibilitan toda la informacin necesaria por el -*B para poder ejecutar nuestra aplicacin correctamente.
%(dulo 1 ? Cap!tulo 3 1. "esplegando con GC$,H Notas previas Todas las aplicaciones desarrolladas con .NET estn aisladas, no como ocurra en Visual Basic 6, de manera tal que los conflictos con las DLL se han visto reducidos enormemente, por no decir que han desaparecido. El famoso infierno de las DLLs que tanto hemos sufrido los desarrolladores de Visual Basic, ha pasado ya a la historia. 211 Al igual que en Visual Basic, puedes usar componentes privados. Basta con copiarlos al mismo directorio en el que se encuentra nuestra aplicacin ejecutable. Adems, .NET nos permite tener ms de un componente versionado (diferentes versiones de un mismo componentes) dentro de un mismo ordenador, por lo que los problemas de compatibilidad estaran resueltos. Con estos detalles, repasamos algunas de las mejoras a la hora de desplegar o instalar una aplicacin desarrollada con Visual Basic 2005 en un sistema. A continuacin, veremos algunas anotaciones adicionales que nos ayudarn a realizar estas y otras acciones.
GC$,H Lo que nos ofrece L-78S a los desarrolladores e ingenieros, es la posibilidad de instalar e implementar nuestra solucin y proyecto a un sistema de una manera rpida, fiable y sin apenas impacto. El mtodo L-78S para desplegar aplicaciones, pasa por alto la posibilidad de implementar los ensamblados en la O4-, algo que segn determinadas circunstancias, resulta ms que provechoso. Hay que tener en cuenta, que si hacemos un mal uso de la O4-, sta puede convertirse en un desvn difcil de gestionar. Utilice la O4- con criterio y si no quiere complicaciones, despliegue sus aplicaciones con L-78S. Como habr podido ya observar, dentro de un proyecto de Visual Basic 2005, nos podemos encontrar con una extensa estructura de directorios. Esta estructura, lejos de ser una molestia, constituye las caractersticas ms importantes a la hora de desplegar nuestras aplicaciones a travs de L-78S. Como vemos, todo en .NET tiene su sentido y tiene un porqu. Para instalar nuestra aplicacin desarrollada en Visual Basic 2005 en un sistema cliente, bastar por lo tanto, con realizar una accin similar al L-78S de DOS, es decir, copiaremos en el ordenador cliente, los ficheros o ensamblados necesarios (ejecutables, recursos, dlls, etc.) de la estructura de nuestro proyecto. Debemos tener en cuenta, que en Visual Basic 6, podamos hacer esto igualmente, pero debamos adems registrar las DLL con aquel famossimo comando regsr32 para que no hubiera problemas, an as, algunas veces nos encontrbamos con algunos contratiempos, sin embargo, con Visual Basic 2005, esta forma de trabajar ha desaparecido y ahora el despliegue de una aplicacin es mucho ms sencilla. J7(oK Cuando desarrollemos una aplicaci<n3 tenga en cuenta otros recursos 9ue utili.a en la misma. Crystal 'eports3 bases de datos SEL Serer3 9ue la m@9uina cliente disponga de .$%T )rame!or6 o de la ersi<n mInima de (D*C necesaria3 etc. %n caso contrario3 la ejecuci<n de nuestra aplicaci<n3 podrIa no funcionar o proocar alg;n tipo de e&cepci<n o error. +or ;ltimo3 y aun9ue pare.ca de perogrullo3 no olide 9ue debe tener los permisos necesarios para instalar y ejecutar los ensamblados y otras partes de Soft!are3 necesarios para ejecutar su aplicaci<n. 212
%(dulo 1 ? Cap!tulo 3 A. 5AC y Strong Names 5AC El O4- o 1lobal *ssembly Cac"e no es otra cosa que un repositorio de ensamblados globales. Imaginemos que ustes todos los das cuando llega a casa de trabajar, lo primero que hace es quitarse los zapatos y ponerse una zapatillas cmodas para estar por casa. Lo lgico en este caso, ser situar un zapatero o un pequeo armarito para que ponga ah las zapatillas, ya que todos los das, hace repetitivamente esta operacin. Obviamente, las zapatillas deben de estar ah y no en la otra punta de la casa, pero para estar en ese zapatero, deber cumplir una serie de requisitos que usted mismo exige, por lo que no todos los zapatos o zapatillas deberan estar ah. El O4- funciona de una manera realmente semejante. En el O4- se incluyen aquellas libreras o ensamblados que son utilizados frecuentemente. Si usted va a desarrollar y distribuir su propio ensamblado, convendra ponerlo en el O4-. Una aplicacin .NET, lo primero que hace cuando se ejecuta, es revisar los ensamblados que va a necesitar, y el primer sitio dnde va a ir a buscarlo es en el O4-. Sino lo encuentra, se pondr a buscarlo en el directorio en el que se encuentra el fichero ejecutable, pero esto repercute en el rendimiento. Si nuestras aplicaciones utilizan frecuentemente unos ensamblados, sera lgico y conveniente ponerlos en el O4-. 213 Y cmo se aade un ensamblado al O4-?. La tarea no es sencilla, ya que para aadirlo debemos realizar algunos pasos, entre los que est el crear un nombre fuerte o .tron' +a)e.
Strong Names Con los Stron' Names, aseguramos un uso seguro de los componentes contenidos en el ensamblado. Hay que tener en cuenta que un ensamblado declarado en la O4- que es llamado por varias aplicaciones, crea una nica instancia. De ah, que crear un Stron' Name para el ensamblado, est ms que justificado. Un Stron' Name, nos asegura por otro lado, que el nombre de un ensamblado es nico y que por lo tanto, al ser nico, no puede ser utilizado por otros ensamblados. Los Stron' Names se generan con un par de claves, una de ellas de carcter pblico y otra de carcter privado, claves que se introducen en fichero de ensamblado de la aplicacin, y que luego al compilar nuestra aplicacin, queda registrada con ese Stron' Name. Para indicar un Stron' Names a un ensamblado, debe crear primero el par de claves (clave pblica y clave privada), que se generar en un fichero con extensin sn!, y modificar posteriormente el fichero *ssemblyInfo.b de su proyecto. En ese archivo, debe aadir una instruccin similar a la siguiente: <Assembly: AssemblyKeyFile("KeyFile.snk")>
214 %(dulo 1 ? Cap!tulo 3 B. Creando un pa6uete de instalaci(n Setup ,roect Otra accin habitual, es que cuando desarrollemos nuestras aplicaciones, generemos diferentes dependencias con otros ensamblados o componentes, para lo cul, la forma ms sencilla de desplegar nuestra aplicacin, es generando un paquete de distribucin que contenga esas dependencias y relaciones. En el caso de Visual Basic 2005 Express, esta opcin no est disponible, aunque s en versiones ms avanzadas de Visual Studio 2005. Con la plantilla Setup +roject que no viene integrada en Visual Basic 2005 Express, crearemos un nuevo proyecto para generar el paquete de distribucin e instalacin de nuestra aplicacin y proyecto. Cuando generamos el paquete de instalacin, debemos tener en cuenta que se generan dos archivos que por lo general tendrn los nombres de Setup.e&e y Setup.msi. La diferencia entre ambos ficheros es que el fichero con extensin e;e instalar 4indo!s Installer si es necesario, mientras que el fichero con extensin msi, no instalar 4indo!s Installer, por lo que si 4indo!s Installer no est presente en la mquina en la que se ejecuta el programa de instalacin, dar un error. Otra consideracin a tener en cuenta cuando generamos el paquete de instalacin, es en el momento de la distribucin, el asegurar que el sistema destino tenga el .NET Framework correspondiente. Su'erencia3 Cmo usar Visual Studio 2005 para distribuir Microsoft .NET Framework? (@s 9ue interesante artIculo d<nde obtendr@ informaci<n adicional sobre c<mo generar un pa9uete de instalaci<n para distribuir .$%T )rame!or6. Using Visual Studio .NET to Redistribute the .NET Framework (en ingls) Respecto al comportamiento de 4indo!s Installer sobre aplicaciones ya instaladas que deseamos desinstalar, el entorno se comporta de manera tal, que si detecta componentes compartidos, estos no son desinstalados del sistema. Otro comportamiento de 4indo!s Installer es el que nos permite echar marcha atrs si el proceso de instalacin se cancela o falla por algn motivo. 4indo!s Installer dejar el sistema en el mismo estado que tena justo antes de realizar la instalacin.
Tipos de despliegues de proyectos Cuando generamos un paquete de instalacin tenemos dentro del entorno de desarrollo Visual Studio varias opciones, como se indica en la imagen 1. As, nos podemos encontrar por lo general con diferentes tipos de despliegues de proyectos. 215 $ipos de desplie"ue de proyectos en &isual !tudio Cno en &isual #asic ,++- 5pressD Figura 1 La plantilla "a! -roject El fichero con extensin -4B es un fichero comprimido que contiene todos los ficheros y recursos necesarios para instalar nuestra aplicacin. Por lo general, este tipo de ficheros son usados para descargarlos de Servidores Web. Por lo general y en nuestro caso, crearemos aplicaciones Windows, y deberamos entonces utilizar la plantilla Setup 8ro(ect. Si utilizamos la plantilla, <er'e <odule -roject, crearemos un paquete instalacin para componentes compartidos. El uso de la plantilla, We! .etup -roject por su parte, nos permitir generar un paquete de instalacin para aplicaciones basadas en Internet o aplicaciones Web. La plantilla, .etup Wi;ard genera un asistente para generar uno de los cuatro anteriores tipos de proyectos de despliegue. Es una forma rpida de generar el proyecto de despliegue.
216
%(dulo 1 ? Cap!tulo 3 3. $tras consideraciones Setup ,roect Ya lo hemos comentado por encima en los captulos anteriores sobre el Despliegue de aplicaciones, pero cuando se procede a instalar y distribuir una aplicacin en otros sistemas, se deben tener en cuenta diferentes aspectos que no podemos pasar por alto. Debemos conocer en primer lugar, la naturaleza del sistema o sistemas destino dnde vamos a implantar nuestro programa. Entendiendo y conociendo bien esto, podremos saber qu aplicaciones Software adicionales necesitaremos para abordar nuestro trabajo adecuadamente. Entre otros, debemos tener en cuenta que el sistema destino dnde se va a ejecutar nuestra aplicacin dispone de los drivers de acceso a datos adecuados, (D*C (versin 2.6 superior), y por supuesto de Microsoft .NET Framework. F4N3 Cuando instalar con Windows Installer y cuando a travs de XCOPY? Interesante artIculo 9ue debate sobre cuando instalar con 4indo!s Installer y cuando con JCO+P. Determining When to Use Windows Installer Versus XCOPY (en ingls) El anterior artculo, nos da un enfoque sobre qu tipo de instalacin o despliegue establecer de nuestra aplicacin desarrollada con Visual Basic 2005.
El concepto ClicI$nce En #isual Basic 2BBC %&press se instroduce un concepto nuevo denominado Clic6Once. 217 Este concepto representa la tecnologa que permite instalar y ejecutar aplicaciones Windows desde un servidor Web con una escasa accin por parte del usuario. Inicie un nuevo proyecto Windows. Dentro del formulario Windows inserte un control Button dentro del cul, inserte un texto identificativo como el que se muestra en la figura 1. Aplicacin 6indo7s de ejemplo para e5plicar el concepto de Clic=:nce Figura 1 A continuacin, escriba el siguiente cdigo fuente: ?!blic Class )orm1 ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ Hessage,oG/ShoJ("#em2lo ClicQ0nce e#ec!ta%o a lasD" P 5bCr'8 P Date/AoJ/1o'ong1imeString) n% S!b n% Class Una vez que ha escrito el cdigo fuente de la aplicacin, ejectela y compruebe que funciona como se espera. Si lo desea, abra el fichero *ssemblyInfo.b e indique la versin de la aplicacin que desee. En mi caso he dejado la versin H.B.B.B que es la versin que aparece por defecto. 218 Despus de esto, genere la aplicacin como se indica en la figura 2. <eneramos la aplicacin para comprobar entre otras cosas que todo est> preparado Figura 2 Por ltimo, pulse el botn derecho del ratn sobre el proyecto y seleccione la opcin 8u#licar... como se indica en la figura 3. :pcin de 9ublicar la aplicacin para ejecutar la tecnolo"@a de distribucin Clic=:nce Figura 3 Al ejecutar la opcin 8u#licar..., el entorno nos muestra una ventana similar a la que se muestra en la figura 4. 219 Asistente de la publicacin de la aplicacin Figura 4 Seleccione una ubicacin para publicar la aplicacin y haga clic en el botn Si'uiente. Aparecer en el asistente entonces, una ventana similar a la que se presenta en la figura 5. 220 &entana del asistente para indicar dnde estar> disponible la aplicacin Figura 5 Pulse el botn Si'uiente. Nuevamente, el asistente mostrar una ltima ventana similar a la que se muestra en la figura 6. 221 &entana final del asistente con un resumen de las opciones seleccionadas Figura 6 El asistente muestra en este caso, una informacin de resumen. Lo que haremos a continuacin, ser pulsar el botn Finali@ar. Con esta accin, nuestra aplicacin est ya publicada en el servidor web, por lo que si abrimos una ventana de nuestro explorador Web y escribimos la direccin en la cul hemos publicado nuestra aplicacin, sta se ejecutar de forma correcta como se indica en la figura 7. 222 Aplicacin 6eb del lan4amiento de la aplicacin 6indo7s a travs del !ervidor 6eb Figura 7 Si pulsamos sobre el botn E(ecutar del navegador web, observaremos que el Servidor Web realiza diferentes acciones de verificacin. La primera accin es una accin de conexin como la que se muestra en la figura 8. La primera accin que se reali4a es una accin de cone5in con el !ervidor 6eb Figura 8 Posteriormente, puede aparecer una ventana de seguridad como la que se indica en la figura 9, siempre y cuando no hayamos realizado un proceso de generacin segura o confiable de la aplicacin, como ha sido el caso. 223 La aplicacin 6indo7s ejecutada a travs del !ervidor 6eb8 debe ser confiable y se"ura Figura 9 En nuestro caso, como sabemos que no hay problema en ejecutar la aplicacin, pulsaremos el botn E(ecutar. Nuestra aplicacin en ejecucin es la que se muestra en la figura 10. Aplicacin 6indo7s en ejecucin Figura 10 Cuando hacemos esto, siempre que ejecutemos la aplicacin, el sistema detectar que aceptamos una vez su seguridad, por lo que siempre se ejecutar sin indicarnos ningn mensaje de seguridad. Ahora bien, supongamos que decidimos modificar parte del cdigo de nuestra aplicacin y que por supuesto, cambiamos la versin de la misma. Acuda antes a la ventana del %&plorador de soluciones y observe que se ha aadido en la ventana un fichero de nombre 4indo!s*pplicationH-TemporaryKey.pf& que 224 corresponde a una llave o clave temporal relacionada con el proyecto publicado. Esto es lo que se muestra en la figura 11. &entana del 5plorador de soluciones con el fic%ero 6indo7sApplication.E$emporaryFey*pf5 a2adido a l Figura 11 Vamos a actualizar nuestra aplicacin y la vamos a cambiar la versin, por lo que ahora escribiremos el siguiente cdigo: ?!blic Class )orm1 ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ Hessage,oG/ShoJ("#em2lo ClicQ0nce e#ec!ta%o a lasD" P 5bCr'8 P + Date/AoJ/1oShortDateString P 5bCr'8 P + Date/AoJ/1o'ong1imeString) n% S!b n% Class La versin de la aplicacin, la he modificado en mi caso a H.H.B.B. El siguiente paso que he hecho es compilar la aplicacin y publicarla nuevamente. Una vez hecho esto, acudimos a la pgina web de la aplicacin y pulsamos nuevamente el botn E(ecutar como se indica en la figura 12. 225 &entana 6eb para ejecutar la aplicacin Figura 12 La ejecucin de la aplicacin se realizar sin problemas de manera sencilla y controlada. Como vemos, la publicacin de aplicaciones Windows a travs de un Servidor Web, lo que se denomina tecnologa de publicacin Clic6Once, es un proceso de publicacin rpido y sencillo, que aporta grandes ventajas y que en otras versiones de .NET trae consigo algunos inconvenientes superados en esta versin de .NET. -omunidad dotNet3 Si 9uiere saber m@s sobre Clic6Once3 le recomiendo la lectura del siguiente artIculo3 escrito en espaAol 9ue complementar@ y ampliar@ la informaci<n de este tutorial. (Diciembre 2004) Implementacin de aplicaciones de Windows Forms con ClickOnce 226
Contenido 'ecci(n @: Colecciones de datos o Los tipos de colecciones de .NET o Las clases base para crear colecciones o Colecciones de tipo generic (en breve) 'ecci(n 1: Streams en .NET o Las clases basadas en Stream o Manejar un fichero usando FileStream o Manejar ficheros con StreamReader y StreamWriter o Cifrar y descifrar un fichero 'ecci(n A: Acceso al sistema de arc#ivos o Las clases de System.IO o Clases para manipular unidades, directorios y ficheros o Clases para leer o escribir en streams o El objeto My: My.Computer.FileSystem 'ecci(n B: Acceso a -nternet o Las clases de System.Net o Acceder a una pgina Web o Acceder a un servidor FTP o Acceso rpido a la red con My.Computer.Network 227
-olecciones de datos A diferencia de Visual Basic 6.0, en la que solo tenemos dos formas de crear colecciones, en la versin 2005 de Visual Basic, tenemos un amplio abanico de tipos de colecciones, desde colecciones genricas (o de uso comn, para no confundir el trmino con las colecciones "generic"), hasta colecciones especializadas, es decir, colecciones que estn pensadas para usarlas de forma muy concreta y que por tanto no nos sirven para usarlas en la mayora de las ocasiones. Empecemos viendo los tres tipos bsicos de colecciones que podemos utilizar en nuestras aplicaciones de .NET.
Nota3 Las colecciones que vamos a ver a continuacin son las colecciones "clsicas" de .NET, (cuyo tipo interno es Object), pero debemos saber que tambin existen colecciones casi con las mismas caractersticas pero que estn definidas en el espacio de nombres System.Collections.Generic, las cuales utilizan la nueva "tecnologa" de los tipos genricos (generic) para almacenar los elementos, (cuyo tipo interno puede ser de cualquier tipo).
228 *os tipos de colecciones de .NET En .NET Framework existen tres tipos principales de colecciones, stas dependen del tipo de interfaz que implementan: Las colecciones basadas en ICollection Las colecciones basadas en la interfaz IList Las colecciones basadas en la interfaz IDictionary Como podemos imaginar, dependiendo del "contrato" firmado por cada una de estos tipos de colecciones, podremos hacer ciertas operaciones con ellas. A continuacin veremos con ms detalle estos tipos de colecciones y cuales son las que podemos usar dependiendo del interfaz que cada una de ellas implemente.
Nota3 La diferencia bsica entre estos tipos de colecciones es cmo estn almacenados los elementos que contienen, por ejemplo, las colecciones de tipo IList (y las directamente derivadas de ICollection) solo almacenan un valor, mientras que las colecciones de tipo IDictionary guardan un valor y una clave relacionada con dicho valor.
Tambin veremos unas clases base que implementan cada una de estas dos interfaces, las cuales las podemos usar como base de nuestras propias colecciones personalizadas.
'as colecciones basadas en -Collection La interfaz ICollection es un caso aparte, ya que realmente todas las colecciones de .NET implementan esta interfaz, de hecho, esta interfaz se deriva de I%numerable que es la que nos permite recorrer las colecciones usando bucles )or %ac". Para hacer una comparacin con Visual Basic 6.0, podramos decir que las colecciones "puramente" basadas en ICollection son las colecciones equivalentes al tipo Collection de VB6. Esta interfaz no la tendremos que usar de forma habitual, ya que realmente el resto de colecciones (e interfaces) tiles ya se derivan de ella, por tanto vamos a centrarnos en las otras dos. Aunque es importante que tengamos en cuenta que el resto de colecciones implementan ICollection, por tanto siempre podremos usar un objeto de este tipo para acceder a cualquier coleccin. 229 Independientemente de que todas las colecciones de .NET estn basadas en esta interfaz, hay ciertos tipos de colecciones que solo implementan esta interfaz, por ejemplo las colecciones de tipo Eueue, Stac6 o Bit*rray, por tanto esas colecciones estarn limitadas a los mtodos expuestos por la interfaz ICollection y los que esas colecciones implementen de forma independiente. Por regla general, los tipos que solo se basan en ICollection suelen ser colecciones que no necesitan de las caractersticas que proporcionan las otras interfaces y, por regla general, nos permiten la manipulacin de los datos de una forma bsica o elemental, de forma que su uso sea para casos muy concretos. Por ejemplo, la clase Eueue nos permite crear fcilmente una coleccin de tipo FIFO (primero en entrar, primero en salir); por otra parte, con la clase Stac6 podemos crear colecciones del tipo LIFO (ltimo en entrar, el primero en salir), de forma que sean muy tiles para crear "pilas" de datos. El caso de la otra clase que hemos comentado: Bit*rray, nos sirve para almacenar valores de tipo "bit", en el que cada valor se almacena como un cero o un uno, de forma que podamos tener una coleccin muy compacta, pero, tambin muy especfica y no de uso general, ya que en este caso particular, los mtodos que implementa esta clase estn enfocados en la manipulacin de valores de tipo Boolean, ()alse y True), aunque internamente se almacenen como valores cero y uno respectivamente.
Nota3 Realmente la clase BitArray no se comporta como una coleccin "normal", ya que el tamao de la misma debemos controlarlo nosotros, al igual que ocurre con los arrays, aunque de forma ms "fcil", mediante la propiedad Length.
'as colecciones basadas en -'ist La interfaz IList se utiliza en las colecciones a las que queremos acceder mediante un ndice, por ejemplo, los arrays realmente est basados en esta interfaz, y tal como pudimos comprobar, la nica forma que tenemos de acceder a los elementos de un array, (y por extensin a los elementos de las colecciones basadas en IList), es mediante un ndice numrico. Existen tres tipos principales de colecciones que implementan esta interfaz: Las de solo lectura, colecciones que no se pueden modificar. Este tipo de colecciones suelen basarse en la clase abstracta 'eadOnlyCollectionBase. Las colecciones de tamao fijo, no se pueden quitar ni aadir elementos, pero si modificarlos. Por ejemplo, las colecciones basadas en *rray son de tamao fijo. 230 Las de tamao variable permiten cualquier tipo de adicin, eliminacin y modificacin. La mayora de las colecciones suelen ser de este tipo, es decir, nos permiten dinmicamente aadir o eliminar elementos. Existe un gran nmero de colecciones en .NET que implementan esta interfaz, (sobre todo las colecciones basadas en controles), entre las que podemos destacar las siguientes: *rrayList, la coleccin "clsica" para este tipo de interfaz. Contiene todos los miembros habituales en este tipo de colecciones. CollectionBase, una clase abstracta para poder crear nuestras propias colecciones basadas en IList. StringCollection, una coleccin especializada que solo puede contener valores de tipo cadena.
La coleccin ArrayList Tal como hemos comentado, el tipo de coleccin que se usa como referencia a la hora de hablar de las colecciones basadas en la interfaz IList, es *rrayList. Esta coleccin permite aadir, eliminar y modificar fcilmente los elementos que contiene. Tambin podemos recorrerlos mediante un bucle )or accediendo a los elementos por medio de un ndice e incluso mediante un bucle del tipo )or %ac". Al igual que ocurre con los arrays, el ndice inferior es siempre el cero y los elementos se almacenan de forma consecutiva, es decir, si aadimos dos elementos a una coleccin de tipo *rrayList (y a las que implementen la interfaz IList), el primero ocupar la posicin cero y el segundo la posicin uno. La ventaja de trabajar con las colecciones es que no debemos preocuparnos de reservar memoria cada vez que vayamos a aadir un nuevo elemento, simplemente usamos el mtodo *dd y asunto arreglado. Lo mismo ocurre a la hora de quitar elementos de una coleccin, no tenemos que preocuparnos demasiado por el espacio dejado al quitar elementos, de eso se encarga el propio .NET, nosotros simplemente debemos llamar al mtodo 'emoe o 'emoe*t indicando respectivamente el elemento a eliminar o el ndice en el que se encuentra almacenado.
Truco3 Si decidimos eliminar varios elementos de una coleccin de tipo IList (o de un array), lo normal es que lo hagamos usando un bucle For; si este es el caso, para evitar una posible excepcin, (realmente no es posible, sino con toda certeza segura), debemos recorrer el bucle desde el final hacia adelante, con idea de que al cambiar el nmero de elementos no falle al intentar a acceder a un elemento que ya no existe. 231
l tipo de datos de almacenamiento de las colecciones Estos elementos internamente estn almacenados como objetos del tipo Object, por tanto podemos aadir cualquier tipo de datos a una coleccin de este tipo, ya que todos los tipos de datos de .NET estn basado en la clase Object. El problema con este tipo de colecciones es que siempre que queramos acceder a uno de los elementos que contiene, debemos hacer una conversin al tipo adecuado, es decir, si en una coleccin de este tipo guardamos objetos de tipo Cliente y queremos acceder a uno de ellos, debemos hacer una conversin (cast) del tipo Object al tipo Cliente, ya que si no lo hacemos y tenemos activada Option Strict (la opcin para las comprobaciones estrictas), se producir un error si hacemos algo como esto: Dim lista As AeJ Arra-'ist lista/A%%(AeJ Cliente("?e2e")) lista/A%%(AeJ Cliente("'ola")) Dim !nCliente As Cliente 4 7rrorY !nCliente = lista(0) /// Por tanto, la ltima lnea deberamos escribirla de una de estas dos formas: 4 Fsan%o C1-2e 2ara hacer la con5ersiSn !nCliente = C1-2e(lista(0), Cliente) /// 4 Fsan%o DirectCast 2ara hacer la con5ersiSn (m6s recomen%able) !nCliente = DirectCast(lista(0), Cliente)
Otro de los problemas que tienen las colecciones "normales" es que en algunos casos, particularmente cuando almacenamos tipos por valor, el rendimiento se ve bastante mermado, ya que el runtime de .NET (el CLR) debe hacer lo que en ingls se conoce como boxing/unboxing, es decir, convertir un tipo por valor en uno por 232 referencia cuando va a guardarlo en la coleccin (boxing), y el proceso inverso cuando lo queremos recuperar (unboxing). De todas formas, el consuelo que podemos tener los que estamos acostumbrados a trabajar con Visual Basic 6.0, es que esto mismo ocurre (o casi) cuando trabajamos con las colecciones de VB6, ya que el tipo de datos de las colecciones es #ariant, que sera ms o menos el equivalente del tipo Object de .NET.
Nota3 Por suerte, en Visual Basic 2005 tenemos otra forma de mejorar el rendimiento de las colecciones, y es mediante las colecciones "generic", de esto, nos ocuparemos ms adelante.
Otras de las ventajas de las colecciones de .NET, no solo las basadas en la interfaz IList, es que proporcionan una gran cantidad de mtodos que nos facilitan la manipulacin de ese tipo de datos. Por ejemplo, tenemos mtodos para clasificar el contenido de las colecciones, (aunque esos objetos deben implementar la interfaz IComparable, tal como vimos en el ltimo ejemplo del captulo de las interfaces), adems tienen mtodos para hacer copias, buscar elementos y muchos etcteras ms.
'as colecciones basadas en -"ictionary El otro grupo de colecciones que podemos encontrar en .NET son las colecciones basadas en la interfaz IDictionary. stas, a diferencia de las colecciones IList, siempre mantienen el par clave/valor, ya que la forma de acceder a los elementos es mediante una clave nica. Por tanto, cada vez que aadimos un elemento a una coleccin de este tipo tendremos que indicar una clave y un valor. Cada valor estar relacionado con su correspondiente clave. Sabiendo esto, es fcil adivinar que si queremos acceder a un elemento, lo normal es que lo hagamos usando la clave indicada al aadirlo. Los que hayan trabajado anteriormente con Visual Basic 6.0, (o lo estn haciendo actualmente), puede que piensen que tambin se podr acceder a cada elemento mediante un ndice numrico, ya que el objeto Collection de VB6, (que an sigue existiendo en Visual Basic 2005), nos permite indicar una clave para cada elemento, y adems de acceder a esos elementos mediante la clave, podemos hacerlo mediante un valor numrico (ndice). Pero en las colecciones basadas en IDictionary, salvo casos muy especiales, siempre accederemos a los valores contenidos mediante la clave y "nunca" mediante un ndice que haga referencia a la posicin dentro de la coleccin, entre otras cosas porque cuando almacenamos valores en este tipo de colecciones, stos no se guardan en el mismo orden en que fueron aadidos. 233
Nota3 Si bien la clase Collection est disponible en la nueva versin de Visual Basic, sta tiene algunas mejoras con respecto a la que tiene VB6, entre esas mejoras estn dos nuevos mtodos que nos facilitarn su uso: el mtodo Clear con el que podemos eliminar todos los elementos de la coleccin y el mtodo Contains, con el que podemos averiguar si un determinado elemento est en la coleccin.
Entre las colecciones basadas en la interfaz IDictionary podemos destacar: 7as"table, es la coleccin por excelencia de las basadas en IDictionary. Los elementos se organizan basndose en el cdigo "as" de las claves. DictionaryBase, es una clase abstracta que podemos usar como base de nuestras propias colecciones de tipo diccionario. ListDictionary, es una coleccin con mayor rendimiento que 7as"table pensada para trabajar con 10 o menos elementos. 7ybridDictionary, es una coleccin especial en la que si hay 10 o menos elementos, se utiliza una coleccin ListDictionary y si contiene ms elementos se utiliza una coleccin 7as"table. SortedList, es una coleccin en la que los elementos estn clasificados por las claves. Internamente utiliza una mezcla entre 7as"table y *rray, segn la forma en que se accedan a esos elementos.
Almacenar valores en una coleccin tipo IDictionary Para aadir nuevos elementos a una coleccin de tipo IDictionary siempre tendremos que indicar la clave y el valor, la clave no puede ser un valor nulo, ($ot"ing), pero puede ser de cualquier tipo. El valor tambin puede ser de cualquier tipo y en este caso si que se admiten valores nulos. Los elementos los aadiremos usando el mtodo *dd, al que habr que indicar primero la clave y despus el valor: Dim 5alores() As String = ="!no", "%os", "tres"> Dim %ic As AeJ S-stem/Collections/Hashtable )or i As Integer = 0 1o 5alores/'ength C 1 %ic/A%%(5alores(i), "l 5alor %e " P 5alores(i)) 234 AeGt
Cmo se almacenan los elementos de las colecciones IDictionary Tal como hemos comentado, las colecciones que implementan la interfaz IDictionary siempre almacenan un par de datos: la clave y el valor propiamente dicho, por tanto cada vez que queramos acceder a un valor, debemos usar la clave asociada con dicho valor. Al menos esa es la forma habitual, ya que como veremos, tambin podremos acceder a esos valores directamente. Debido a esta caracterstica, para acceder a los elementos de este tipo de colecciones por medio de un bucle del tipo )or %ac", debemos usar una clase llamada Dictionary%ntry, esta clase tiene dos propiedades, una contiene la clave y otra el valor. Por tanto, cuando usemos un bucle )or %ac", el tipo de objeto usado para acceder a los elementos de la coleccin ser Dictionary%ntry, tal como vemos en el siguiente cdigo: )or ach %e As Dictionar-ntr- In %ic Console/<rite'ine("=0> = =1>", %e/Ze-, %e/$al!e) AeGt
:btener todas las claves y valores de una coleccin IDictionary Independientemente de que podamos recorrer los valores contenidos en una coleccin de tipo IDictionary usando un objeto de tipo Dictionary%ntry, habr ocasiones en las que realmente nos interesen tener solo los valores e incluso solo las claves, en estos casos, podemos usar dos propiedades que la interfaz IDictionary define: Keys y #alues. Estas propiedades devuelven un objeto del tipo ICollection con las claves y valores respectivamente. Al ser objetos ICollection, solo podremos usarlos para recorrerlos por medio de un bucle )or %ac", ya que las colecciones ICollection no tienen ningn mtodo que nos permita acceder a los elementos que contiene usando un ndice. En el siguiente cdigo mostramos todas las claves de la coleccin creada en el ejemplo anterior: )or ach cla5e As String In %ic/Ze-s Console/<rite'ine(cla5e) AeGt
235
Nota3 Siempre que usemos un bucle For Each para recorrer los elementos (o datos) de una coleccin, solo tendremos acceso de solo lectura a esos datos, es decir, no podremos modificarlos usando la varable por medio de la que accedemos a ellos.
*as clases #ase para crear colecciones personali@adas Tal como hemos visto, en el espacio de nombres System.Collections tenemos dos clases abstractas que podemos usar como clases base para crear nuestras propias colecciones. Dependiendo que queramos crear una coleccin basada en IList, por ejemplo para acceder a los elementos mediante un ndice numrico, o bien una coleccin basada en IDictionary, para almacenar los elementos usando el par clave/valor, tendremos que usar la clase CollectionBase o DictionaryBase. Estas clases ya tienen cierta funcionalidad que podremos aprovechar para no tener que reinventar la rueda, (esa es la "gracia" de la herencia), y lo nico que tendremos que hacer es definir nuestros propios mtodos o propiedades para que la coleccin acte como nosotros decidamos y, lo ms importante, para que solo acepte los tipos de datos que realmente queramos. 236 Por ejemplo, si queremos almacenar datos de tipo Cliente y queremos acceder a esos datos solo por un ndice numrico, podramos basar nuestra coleccin en CollectionBase, pero si lo que necesitamos es una coleccin que contenga, por ejemplo, objetos de tipo Artculo, nos podra interesar crear una coleccin basada en DictionaryBase para que de esta forma podamos acceder a cada uno de los elementos por medio del cdigo del artculo. A continuacin veremos el cdigo (reducido) para crear estos dos tipos de colecciones personalizadas.
Nota3 En las definiciones de las colecciones que vamos a mostrar, no hemos aadido ninguna funcionalidad extra, sino que hemos creado las clases/colecciones para que tengan un funcionamiento parecido al de las colecciones "normales". La diferencia principal con esas colecciones "normales" es que estas dos clases/colecciones que vamos a mostrar, solo admitirn elementos de un tipo concreto. Esto lo hemos hecho as para que podamos comparar y comprobar la facilidad que ahora tenemos si usamos colecciones del espacio de nombres System.Collection.Generic.
Crear una colecci(n basada en Collection.ase A continuacin vamos a ver un ejemplo de una coleccin personalizada basada en CollectionBase y cmo usarla. Esta coleccin almacenar elementos de un tipo definido por nosotros: Cliente. Primero veamos una clase Cliente muy simple, pero que implementa la interfaz IComparable, de forma que se puedan clasificar sus elementos por el campo Apellidos. Tambin define el mtodo ToString, ya que esta es una recomendacin que siempre deberamos seguir, ya que muchas de las clases de punto NET utilizan este mtodo para mostrar el contenido de los objetos. 444 (s!mmar-* 444 Clase Cliente 444 (Vs!mmar-* 444 (remarQs* 444 sta clase se 2!e%e clasi8icar 2or el cam2o A2elli%os 444 (VremarQs* 237 ?!blic Class Cliente Im2lements S-stem/ICom2arable 4 ?!blic Aombre As String ?!blic A2elli%os As String 4 ?!blic S!b AeJ(,-$al nombre As String, ,-$al a2elli%os As String) He/Aombre = nombre He/A2elli%os = a2elli%os n% S!b 4 ?!blic 05erri%es )!nction 1oString() As String "et!rn A2elli%os P ", " P Aombre n% )!nction 4 ?!blic )!nction Com2are1o(,-$al ob# As 0b#ect) As Integer + Im2lements S-stem/ICom2arable/Com2are1o I8 1-2e08 ob# Is Cliente 1hen Dim cli As Cliente = DirectCast(ob#, Cliente) "et!rn String/Com2are(He/A2elli%os, cli/A2elli%os) lse "et!rn 0 n% I8 n% )!nction n% Class
En el siguiente cdigo tenemos la definicin de la clase/coleccin Clientes, que al estar derivada de CollectionBase tendr todos los miembros definidos en esa clase abstracta, (que solo se puede usar para crear clases derivadas); y en la que hemos 238 definido los mtodos ms habituales, as como una propiedad por defecto que nos permite acceder a los elementos mediante un ndice numrico. Como podemos comprobar, en los mtodos que hemos definido, realmente no tenemos que hacer demasiadas cosas, ya que en el cdigo que hemos escrito en esos nuevos miembros nos apoyamos en las colecciones internas proporcionadas por la clase base: List, que es una coleccin basada en IList que contiene los elementos, e InnerList que es una coleccin de tipo *rrayList que tambin hace referencia a la coleccin List. En la propiedad Item, que es la propiedad predeterminada o indizador, cuando devolvemos el valor indicado por el ndice numrico, tenemos que hacer una conversin para que se devuelva un objeto de tipo Cliente en lugar de uno de tipo Object que es como realmente se almacena en la coleccin. 444 (s!mmar-* 444 ColecciSn %e ti2o Cliente basa%a en I'ist 444 (Vs!mmar-* 444 (remarQs*(VremarQs* ?!blic Class Clientes Inherits S-stem/Collections/Collection,ase
?!blic )!nction A%%(,-$al 5al!e As Cliente) As Integer "et!rn 'ist/A%%(5al!e) n% )!nction ?!blic )!nction Contains(,-$al 5al!e As Cliente) As ,oolean "et!rn 'ist/Contains(5al!e) n% )!nction ?!blic )!nction In%eG08(,-$al 5al!e As Cliente) As Integer "et!rn 'ist/In%eG08(5al!e) n% )!nction 239 ?!blic S!b Insert(,-$al in%eG As Integer, ,-$al 5al!e As Cliente) 'ist/Insert(in%eG, 5al!e) n% S!b De8a!lt ?!blic ?ro2ert- Item(,-$al in%eG As Integer) As Cliente ;et "et!rn DirectCast('ist(in%eG), Cliente) n% ;et Set(,-$al 5al!e As Cliente) 'ist(in%eG) = 5al!e n% Set n% ?ro2ert- ?!blic S!b "emo5e(,-$al 5al!e As Cliente) 'ist/"emo5e(5al!e) n% S!b ?!blic S!b Sort() Inner'ist/Sort() n% S!b n% Class
Para usar esta coleccin, lo haremos como es costumbre en las colecciones de tipo IList: S!b Hain() Dim col As AeJ Clientes col/A%%(AeJ Cliente("?e2e", "'S2e&")) 240 col/A%%(AeJ Cliente("'oli", "?Rre&")) col/A%%(AeJ Cliente("5a", "Zelo")) col/A%%(AeJ Cliente("@!an", "Sal5a%or")) col/A%%(AeJ Cliente("Hig!el", "An%ra%e")) col/Sort() )or i As Integer = 0 1o col/Co!nt C 1 Console/<rite'ine(col(i)/A2elli%os) AeGt col/"emo5eAt(2) )or ach cli As Cliente In col Console/<rite'ine(cli/1oString) AeGt n% S!b
Crear una colecci(n basada en "ictionary.ase En el siguiente cdigo veremos cmo definir una coleccin personalizada basada en la clase abstracta DictionaryBase. Esta coleccin almacenar objetos del tipo 4rt,culo. Esos objetos se almacenarn indicando como clave el cdigo del artculo. Veamos el cdigo y comentaremos las cosas dignas de resaltar. La clase 4rticulo no tiene nada que resaltar, es una clase "normalita". 444 (s!mmar-* 444 Clase artKc!lo 444 (Vs!mmar-* 241 444 (remarQs* 444 Clase 2ara el e#em2lo %e colecciSn %eri5a%a %e Dictionar-,ase 444 (VremarQs* ?!blic Class Artic!lo ?!blic Co%igo As String ?!blic Descri2cion As String ?!blic ?$? As Decimal S!b AeJ( ,-$al co%igo As String, + ,-$al %escri2cion As String, + ,-$al 2recio As Decimal) He/Co%igo = co%igo He/Descri2cion = %escri2cion He/?$? = 2recio n% S!b ?!blic 05erri%es )!nction 1oString() As String "et!rn Co%igo P ", " P Descri2cion n% )!nction n% Class
La clase/coleccin la derivamos de DictionaryBase para que tenga todas las "caractersticas" expuestas por esa clase abstracta, a la que le aadimos nuevos mtodos y propiedades para darle funcionalidad. Tal como hicimos en la coleccin Clientes, nos apoyamos en las colecciones internas de la clase base para realizar el trabajo de esos nuevos miembros. 444 (s!mmar-* 444 ColecciSn Clientes basa%a en IDictionar- 444 (Vs!mmar-* 444 (remarQs* 242 444 (VremarQs* ?!blic Class Artic!los Inherits S-stem/Collections/Dictionar-,ase De8a!lt ?!blic ?ro2ert- Item(,-$al Qe- As String) As Artic!lo ;et "et!rn DirectCast(Dictionar-(Qe-), Artic!lo) n% ;et Set(,-$al 5al!e As Artic!lo) Dictionar-(Qe-) = 5al!e n% Set n% ?ro2ert- ?!blic "ea%0nl- ?ro2ert- Ze-s() As ICollection ;et "et!rn Dictionar-/Ze-s n% ;et n% ?ro2ert- ?!blic "ea%0nl- ?ro2ert- $al!es() As ICollection ;et "et!rn Dictionar-/$al!es n% ;et n% ?ro2ert- ?!blic S!b A%%(,-$al Qe- As String, ,-$al 5al!e As Artic!lo) Dictionar-/A%%(Qe-, 5al!e) n% S!b 243 ?!blic )!nction Contains(,-$al Qe- As String) As ,oolean "et!rn Dictionar-/Contains(Qe-) n% )!nction ?!blic S!b "emo5e(,-$al Qe- As String) Dictionar-/"emo5e(Qe-) n% S!b n% Class
La forma de usar esta coleccin es la misma que cualquier coleccin basada en IDictionary. Dim col As AeJ Artic!los col/A%%("!no", AeJ Artic!lo("Fno", "Art/ Fno", 10/6D)) col/A%%("%os", AeJ Artic!lo("Dos", "Art/ Dos", 22)) col/A%%("tres", AeJ Artic!lo("1res", "Art/ 1res", .5/55D)) )or ach %e As Dictionar-ntr- In col Dim art As Artic!lo art = DirectCast(%e/$al!e, Artic!lo) Console/<rite'ine("=0>, =1>, =2>", %e/Ze-, art/Descri2cion, art/?$?) AeGt col/"emo5e("%os") )or ach s As String In col/Ze-s Console/<rite'ine("=0>, =1>", s, col(s)/Descri2cion) AeGt 244
Crear colecciones personali*adas usando colecciones generic Hasta esta versin de Visual Basic, si queramos crear colecciones "fuertemente tipadas", es decir, colecciones que solo admitieran datos del tipo que nosotros quisiramos, tenamos que hacerlo con un cdigo parecido al que hemos visto. Pero si nuestra intencin es crear colecciones que "simplemente" contengan elementos de un tipo determinado, por ejemplo objetos de tipo -liente o 4rticulo, pero que no tengan ninguna funcionalidad extra a las que de forma predeterminada tienen las clases base para crear colecciones, no es necesario que creemos nuestros propias clases/coleccin, ya que Visual Basic 2005 puede crear colecciones con esas caractersticas sin necesidad de crear un clase especfica. Veamos primero el cdigo equivalente usando colecciones del espacio de nombres 1eneric con respecto a los dos tipos de colecciones anteriores, y despus explicaremos un poco de que va todo esto de los generics.
La coleccin Clientes en versin "eneric La coleccin Clientes es una coleccin que solo acepta elementos del tipo Cliente y a la que podemos acceder mediante un ndice numrico, por tanto debemos buscar una coleccin del espacio de nombres System.Collections.1eneric que nos ofrezca esa misma funcionalidad, y esa coleccin es: List. Debido a que las colecciones generic necesitan saber el tipo de datos que van a almacenar, no necesitamos crear una clase/coleccin para almacenar los elementos del tipo -liente, simplemente tendremos que indicar en el constructor de esa coleccin que tipo debe contener. En el siguiente cdigo tenemos la forma de declarar la coleccin de tipo List y cmo acceder a los elementos que tienen, como podr comprobar es prcticamente el mismo que el mostrado en el ejemplo de la coleccin basada en CollectionBase. 4 ColecciSn generic eI!i5alente a Arra-'ist Console/<rite'ine("#em2lo !san%o ;eneric/'ist") Dim colCli As AeJ S-stem/Collections/;eneric/'ist(08 Cliente) colCli/A%%(AeJ Cliente("?e2e", "'S2e&")) colCli/A%%(AeJ Cliente("'oli", "?Rre&")) colCli/A%%(AeJ Cliente("5a", "Zelo")) 245 colCli/A%%(AeJ Cliente("@!an", "Sal5a%or")) colCli/A%%(AeJ Cliente("Hig!el", "An%ra%e")) colCli/Sort() )or i As Integer = 0 1o colCli/Co!nt C 1 Console/<rite'ine(colCli(i)/A2elli%os) AeGt Console/<rite'ine() 4 limina el elemento %e la 2osiciSn 3 4 (2ero %es2!Rs %e haberlo clasi8ica%o) colCli/"emo5eAt(2) )or ach cli As Cliente In colCli Console/<rite'ine(cli/1oString) AeGt El "quid" de la cuestin est en la forma de declarar la variable col-li, en la que le indicamos que el tipo de datos que contendr la coleccin es "de" -liente: Dim colCli As AeJ 'ist(08 Cliente) Por lo dems, el cdigo a usar para acceder a los elementos, eliminarlos, etc., es el mismo que con cualquier otra coleccin basada en IList, pero con el "detalle" de que dicha coleccin "sepa" manejar elementos del tipo -liente. Si no fuera as, esta lnea producira un error, ya que estamos accediendo a un tipo de datos que define una propiedad llamada 4pellidos: Console/<rite'ine(colCli(i).Apellidos)
246 La coleccin Articulos en versin "eneric Como vimos, la coleccin 4rticulos solo acepta elementos del tipo 4rticulo, pero como es una coleccin de tipo IDictionary cada vez que aadimos algn elemento o queremos acceder a cualquiera de los contenidos en ella, debemos indicar tambin una clave. Por tanto necesitamos una coleccin generic que tenga esas mismas "caractersticas" y la que nos puede servir es la clase System.Collections.1eneric.Dictionary. A continuacin tenemos el cdigo para manejar objetos de tipo Articulo en una coleccin tipo IDictionary, pero como veremos, en este caso hay que usar otra de las clases del espacio de nombres 1eneric que nos sea til para hacer el bucle )or %ac", ya que para usar la clase 1eneric.Dictionary debemos indicar tanto el tipo del valor a almacenar como el de la clave. Veamos el cdigo y despus entremos en ms detalles: 4 ColecciSn generic eI!i5alente a Hashtable (IDictionar-) Console/<rite'ine("#em2lo !san%o ;eneric/Dictionar-") Dim colArt As AeJ S-stem/Collections/;eneric/Dictionar-(08 String, Artic!lo) colArt/A%%("!no", AeJ Artic!lo("Fno", "Art/ Fno", 10/6D)) colArt/A%%("%os", AeJ Artic!lo("Dos", "Art/ Dos", 22)) colArt/A%%("tres", AeJ Artic!lo("1res", "Art/ 1res", .5/55D)) )or ach %e As Ze-$al!e?air(08 String, Artic!lo) In colArt Dim art As Artic!lo art = DirectCast(%e/$al!e, Artic!lo) Console/<rite'ine("=0>, =1>, =2>", %e/Ze-, art/Descri2cion, art/?$?) AeGt Console/<rite'ine() colArt/"emo5e("%os") )or ach s As String In colArt/Ze-s 247 Console/<rite'ine("=0>, =1>", s, colArt(s)/Descri2cion) AeGt Nuevamente "el truco" est en la forma de declarar la variable col4rt, en la que le decimos que la coleccin Dictionary usar claves de tipo String y valores del tipo 4rticulo: Dim colArt As AeJ Dictionar-(08 String, Artic!lo) Para acceder a los elementos de esta coleccin por medio de un bucle )or %ac", en lugar de usar una variable de tipo Dictionary%ntry debemos usar una del tipo generic Key#alue+air en la que debemos especificar los tipos de datos que contiene la coleccin: )or ach %e As Ze-$al!e?air(08 String, Artic!lo) In colArt Podemos comprobar que los tipos de datos que esta coleccin contiene son de tipo String para las claves y de tipo Articulo para los valores, ya que al acceder a un elemento dentro del segundo bucle, esos son los tipos de datos que deben tener, ya que de lo contrario, no podramos acceder a la propiedad /escripcion del objeto almacenado: Console/<rite'ine("=0>, =1>", s, colArt(s).Descripcion)
248 -olecciones de tipo 'eneric 9en #reve: En los ejemplos que acabamos de ver podemos apreciar que las colecciones del espacio de nombres 1eneric son bastantes "potentes" o al menos nos pueden facilitar la tarea de crear colecciones fuertemente tipadas, ya que podremos indicar que tipo de datos son los que queremos que contenga. En la siguiente imagen podemos ver cmo al declarar una coleccin 1eneric.Dictionary nos pide tanto el tipo de datos de la clave como del valor: Fi'ura 0<.0$.0$ IntelliSense en las colecciones 'enerics La principal ventaja de estas colecciones es que debemos indicar siempre el tipo de datos que va a contener y en el caso de las colecciones Dictionary tambin tenemos que indicar el tipo de datos para las claves. Estos tipos de datos pueden ser cualquiera de los hay definidos en el propio .NET Framework o los que hayamos definido nosotros. Como podemos comprobar, siempre que vamos a declarar una coleccin 1eneric vemos la "palabra clave" Of, esta instruccin lo que hace es indicar cual ser el tipo de datos que contendr la coleccin. Y ese ser el nico tipo de datos que la coleccin podr contener.
Eestricciones en los tipos generic La ventaja de este tipo de "restriccin" del tipo que puede contener una coleccin de tipo 1eneric es que en lugar de indicar un tipo concreto, podemos indicar tambin un tipo algo ms "genrico", (genrico en el sentido de no tan estricto), por ejemplo, si tenemos un tipo de datos que implementa una interfaz o que se deriva de una clase, podemos indicar ese tipo despus de Of y en ese caso solamente se admitirn objetos que implemente o se derive de ese tipo. 249 Por ejemplo, si tenemos una clase de tipo 8ersona y la utilizamos como clase base de un tipo -liente y otro de tipo Empleado, podramos crear una coleccin 1eneric.List que admita solo elementos del tipo 8ersona, con lo cual podemos hacer que podamos aadir tanto elementos de tipo -liente y/o de tipo Empleado, ya que estas dos clases se derivan de la clase Persona. En el siguiente cdigo tenemos las definiciones de estas tres clases y el cdigo para crear la coleccin generic: La clase Persona: ?!blic Class ?ersona ?ri5ate +Aombre As String ?!blic ?ro2ert- Aombre() As String ;et "et!rn +Aombre n% ;et Set(,-$al 5al!e As String) +Aombre = 5al!e n% Set n% ?ro2ert- ?ri5ate +A2elli%os As String ?!blic ?ro2ert- A2elli%os() As String ;et "et!rn +A2elli%os n% ;et Set(,-$al 5al!e As String) +A2elli%os = 5al!e n% Set n% ?ro2ert- n% Class La clase Cliente ?!blic Class Cliente 250 Inherits ?ersona ?!blic S!b AeJ(,-$al em2resa As String) He/Aombre = em2resa n% S!b ?!blic 05erri%es )!nction 1oString() As String "et!rn Aombre n% )!nction n% Class La clase Empleado ?!blic Class m2lea%o Inherits ?ersona ?!blic S!b AeJ(,-$al nombre As String, ,-$al a2elli%os As String) He/Aombre = nombre He/A2elli%os = a2elli%os n% S!b ?!blic 05erri%es )!nction 1oString() As String "et!rn A2elli%os P ", " P Aombre n% )!nction n% Class
El cdigo para usar estas clases Dim col As AeJ S-stem/Collections/;eneric/'ist(08 ?ersona) 251 col/A%%(AeJ Cliente("Constr!cciones ?e2e")) col/A%%(AeJ m2lea%o("@!an", "?Rre&")) )or ach 2 As ?ersona In col Console/<rite'ine(2/Aombre) AeGt
Las colecciones y clases generic son bastantes "potentes" y en principio fciles de utilizar, con el valor aadido (o ventaja) es que nosotros tambin podemos crear nuestros propios tipos de datos que utilicen esta nueva "tecnologa", pero su tratamiento en profundidad sobrepasa el objetivo de este curso, aunque con lo aqu expuesto creemos que el lector est preparado para investigar por su cuenta, con la ayuda de la documentacin de Visual Basic 2005 Express y de los muchos recursos que en breve estarn disponibles tanto en formato "artculo Web" y en libros especializados en las novedades de Visual Basic 2005.
Introduccin Al empezar a trabajar con .NET, uno de los "grandes" cambios que notaremos los que estamos habituados a desarrollar con Visual Basic 6.0 es todo lo relacionado 252 con el acceso al contenido de los ficheros. En VB6 no tenemos demasiadas complicaciones, simplemente utilizamos las funciones para acceder a los ficheros y poco ms, pero en .NET, y por tanto en Visual Basic 2005, el acceso a ficheros se extiende con otros conceptos que en VB6 no tenemos, salvo que nos atrevamos con las APIs de Windows y la "interoperabilidad" con el lenguaje C/C++, estamos hablando de los streams (o usando la traduccin de la documentacin: las secuencias). Con los streams no solo podemos acceder a los ficheros de disco, sino que tambin podemos acceder a otros tipos de "secuencias" o flujos de datos, desde streams de memoria a streams para enviar informacin a travs de Internet. Toda esta transmisin o flujo de informacin se realiza mediante una serie de mtodos de lectura y escritura que estn bsicamente encapsulados en la clase abstracta Stream. Esta clase ser la clase base de todas aquellas que de alguna forman tienen que transmitir cierta informacin entre la fuente de datos y nuestra aplicacin. En este captulo trataremos de las clases basadas en Stream que con ms frecuencia utilizaremos, sobre todo en lo relacionado con el acceso a ficheros, que es al fin y al cabo la utilidad principal de este tipo de "secuencias".
Streams en .NET Streams en .NET 'as clases basadas en Stream o Manejar un fichero usando FileStream o Manejar un fichero usando StreamReader y StreamWriter Asegurarnos que el fichero se cierra Liberar recursos: Using... End Using o Ejemplo de para cifrar y descifrar un fichero
*as clases #asadas en Stream Entre las clases que estn basadas en esta clase abstracta tenemos las siguientes: BufferedStream, clase abstracta que representa un buffer de almacenamiento para operaciones de lectura y escritura de otro stream. DeflateStream, permite la compresin y descompresin de streams usando el algoritmo Deflat. 1QipStream, usada para comprimir y descomprimir streams. )ileStream, nos permite una forma bsica de acceder y manipular ficheros. 253 (emoryStream, crear un stream que se almacena en la memoria como una secuencia de bytes. $et!or6Stream, proporciona una secuencia de datos para el acceso a la red. CryptoStream, un stream usado para encriptar otros streams.
Nota3 Las clases DeflateStream y GZipSteam estn incluidas en el espacio de nombres System.IO.Compression. La clase CryptoStream est incluida en el espacio de nombres System.Security.Cryptography. La clase NetworkStream est incluida en el espacio de nombres System.Net.Sockets.
Adems de estas clases que se derivan directamente de la clase Stream, y que normalmente se usan como "secuencias" a usar por otras clases de entrada/salida, tenemos otras que nos permitirn acceder a esas secuencias de datos de una forma ms directa, (algunas de estas las veremos con algo de ms detalle en el prximo captulo dedicado al sistema de archivos de .NET), por ejemplo: Binary'eader 5 Binary4riter, lee o escribe tipos primitivos como valores binarios utilizando una codificacin especfica. Stream'eader 5 Stream4riter, clases para leer y escribir caracteres en ficheros utilizando una codificacin determinada. String'eader 5 String4riter, implementa Te&t'eader o Te&t4riter para leer o escribir en una cadena. Te&t'eader 5 Te&t4riter, clases abstractas para leer o escribir en una secuencia de caracteres.
Cuando trabajamos con los streams debemos olvidarnos de las "cosas simples" y debemos tener en cuenta que trataremos casi siempre con secuencias de bytes, ya que al fin y al cabo esa es la forma de almacenar la informacin en los streams. Por tanto cuando veamos los ejemplos que la documentacin de Visual Basic 2005 nos proporciona no debemos extraarnos de que haya que hacer tantas "cosas" para acceder o manipular la informacin almacenada en esas "secuencias" de datos. Si bien, esa "complicacin" nos da mayor control sorbe el formato de la informacin contenida en los streams. Por suerte, para los que nos gustan las cosas "simples" las clases especficas nos facilitan mucho las cosas. A continuacin veremos un par de ejemplos en los que manipularemos cierta informacin tanto en la memoria usando un objeto del tipo (emoryStream, como en un fichero de disco usando )ileStream y las clases que casi con seguridad usaremos habitualmente para acceder al contenido de los ficheros: Stream'eader y Stream4riter. 254
%anear un fic#ero usando <ileStream En este primer ejemplo veremos lo complicado que pude parecer acceder a un fichero usando la clase )ileStream y por extensin cualquier mtodo de otras clases que devuelvan este tipo de secuencia, como por ejemplo los mtodos Open'ead, Open4rite, etc. de la clase File. La "complejidad" de esta clase es que realmente obtiene o guarda la informacin por medio de un array de tipo Byte, cuando a lo que estamos acostumbrados es a usar cadenas.
Nota3 Realmente, esta forma "binaria" de acceder a la informacin de un fichero no la tenemos que ver como un inconveniente, ya que nos puede servir para acceder de forma "binaria" a ese fichero, en caso de que nuestra intencin sea acceder de forma "normal" para, por ejemplo, leer solo texto, deberamos usar otras clases ms especializadas para esa tarea, como lo es StreamReader.
En el siguiente cdigo tenemos dos mtodos, uno que guarda una cadena en el fichero indicado: ?ri5ate S!b g!ar%arDatos(,-$al 8ichero As String, ,-$al ca%ena As String) 4 Abrimos o creamos el 8ichero, 2ara escribir en Rl Dim 8s As AeJ S-stem/I0/)ileStream(8ichero, + S-stem/I0/)ileHo%e/02en0rCreate, + S-stem/I0/)ileAccess/<rite) 4 scribimos alg!nas ca%enas, 4 el 2roblema es I!e solo 2o%emos escribir arra-s %e b-tes, 4 2or tanto %ebemos con5ertir la ca%ena en !n arra- %e b-tes Dim %atos() As ,-te 4 2ero !san%o la co%i8icaciSn I!e creamos con5eniente 4 %e 8orma 2re%etermina%a es F1)CX, 255 4 a!nI!e la co%i8icaciSn %e <in%oJs es AASI (nco%ing/De8a!lt) Dim enc As AeJ S-stem/1eGt/F1)Xnco%ing 4 con5ertimos la ca%ena en !n arra- %e b-tes %atos = enc/;et,-tes(ca%ena) 4 lo escribimos en el stream 8s/<rite(%atos, 0, %atos/'ength) 4 nos aseg!ramos I!e se escriben to%os los %atos 8s/)l!sh() 4 cerramos el strema 8s/Close() n% S!b En el constructor de la clase )ileStream indicamos el fichero en el que queremos guardar la informacin, tambin le indicamos que queremos crearlo o abrirlo, es decir, si ya existe lo abre y si no existe lo crea, de cualquiera de las formas, en el siguiente parmetro del constructor le indicamos que nuestra intencin es escribir en ese fichero. Como hemos comentado, la clase )ileStream (y en general todos los streams) trabaja con bytes, por tanto para poder almacenar algo en ese fichero debemos hacerlo mediante un array de tipo Byte. En el caso de las cadenas, stas siempre deben estar codificadas, es decir, deben usar el juego de caracteres que creamos conveniente, en el mundo de .NET ese juego de caracteres es Unicode, ms concretamente usando la codificacin UTF-8, la cual permite trabajar con cualquier carcter de cualquier cultura. Como lo que nos interesa es convertir una cadena en un array de bytes, usamos el mtodo 1etBytes de un objeto ,T)R%ncoding, el cual convierte la cadena en una "ristra" de bytes con el formato adecuado, en este caso UTF-8. Si en lugar de usar UTF-8 quisiramos usar otro "codificador", por ejemplo el predeterminado de Windows, con idea de que los ficheros sean compatibles con nuestras aplicaciones de VB6, tendremos que declarar la variable enconding de la siguiente forma: Dim enc As S-stem/1eGt/nco%ing = S-stem/1eGt/nco%ing/De8a!lt A continuacin, simplemente le pasamos el array de bytes al mtodo 4rite del )ileStream indicando desde que posicin de dicho array debe escribir y cuantos bytes. Por ltimo nos aseguramos de que todos los bytes del "buffer" se guarden en el fichero y lo cerramos.
Y otra funcin que devuelve el contenido de un fichero en formato cadena: 256 ?ri5ate )!nction leerDatos(,-$al 8ichero As String) As String 4 'os bloI!es leK%os los almacenaremos en !n String,!il%er Dim res As AeJ S-stem/1eGt/String,!il%er 4 Abrimos el 8ichero 2ara leer %e Rl Dim 8s As AeJ S-stem/I0/)ileStream(8ichero, + S-stem/I0/)ileHo%e/02en, + S-stem/I0/)ileAccess/"ea%) 4 los %atos se leer6n en bloI!es %e 102. b-tes (1 Z,) Dim %atos(102.) As ,-te Dim enc As AeJ S-stem/1eGt/F1)Xnco%ing() 4 leemos mientras ha- algo en el 8ichero <hile 8s/"ea%(%atos, 0, 102.) * 0 4 agregamos al string,!il%er los b-tes leK%os 4 (con5erti%os en !na ca%ena) res/A22en%(enc/;etString(%atos)) n% <hile 4 cerramos el b!88er 8s/Close() 4 %e5ol5emos to%o lo leK%o "et!rn res/1oString n% )!nction En esta funcin vamos a leer los datos del fichero indicado, como ya hemos vistos, la clase )ileStream trabaja con bytes y esos bytes los convertimos a caracteres por medio de las clases de codificacin especializadas. Por regla general, esas lecturas las haremos de forma parcial, es decir leyendo bloques de bytes y como tenemos que convertir esos bytes en caracteres, y puede ser que el fichero sea muy grande, en lugar de concatenar una cadena para almacenar las lecturas parciales, vamos a usar un objeto del tipo StringBuilder en el que iremos "agregando" cada trozo ledo, de forma que el rendimiento no se vea penalizado por la forma de ser de las cadenas, ya que cada vez que hacemos una concatenacin en una variable de tipo String, realmente estamos creando nuevos objetos en la memoria y si son muchos, pues la verdad es que tendremos al recolector de basura (GC) trabajando a tope, y si usamos un objeto StringBuilder el rendimiento mejora una barbaridad. 257 La lectura de cada bloque de bytes lo hacemos en un bucle 4"ile, tambin podramos haberlo hecho en un bucle Do 4"ile, pero en estos casos, el rendimiento de 4"ile es un "poquitn" mayor. Los datos ledos los agregamos al objeto StringBuilder por medio del mtodo *ppend que se encarga de agregarlo a la cadena interna. Finalmente cerramos el stream y devolvemos la cadena leda.
Para usar estas dos funciones lo podemos hacer de la siguiente forma: 4 !n 8ichero %e e#em2lo (el %irectorio %ebe eGistir) Const 8ichero As String = "DU?r!ebasU2r!eba/tGt" 4 g!ar%amos !na ca%ena en el 8ichero g!ar%arDatos(8ichero, "Hola, H!n%o %e )ileStream") 4 4 'eemos el conteni%o %el 8ichero - lo mostramos Console/<rite'ine(leerDatos(8ichero)) Este cdigo no necesita mayor explicacin.
%anear un fic#ero usando StreamEeader y StreamJriter A continuacin veremos cmo crear las dos funciones del ejemplo anterior para que utilicen las clases "especializadas" para leer y escribir cadenas en un fichero. Como podremos comprobar, esta es una forma muchsimo ms simple y, por tanto recomendada para este tipo de acceso a los ficheros. Aunque debemos recordar que solo servir para leer la informacin de forma secuencial y en formato cadena.
Nota3 El cdigo para usar estas dos funciones ser el mismo que el usado para las funciones que utilizan la clase FileStream.
La funcin para guardar una cadena en un fichero: 258 ?ri5ate S!b g!ar%arDatos(,-$al 8ichero As String, ,-$al ca%ena As String) 4 Abrimos el 8ichero 2ara escribir, (no aTa%ir), 4 !san%o la co%i8icaciSn 2re%etermina%aD F1)CX Dim sJ As AeJ S-stem/I0/Stream<riter(8ichero, )alse) 4 g!ar%amos to%a la ca%ena sJ/<rite'ine(ca%ena) 4 Cerramos el 8ichero sJ/Close() n% S!b Como podemos apreciar, esta es una forma mucho ms "compacta" que la anterior, ya que solo tenemos que indicar en el constructor lo que queremos hacer y usar el mtodo 4rite o 4riteLine para guardar lo que queramos en el fichero. Para guardarlo usando la codificacin predeterminada del Sistema Operativo en el que se utilice la aplicacin, (en Windows ser ANSI), simplemente usamos este constructor: Dim sJ As AeJ S-stem/I0/Stream<riter(8ichero, )alse, S-stem/1eGt/nco%ing/De8a!lt)
Para leer los datos podemos hacerlo de dos formas: lnea a lnea o todo el contenido de una sola vez. En el cdigo siguiente se muestra lnea a lnea, y al final, (comentado), cmo hacerlo en una sola pasada. ?ri5ate )!nction leerDatos(,-$al 8ichero As String) As String 4 Abrimos el 8ichero 2ara leer 4 !san%o la co%i8icaciSn F1)CX (la 2re%etermina%a %e /A1) Dim sr As AeJ S-stem/I0/Stream"ea%er(8ichero, 1r!e) 4 si I!eremos !sar la 2re%etermina%a %e <in%oJs 4Dim sr As AeJ S-stem/I0/Stream"ea%er(8ichero, S-stem/1eGt/nco%ing/De8a!lt) 4 ?o%emos leer ca%a !na %e las lKneas %el 8ichero o to%o el conteni%o 4 )orma largaD 259 4 si 5amos a leer el 8ichero lKnea 2or lKnea, me#or !sar !n String,!il%er Dim ret As AeJ S-stem/1eGt/String,!il%er 4 recorremos el 8ichero hasta I!e no ha-a na%a I!e leer <hile sr/?eeQ (* C1 ret/A22en%(sr/"ea%'ine) n% <hile 4 cerramos el 8ichero sr/Close() 4 %e5ol5emos lo leK%o "et!rn ret/1oString 4 44 )orma cortaD 44 leemos to%o el conteni%o %el 8ichero 4Dim ret As String = sr/"ea%1on%() 44 lo cerramos 4sr/Close() 44 %e5ol5emos lo leK%o 4"et!rn ret n% )!nction Si nos decidimos a leer el contenido del fichero lnea a lnea, podemos usar el mtodo +ee6, el cual devolver el siguiente carcter del buffer del stream, o -1 si no hay nada que leer. +ee6 no "consume" el carcter, simplemente comprueba si hay algo que leer. Si hay algo que leer, leemos la lnea completa y la aadimos al objeto StringBuilder, el bucle se repetir mientras haya informacin pendiente de leer. Pero si optamos por la va rpida, porque realmente no nos interese procesar cada lnea, podemos usar el mtodo 'eadTo%nd, que en nuestro ejemplo, el valor devuelto ser todo el contenido del fichero, el cual asignamos a una variable de tipo String para usarla como valor devuelto por la funcin, despus de cerrar el fichero.
260 Ase"urarnos que el fic%ero se cierra Si queremos ahorrarnos el paso intermedio de asignar el valor en una variable y despus devolverlo, tambin podemos hacerlo de esta forma: 1r- "et!rn sr/"ea%1on%() )inall- sr/Close() n% 1r- Ya que el mtodo )inally siempre se ejecutar, por tanto nos aseguramos de que el fichero se cierra.
Liberar recursos: Gsin"*** nd Gsin" O si lo preferimos, podemos usar la nueva forma de asegurarnos de que los recursos usados se liberan: 4 ?o%emos !sar Fsing 2ara aseg!rarnos %e I!e el rec!rso se libera ?ri5ate )!nction leerDatos(,-$al 8ichero As String) As String Dim sr As AeJ S-stem/I0/Stream"ea%er(8ichero, 1r!e) Fsing sr "et!rn sr/"ea%1on%() n% Fsing n% )!nction En este cdigo, cuando usamos Gsin' sr, al ejecutarse End Gsin', el CLR se encargar de llamar al mtodo Dispose de la clase, de forma que se liberen los recursos que estemos usando, en este ejemplo: el fichero abierto. Estos dos cdigos seran equivalentes, ms seguros, pero tambin con ms "trabajo" para el CLR.
Nota3 Using... End Using solo se puede usar con clases que implementen la interfaz IDisposable, que es la que asegura que el objeto implementa 261 el mtodo Dispose. Por tanto, si implementamos el mtodo IDisposable.Dispose en nuestras clases, en ese mtodo nos tenemos que asegurar que liberamos los recursos que nuestra clase est utilizando.
Eemplo de para cifrar y descifrar un fic#ero En el siguiente ejemplo (adaptado de uno de la documentacin), veremos cmo usar algunas de las clases basadas en Stream, particularmente las clase (emoryStream, )ileStream, CryptoStream adems de las clases Stream'eader y Stream4riter. Este cdigo tiene dos funciones: La primera encripta (cifra) una cadena y la guarda en un fichero. La segunda desencripta (descifra) el contenido de un fichero y lo guarda en otro. Ambas funciones devuelven la cadena cifrada o descifrada respectivamente. Im2orts S-stem Im2orts S-stem/I0 Im2orts S-stem/1eGt Im2orts S-stem/Sec!rit-/Cr-2togra2h- Ho%!le Ho%!le1 Const 8ic1 As String = "DU?r!ebasU?r!eba Cr-2toStream/tGt" Const 8ic3 As String = "DU?r!ebasU?r!eba Cr-2toStream %es/tGt" Const sZe- As String = "l 2assJor% a !sar" 4 S!b Hain() Dim ret As String 4 ret = ci8rar)ichero("Hola, H!n%o encri2ta%o", 8ic1) Console/<rite'ine("Ca%ena encri2ta%a D =0>", ret) 4 262 ret = %esci8rar)ichero(8ic1, 8ic3) Console/<rite'ine("Ca%ena %esencri2ta%aD =0>", ret) 4 Console/"ea%'ine() n% S!b )!nction ci8rar)ichero( + ,-$al teGto As String, + ,-$al 8icSali%a As String) As String 4 Creamos !n Hemor-Sream con el teGto a ci8rar Dim enc As AeJ F1)Xnco%ing Dim %atos() As ,-te = enc/;et,-tes(teGto) Dim ms As AeJ Hemor-Stream(%atos) 4 l 8ichero %e sali%a Dim 8s As AeJ )ileStream(8icSali%a, )ileHo%e/Create, )ileAccess/<rite) 4 l 2ro5ee%or cri2togr68ico Dim r As AeJ DSCr-2toSer5ice?ro5i%er 4 4 stablecer la cla5e secreta r/Ze- = nco%ing/De8a!lt/;et,-tes(sZe-/S!bstring(0, X)) r/I$ = nco%ing/De8a!lt/;et,-tes(sZe-/S!bstring(0, X)) 4 4 Crear !na sec!encia %e ci8ra%o Dim cs As AeJ Cr-2toStream(8s, + r/Createncr-2tor(), + Cr-2toStreamHo%e/<rite) 4 4 scribir el 8ichero ci8ra%o 263 cs/<rite(%atos, 0, %atos/'ength) cs/Close() 4 4 %e5ol5er el teGto ci8ra%o "et!rn Con5ert/1o,ase6.String(ms/1oArra-()) n% )!nction )!nction %esci8rar)ichero( + ,-$al 8ichero As String, + ,-$al 8icSali%a As String) As String 4 el 2ro5ee%or %el ci8ra%o - las cla5es !sa%as 2ara ci8rar Dim r As AeJ DSCr-2toSer5ice?ro5i%er r/Ze-() = nco%ing/De8a!lt/;et,-tes(sZe-/S!bstring(0, X)) r/I$ = nco%ing/De8a!lt/;et,-tes(sZe-/S!bstring(0, X)) 4 4 crear la sec!encia 2ara leer el 8ichero ci8ra%o Dim 8s As AeJ )ileStream(8ichero, )ileHo%e/02en, )ileAccess/"ea%) 4 Dim cs As AeJ Cr-2toStream(8s, + r/CreateDecr-2tor(), + Cr-2toStreamHo%e/"ea%) 4 4 g!ar%ar el conteni%o %e 8ichero %esci8ra%o Dim sJ As AeJ Stream<riter(8icSali%a) Dim sr As AeJ Stream"ea%er(cs) sJ/<rite(sr/"ea%1on%) sJ/)l!sh() sJ/Close() 264 4 4 %e5ol5er el teGto sr = AeJ Stream"ea%er(8ic3) Dim ret As String = sr/"ea%1on%() sr/Close() 4 "et!rn ret n% )!nction n% Ho%!le
Introduccin En la leccin anterior vimos cmo podemos acceder al contenido de los ficheros, es decir, cmo leer lo que hay dentro de un fichero, e incluso cmo haremos para escribir en ellos. En esta leccin trataremos del acceso al sistema de archivos, es decir, veremos las clases que .NET pone a nuestra disposicin para que podamos manejar tanto los ficheros (archivos) como los directorios. Los que hayan utilizado el objeto )ile System Objects desde Visual Basic 6.0 seguramente encontrarn muchas similitudes con lo que .NET ofrece, pero, como 265 comprobaremos, en .NET tenemos ms variedad de clases y, por supuesto, podemos hacer muchas ms cosas, y de forma ms fcil, que desde VB6.
4cceso al sistema de arc?ivos Acceso al sistema de arc#ivos 'as clases del espacio de nombres System.-$ o Clases para manipular unidades, directorios y ficheros o Las clases para crear streams o Las clases para leer o escribir en los streams Clases para manipular unidades/ directorios y fic#eros o Las clases Directory y DirectoryInfo Los mtodos de las clases Directory y DirectoryInfo o Las clases File y FileInfo Cifrar y descifrar un fichero usando File o FileInfo Abrir ficheros para agregar o leer el contenido o Manipular cadenas relacionadas con ficheros y directorios usando Path 'as clases para leer o escribir en los streams o Las clases StreamReader y StreamWriter La codificacin de los ficheros de .NET El obeto %y o El objeto My.Computer.FileSystem Buscar texto en ficheros con FindInFiles Examinar el contenido de un fichero con formato con OpenTextFieldParse
266
4cceso al sistema de arc?ivos Empezaremos viendo las las clases elementales o ms usadas para manejar el sistema de archivos desde .NET. El espacio de nombres System.IO es el que contiene todas las clases relacionadas con los ficheros y directorios, en ese mismo espacio de nombres estn las que vimos en la leccin anterior dedicada a los streams de .NET, ya que al fin y al cabo, cuando tratamos con los ficheros, estaremos tratando con una secuencia de caracteres o bytes, secuencia a la que accederemos con las clases especializadas que ya vimos. Aqu nos centraremos principalmente en la forma de acceder a esos ficheros, a copiarlos, a comprobar si existen, etc., en definitiva: a manipularlos.
*as clases del espacio de nom#res S%stem.I7 Entre las clases que nos podemos encontrar en este espacio de nombres, tenemos clases que podemos agrupar dependiendo de las tareas que podemos hacer con ellas en: Las clases para manipular las unidades, directorios y ficheros Las clases para crear streams Las clases para leer o escribir en los streams 267 Veamos primero una relacin de esas clases y enumeraciones y despus veremos algunas de ellas con ms detalle.
Clases para manipular unidades/ directorios y fic#eros Entre las clases que nos permiten trabajar con los ficheros, directorios y las unidades de disco, podemos destacar las siguientes: Directory, proporciona mtodos estticos para crear, mover y enumerar los ficheros de directorios y subdirectorios. DirectoryInfo, al igual que la clase Directory, pero los mtodos son de instancia, dicha instancia se crear a partir de un directorio determinado. DrieInfo, proporciona mtodos de instancia para crear, mover y enumerar el contenido de unidades. )ile, proporciona mtodos estticos para crear, copiar, eliminar, mover y abrir ficheros, adems de ayudar a la creacin de objetos )ileStream. )ileInfo, igual que la clase )ile, pero los mtodos son de instancia, dicha instancia se crear a partir de un fichero determinado. +at", proporciona mtodos y propiedades para procesar cadenas relacionadas con los directorios. Tambin tenemos las siguientes enumeraciones: )ile*ccess, define constantes para el tipo de acceso a los ficheros: lectura, escritura o lectura/escritura. )ile*ttributes, define constantes para el atributo de los ficheros y directorios: archivo, oculto, solo lectura, etc. )ile(ode, define las constantes que podemos usar para controlar como abrimos un fichero. )ileS"are, define las constantes para controlar el tipo de acceso que otros objetos )ileStream pueden tener al mismo fichero.
'as clases para crear streams La siguiente relacin son clases que nos permiten la creacin de streams, las cuales ya vimos en la leccin anterior, por tanto solo las mencionaremos. BufferedStream, clase abstracta que representa un buffer de almacenamiento para operaciones de lectura y escritura de otro stream. DeflateStream, permite la compresin y descompresin de streams usando el algoritmo Deflat. 1QipStream, usada para comprimir y descomprimir streams. )ileStream, nos permite una forma bsica de acceder y manipular ficheros. (emoryStream, crear un stream que se almacena en la memoria como una secuencia de bytes. $et!or6Stream, proporciona una secuencia de datos para el acceso a la red. 268 CryptoStream, un stream usado para encriptar otros streams.
'as clases para leer o escribir en los streams
Binary'eader, lee tipos primitivos o cadenas codificadas desde un )ileStream. Binary4riter, escribe tipos primitivos o cadenas codificadas en un )ileStream. Stream'eader, lee caracteres desde un )ileStream, usando codificacin para convertir los caracteres a/desde bytes. Stream4riter, escribe caracteres a un )ileStream, usando codificacin para convertir los caracteres en bytes. String'eader, lee caracteres desde un String. La salida puede ser a un stream en cualquier codificacin o a una cadena. String4riter, escribe caracteres a un String. Al igual que String'eader, la salida puede ser a un stream usando cualquier codificacin o a una cadena.
Te&t'eader, es la clase abstracta base para Stream'eader y String'eader. Te&t4riter, es la case abstracta base para Stream4riter y String4riter.
Nota3 La clase abstracta Stream est diseada para entrada y salida de bytes, las clases abstractas TextReader y TextWriter estn diseadas para la entrada/salida de caracteres Unicode.
269
-lases para manipular unidadesC directorios % fic?eros Entre las clases que nos permiten trabajar con los ficheros, directorios y las unidades de disco, podemos destacar las siguientes: Directory, proporciona mtodos estticos para crear, mover y enumerar los ficheros de directorios y subdirectorios. DirectoryInfo, al igual que la clase Directory, pero los mtodos son de instancia, dicha instancia se crear a partir de un directorio determinado. DrieInfo, proporciona mtodos de instancia para crear, mover y enumerar el contenido de unidades. File, proporciona mtodos estticos para crear, copiar, eliminar, mover y abrir ficheros, adems de ayudar a la creacin de objetos )ileStream. )ileInfo, igual que la clase )ile, pero los mtodos son de instancia, dicha instancia se crear a partir de un fichero determinado. +at", proporciona mtodos y propiedades para procesar cadenas relacionadas con los directorios. Tambin tenemos las siguientes enumeraciones: )ile*ccess, define constantes para el tipo de acceso a los ficheros: lectura, escritura o lectura/escritura. )ile*ttributes, define constantes para el atributo de los ficheros y directorios: archivo, oculto, solo lectura, etc. )ile(ode, define las constantes que podemos usar para controlar como abrimos un fichero. )ileS"are, define las constantes para controlar el tipo de acceso que otros objetos )ileStream pueden tener al mismo fichero. 270
'as clases "irectory y "irectory-nfo Cuando necesitemos acceder a un directorio, por ejemplo, para saber que subdirectorios y ficheros contiene o para obtener otro tipo de informacin, incluso para saber si existe o no, en esos casos podemos utilizar las clases Directory o DirectoryInfo. La primera de ellas: Directory, proporciona mtodos estticos, es decir, los mtodos que contienen siempre estarn disponibles sin necesidad de crear una nueva instancia de la clase. Por tanto, podremos usar esos mtodos simplemente usando la propia clase. La segunda: DirectoryInfo, proporciona los mismos mtodos que Directory, pero en lugar de ser mtodos estticos, son mtodos de instancia, es decir, esos mtodos solamente se podrn usar despus de haber creado una instancia (u objeto en memoria) de esta clase. Cada una de las instancias de DirectoryInfo, har referencia a un directorio en particular, ese directorio se indicar al crear el objeto, aunque tambin puede ser que est referenciado al obtenerlo mediante otros mtodos que devuelven este tipo de objetos.
Nota3 El directorio asignado al crear la instancia de DirectoryInfo no tiene porqu existir. De esa forma podemos comprobar si existe, y en caso de que no exista, crearlo, etc.
Por ejemplo, si queremos saber si un directorio existe, utilizaremos el mtodo %&ists, en el caso de la clase Directory, como parmetro le indicaremos el directorio del que queremos comprobar su existencia, sin embargo, si usamos un objeto del tipo DirectoryInfo, no necesitamos indicar ningn parmetro, ya que el directorio que est asociado con esa clase lo habremos indicado al crear el objeto. En el siguiente cdigo vemos cmo usar estas dos clases: Const %ir1 As String = "DU?r!ebas" Const %ir2 As String = "DU?r!ebas2" S!b Hain() 271 claseDirector-() Console/<rite'ine() claseDirector-In8o() Console/<rite'ine() 4 Console/"ea%'ine() n% S!b S!b claseDirector-() 4 'os mRto%os %e la clase Director- son est6ticos, 4 2or tanto no necesitamos crear !na instancia %e esta clase 4 4 Com2robar si eGiste !n %irectorioD I8 S-stem/I0/Director-/Gists(%ir1) 1hen Console/<rite'ine("l %irectorio 4=0>4 SI eGiste", %ir1) lse Console/<rite'ine("l %irectorio 4=0>4 A0 eGiste", %ir1) n% I8 n% S!b S!b claseDirector-In8o() 4 'os mRto%os %e la clase Director-In8o son %e instancia, 4 2or tanto tenemos I!e crear !na instancia %e la clase 4 2ara 2o%er !sarla 4 Dim %i As AeJ S-stem/I0/Director-In8o(%ir2) 4 4 Com2robar si eGiste !n %irectorioD I8 %i/Gists 1hen 272 Console/<rite'ine("l %irectorio 4=0>4 SI eGiste", %ir2) lse Console/<rite'ine("l %irectorio 4=0>4 A0 eGiste", %ir2) n% I8 n% S!b
El resto de mtodos de estas dos clases funcionan de forma similar, al menos en el sentido de que si usamos la clase Directory, siempre habr un parmetro que haga referencia al directorio que queremos usar, mientras que con DirectoryInfo siempre estar haciendo referencia al directorio usado al crear la instancia.
Nota3 No queremos parecer repetitivos, pero nos interesa &ue &uede claro como funcionan este tipo de clases, que por otro lado, ser similar al de las clases File y FileInfo, al menos en el sentido de que la clase con los mtodos estticos siempre necesitar saber a que elemento del sistema de archivos estamos refirindonos, mientras que las clases que debemos instanciar, siempre sabrn con que elemento trabajarn.
Los mtodos de las clases Directory y DirectoryInfo Estas clases siempre manejarn directorios y entre los mtodos que podemos utilizar, destacamos los siguientes, empezamos por la clase Directory: CreateDirectory, crear los directorios indicados en el parmetro. Si algunos de los directorios intermedios no existen, los crear. %&ists, comprueba si el directorio indicado existe. 1etCreationTime, devuelve la fecha y hora de creacin del directorio indicado. 1etCreationTime,tc, devuelve la fecha y hora universal (UTC/GMT) de creacin del directorio. 1etCurrentDirectory, devuelve el directorio de trabajo de la aplicacin. 1etDirectories, devuelve un array de String con todos los subdirectorios del directorio indicado. Se puede indicar un "pattern" de bsqueda y si queremos incluir los subdirectorios que tenga el directorio indicado. 1etDirectory'oot, devuelve el directorio raz del directorio indicado. 1et)iles, devuelve un array de tipo String con los ficheros del directorio indicado. Se puede indicar un filtro de bsqueda. 273 1etLast*ccessTime, devuelve la fecha y hora del ltimo acceso al directorio. 1etLast*ccessTime,tc, dem que el anterior, pero la fecha y hora en formato UTC. 1etLast4riteTime, devuelve la fecha y hora de la ltima escritura realizada en el directorio. 1etLast4riteTime,tc, como el anterior, pero usando fechas UTC. 1etLogicalDries, devuelve un array con los nombres de las unidades lgicas en el formato: "<letra>:\". 1et+arent, devuelve el directorio de nivel superior del indicado. (oe, mueve un fichero o directorio y su contenido a una nueva localizacin. SetCreationTime, asigna la fecha y hora de creacin de un fichero o directorio. SetCreationTime,tc, como el anterior, pero la fecha/hora es UTC. SetCurrentDirectory, indica el directorio de trabajo de la aplicacin. SetLast*ccessTime, asigna la fecha y hora del ltimo acceso. SetLast*ccessTime,tc, como el anterior, pero la fecha/hora en formato UTC. SetLast4riteTime, asigna la fecha y hora de la ltima escritura. SetLast4riteTime,tc, como el anterior, pero usando la fecha y hora UTC.
Como hemos comentado, la clase DirectoryInfo siempre har referencia al directorio utilizado para instanciarla, por tanto algunos de los mtodos de la clase Directory se convierten en propiedades de esta clase y otros cambian de nombre para adecuarlos mejor a la accin a realizar con ellos. Por ejemplo, en la clase Directory tenemos el mtodo %&ists, que en DirectoryInfo es una propiedad. De igual forma, el mtodo (oe de la clase Directory es el mtodo (oeTo de DirectoryInfo. En cuanto a los mtodos de Directory para obtener o asignar la fecha de creacin, acceso, etc., en DirectoryInfo son propiedades de lectura/escritura. Dicho esto, no vamos a relacionar todos estos miembros de DirectoryInfo, ya que en la documentacin estn bien detallados y no nos resultar difcil de saber cmo usarlos. Lo que si queremos resaltar es que algunos de los mtodos de la clase DirectoryInfo, concretamente 1etDirectories y 1et)iles, no devuelven un array de tipo String, sino un array de objetos DirectoryInfo o )ileInfo respectivamente. Tambin debemos indicar que la clase DirectoryInfo tiene ciertas propiedades, como )ull$ame o $ame, que nos dan informacin del nombre de la ruta completa o del nombre del directorio. Y como nota final sobre Directory y DirectoryInfo, aclarar que el "filtro" (o pattern) que podemos usar para filtrar los directorios o ficheros obtenidos con 1etDirectories o 1et)iles solo pueden tener un filtro o especificacin, queremos aclarar este punto, ya que en Visual Basic 6.0, si indicbamos un filtro en los controles File o Directory, podamos indicar varios filtros separados por punto y coma (;), en las clases de .NET esto no se puede hacer. Para dejar claro estos puntos, veremos un ejemplo de cmo usar 1etDirectories y 1et)iles, con estas dos clases. 274 Im2orts S-stem Im2orts S-stem/I0 Ho%!le Ho%!le Const %ir1 As String = "DU?r!ebas" S!b Hain() Console/<rite'ine("#em2lo %e la clase Director-D") claseDirector-() Console/<rite'ine() Console/<rite'ine("#em2lo %e la clase Director-In8oD") claseDirector-In8o() Console/<rite'ine() 4 Console/"ea%'ine() n% S!b S!b claseDirector-() 4 "ecorrer los s!b%irectorios %e !n %irectorio Dim %irs() As String %irs = Director-/;etDirectories(%ir1) mostrar)icheros(%ir1, "O/tGt") 4 recorrer los 8icheros %e ca%a !no %e estos %irectorios )or ach %ir As String In %irs mostrar)icheros(%ir, "O/tGt") AeGt n% S!b 275 S!b mostrar)icheros(,-$al %ir As String, ,-$al 8iltro As String) Console/<rite'ine("'os 8icheros =0> %el %irectorioD =1>", + 8iltro, %ir) Dim 8ics() As String 8ics = Director-/;et)iles(%ir, 8iltro) )or ach 8ic As String In 8ics Console/<rite'ine(" )icheroD =0>", 8ic) AeGt n% S!b S!b claseDirector-In8o() Dim %i As AeJ Director-In8o(%ir1) Dim %irs() As Director-In8o %irs = %i/;etDirectories mostrar)icheros(%i, "O/O") )or ach %ir As Director-In8o In %irs mostrar)icheros(%ir, "O/O") AeGt n% S!b S!b mostrar)icheros(,-$al %ir As Director-In8o, ,-$al 8iltro As String) Console/<rite'ine("'os 8icheros =0> %el %irectorioD =1>", + 8iltro, %ir/)!llAame) Dim 8ics() As )ileIn8o 8ics = %ir/;et)iles(8iltro) )or ach 8ic As )ileIn8o In 8ics Console/<rite'ine(" )icheroD =0>", 8ic/Aame) 276 AeGt n% S!b n% Ho%!le
'as clases <ile y <ile-nfo Al igual que ocurre con las clases para manejar los directorios, tenemos dos clases diferentes para manejar los ficheros, una de ellas ()ile) todos los mtodos que tiene son estticos, por tanto podemos usarlos directamente, sin crear una nueva instancia de la clase. Por otro lado la clase )ileInfo es una clase de la que tenemos que crear un nuevo objeto para poder usarla, al crear ese objeto (o instancia), tenemos que indicar el fichero al que har referencia, ese fichero no tiene porqu existir previamente. En el siguiente cdigo podemos ver cmo usar estas dos clases. Im2orts S-stem Im2orts S-stem/I0 Ho%!le Ho%!le1 Const 8ic1 As String = "DU?r!ebasU?r!eba/tGt" Const 8ic2 As String = "DU?r!ebasUS!bDirU?r!eba3/tGt" S!b Hain() Console/<rite'ine("#em2lo %e la clase )ileD") clase)ile() Console/<rite'ine() Console/<rite'ine("#em2lo %e la clase )ileIn8oD") clase)ileIn8o() Console/<rite'ine() 4 Console/"ea%'ine() 277 n% S!b S!b clase)ile() 4 com2robar si el 8ichero eGiste I8 )ile/Gists(8ic1) 1hen Console/<rite'ine("l 8ichero 4=0>4 SI eGiste", 8ic1) Console/<rite'ine(")echa creaciSnD =0>", )ile/;etCreation1ime(8ic1)) lse Console/<rite'ine("l 8ichero 4=0>4 A0 eGiste", 8ic1) n% I8 n% S!b S!b clase)ileIn8o() Dim 8i As AeJ )ileIn8o(8ic2) 4 4 Com2robar si eGiste el 8icheroD I8 8i/Gists 1hen Console/<rite'ine("l 8ichero 4=0>4 SI eGiste", 8ic2) Console/<rite'ine(")echa creaciSnD =0>", 8i/Creation1ime) agregar1eGto(8i) lse Console/<rite'ine("l 8ichero 4=0>4 A0 eGiste", 8ic2) 4 lo creamos 8i/Create() n% I8 n% S!b S!b agregar1eGto(,-$al 8i As )ileIn8o) 278 Dim 8s As Stream<riter 8s = 8i/Create1eGt 8s/<rite'ine("Hola, H!n%o") 8s/)l!sh() 8s/Close() 4 8i/"e8resh() Console/<rite'ine("1amaTo D =0>", 8i/'ength) n% S!b n% Ho%!le
Cifrar y descifrar un fic%ero usando (ile o (ileInfo En los sistemas operativos Windows XP y Windows 2003, al mostrar el cuadro de dilogo de las propiedades avanzadas de un fichero, nos permite cifrarlo, de forma que el usuario actual slo tenga acceso al contenido del mismo. Fi'ura 0<.0<.0$ Pindos L82200< nos permite cifrar un fic?ero Tanto la clase File como )ileInfo tienen mtodos para realizar esta operacin, as como la inversa: descifrarlo. Cuando ciframos un fichero, solo el usuario actual podr acceder a su contenido (y el resto de administradores), la forma de hacerlo es bien simple: solo tenemos que llamar al mtodo %ncrypt o Decrypt para cifrarlo o 279 descifrarlo. Cuando est cifrado, el nombre del fichero se mostrar en un color diferente al del resto de ficheros (de forma predeterminada en verde). Esta forma de cifrado es diferente a la que vimos en la leccin anterior, ya que si ciframos el contenido de un fichero de forma "manual", siempre estar cifrado, independientemente de quin acceda al fichero. Cuando utilizamos los mtodos para cifrar o descifrar un fichero, no recibiremos ninguna excepcin si aplicamos el mtodo de cifrar sobre un fichero ya cifrado. Pero si queremos saber si un determinado fichero est o no cifrado, lo podemos averiguar mediante la propiedad *ttribute de la clase )ileInfo, particularmente comprobando si tiene activado el atributo )ile*ttributes.%ncrypted. En el siguiente cdigo tenemos ejemplos de cmo usar las clases )ile y )ileInfo para comprobar si un fichero ya est cifrado, en cuyo caso lo desciframos, y si resulta que no est cifrado, lo ciframos. Im2orts S-stem Im2orts S-stem/I0 Ho%!le Ho%!le1 Const 8ic1 As String = "DU?r!ebasU?r!eba/tGt"
S!b Hain() ci8ra%o)ile(8ic1) Console/<rite'ine() ci8ra%o)ileIn8o(8ic2) Console/<rite'ine() 4 Console/"ea%'ine() n% S!b 4 Ci8rar V Desci8rar 8icheros 4 - com2robar si -a est6n ci8ra%os/// S!b ci8ra%o)ile(,-$al 8ichero As String) 4 com2robar si est6 ci8ra%o, 280 Dim atrib As )ileAttrib!tes atrib = )ile/;etAttrib!tes(8ichero) I8 (atrib An% )ileAttrib!tes/ncr-2te%) = )ileAttrib!tes/ncr-2te% 1hen Console/<rite'ine("l 8ichero =0> -a estaba ci8ra%o/", 8ichero) )ile/Decr-2t(8ichero) lse )ile/ncr-2t(8ichero) Console/<rite'ine("l 8ichero =0> no estaba ci8ra%o/", 8ichero) n% I8 n% S!b S!b ci8ra%o)ileIn8o(,-$al 8ichero As String) 4 com2robar si est6 ci8ra%o, Dim 8i As AeJ )ileIn8o(8ichero) Dim atrib As )ileAttrib!tes atrib = 8i/Attrib!tes I8 (atrib An% )ileAttrib!tes/ncr-2te%) = )ileAttrib!tes/ncr-2te% 1hen Console/<rite'ine("l 8ichero =0> -a estaba ci8ra%o/", 8i/)!llAame) 8i/Decr-2t() lse 8i/ncr-2t() Console/<rite'ine("l 8ichero =0> no estaba ci8ra%o/", 8i/)!llAame) n% I8 n% S!b n% Ho%!le
281
Abrir fic%eros para a"re"ar o leer el contenido Estas dos clases de manipulacin de ficheros exponen una serie de mtodos que nos permiten abrir el fichero al que hace referencia la clase para leer el contenido del mismo o para escribir en l. Dependiendo del mtodo usado, ste devolver un objeto de tipo )ileStream o Stream'eader o Stream4riter. Esto nos permite de una forma fcil poder acceder al contenido que tiene sin necesidad de usar otros constructores. Lo que si debemos tener en cuenta es que en el caso de que leamos o escribamos texto, ste se guardar usando la codificacin UTF-8, por tanto si queremos usar otro tipo de codificacin, tendremos que abrirlos usando los constructores correspondientes, normalmente los de las clases Stream'eader y Stream4riter. De estas dos clases nos ocuparemos en el siguiente captulo. Veamos un par de ejemplos que nos permitan leer el contenido o agregar nuevo texto a los ficheros. Im2orts S-stem Im2orts S-stem/I0 Ho%!le Ho%!le1 Const 8ic3 As String = "DU?r!ebasU?r!eba./tGt" Const 8ic. As String = "DU?r!ebasUS!bDirU?r!eba./tGt" S!b Hain() abrir)ile(8ic3) Console/<rite'ine() abrir)ileIn8o(8ic.) Console/<rite'ine() 4 Console/"ea%'ine() n% S!b 4 Abrir 8icheros 2ara leer el conteni%o o escribir en Rl S!b abrir)ile(,-$al 8ichero As String) 282 I8 )ile/Gists(8ichero) 1hen Dim 8s As )ileStream = )ile/02en"ea%(8ichero) Dim enc As AeJ S-stem/1eGt/F1)Xnco%ing() Dim %atos(102.) As ,-te 8s/"ea%(%atos, 0, %atos/'ength) 8s/Close() Dim s As String = enc/;etString(%atos, 0, %atos/'ength) Console/<rite'ine("l conteni%o %eD =0>=2> esD=2>=1>", + 8ichero, s, 5bCr'8) lse Console/<rite'ine(";!ar%an%o algo en el 8ichero =0>", 8ichero) Dim 8s As )ileStream = )ile/02en<rite(8ichero) Dim enc As AeJ S-stem/1eGt/F1)Xnco%ing() Dim %atos() As ,-te = enc/;et,-tes("7Hola b-tesY") 8s/<rite(%atos, 0, %atos/'ength) 8s/Close() n% I8 n% S!b S!b abrir)ileIn8o(,-$al 8ichero As String) Dim 8i As AeJ )ileIn8o(8ichero) I8 8i/Gists 1hen 4 abrir el 8ichero como teGto - leemos el conteni%o Dim sr As Stream"ea%er = 8i/02en1eGt Dim s As String = sr/"ea%1on% Console/<rite'ine("l conteni%o %eD =0>=2> esD=2>=1>", + 8ichero, s, 5bCr'8) 283 lse Console/<rite'ine(";!ar%an%o algo en el 8ichero =0>", 8ichero) Dim sJ As AeJ Stream<riter(8i/02en<rite) sJ/<rite'ine("Hello b-tesY") sJ/Close() n% I8 n% S!b n% Ho%!le
En el mtodo a#rirFile utilizamos la clase )ile para abrir, leer y escribir en un fichero. En este caso estamos usando un objeto del tipo )ileStream para acceder al contenido de dicho fichero, como sabemos, esta clase solo trabaja con bytes, por tanto si queremos leer el contenido del mismo, debemos hacerlo mediante un array de bytes y para mostrar en formato texto ese contenido, tenemos que aplicarle la codificacin correspondiente para extraer el contenido en forma de una cadena. Lo mismo hacemos para guardar una cadena en el fichero, (en caso de que no exista previamente). En el mtodo a#rirFileInfo, en lugar de crear un objeto )ileStream para acceder al contenido, usamos uno del tipo Stream'eader, ya que esta clase si que nos devolver una cadena sin necesidad de hacer conversiones extras. Lo mismo ocurre a la hora de escribir algo en el fichero, aunque en esta ocasin utilizamos un objeto del tipo Stream4riter. En el primer caso, cuando leemos el contenido, utilizamos el mtodo OpenTe&t, el cual devuelve un objeto del tipo Stream'eader. Por otra parte, cuando queremos guardar texto, lo que hacemos es pasarle al constructor de la clase Stream4riter el objeto )ileStream devuelto por el mtodo Open4rite.
Nota3 Como hemos podido comprobar, cuando tratamos con las clases DirectoryInfo o FileInfo, siempre estamos tratando con instancias que pueden ser diferentes y por tanto mltiples, esas instancias siempre manejan el directorio o fichero usado en el constructor, por tanto, ser preferible usar estas clases cuando tengamos que hacer varias operaciones sobre el mismo directorio o fichero. Por otro lado, si solo queremos hacer pocas operaciones, podemos usar las clases Directory o File, ya que no necesitaremos crear un objeto en memoria para hacer las tareas que necesitemos hacer.
284
%anipular cadenas relacionadas con fic#eros y directorios usando ,at# La clase +at" es una clase especial, en la que todos los mtodos son estticos, por tanto para usarla no necesitamos crear una instancia. Lo de "especial" es porque esta clase realmente no realiza ninguna accin "fsica" sobre los directorios (o ficheros), simplemente nos permite manipular los nombres de esos directorios o ficheros. Por ejemplo, podemos usar esta clase para obtener informacin sobre un fichero, como el nombre completo, el nombre del directorio o la extensin. En el siguiente cdigo vemos cmo usar algunos de esos mtodos: Const 8ic1 As String = "DU?r!ebasU?r!eba/tGt" S!b clase?ath() Console/<rite'ine("Aombre com2letoD =0>", 8ic1) Console/<rite'ine("l nombreD =0>", ?ath/;et)ileAame(8ic1)) Console/<rite'ine("Sin eGtensiSnD =0>", ?ath/;et)ileAame<itho!tGtension(8ic1)) Console/<rite'ine("'a eGtensiSnD =0>", ?ath/;etGtension(8ic1)) Console/<rite'ine("l %irectorioD =0>", ?ath/;etDirector-Aame(8ic1)) n% S!b
Nota3 Debido a que la clase Path solo maneja cadenas, podemos usar los mtodos de esta clase con nombres de ficheros o directorios que no existen, y por tanto, cualquier cambio que hagamos en esos parmetros, no afectarn a ningn fichero ni directorio.
285 Debido a que los ensamblados de .NET podemos usarlos con diferentes sistemas operativos (al menos en teora), esta clase proporciona ciertas propiedades que nos permiten averiguar los caracteres especiales de cada sistema, por ejemplo para saber el separador usado por los directorios.
Con esta clase tambin podemos crear ficheros con nombres temporales, de forma que nos aseguremos de que no habr otro fichero "temporal" con el mismo nombre. Igualmente, por medio del mtodo 1etTemp+at" podemos averiguar el directorio temporal del sistema. Incluso podemos generar nombres aleatorios "seguros" para ficheros o directorios mediante el mtodo 1et'andom)ile$ame. Veamos un ejemplo para generar estos nombres temporales y aleatorios: Dim %ir As String %ir = ?ath/;et1em2?ath() Console/<rite'ine("Directorio tem2oralD =0>", %ir) Dim 8ic As String 8ic = ?ath/;et1em2)ileAame() Console/<rite'ine(")ichero tem2oralD =0>", 8ic) 4 8ic = ?ath/;et"an%om)ileAame() Console/<rite'ine(")ichero ran%omD =0>", 8ic)
En los dos primeros casos, se usa el directorio )TEM8) del sistema. Y en el caso del fichero temporal, la extensin es .tmp En el nombre aleatorio, tanto el nombre como la extensin estn formados por caracteres "imprimibles" aleatorios.
286
*as clases para leer o escri#ir en los streams Las clases que podemos usar para leer o escribir en los streams, (y por extensin en los ficheros a los que apuntan esos streams), son: Binary'eader, lee tipos primitivos o cadenas codificadas desde un )ileStream. Binary4riter, escribe tipos primitivos o cadenas codificadas en un )ileStream. Stream'eader, lee caracteres desde un )ileStream, usando codificacin para convertir los caracteres a/desde bytes. Stream4riter, escribe caracteres a un )ileStream, usando codificacin para convertir los caracteres en bytes. String'eader, lee caracteres desde un String. La salida puede ser a un stream en cualquier codificacin o a una cadena. String4riter, escribe caracteres a un String. Al igual que String'eader, la salida puede ser a un stream usando cualquier codificacin o a una cadena.
Te&t'eader, es la clase abstracta base para Stream'eader y String'eader. Te&t4riter, es la case abstracta base para Stream4riter y String4riter.
287 'as clases StreamEeader y StreamJriter Aunque ya hemos utilizado algunas de estas clases en los ejemplos que hemos estado viendo, nos vamos a centrar en las dos que con ms asiduidad usaremos: Stream'eader y Stream4riter, aunque solo trataremos algunos temas, ya que en lneas generales, ya "deberamos" saber cmo usarlas.
Estas dos clases las podemos crear directamente, indicando el fichero al que accederemos o bien, podemos usar el valor devuelto por alguna otra clase o mtodo de las clases )ile o )ileInfo.
La codificacin de los fic%eros de *;$ En lo que resta de este captulo nos centraremos en los formatos de codificacin usados con estas dos clases, y por regla general con los ficheros de .NET. La principal ventaja de estas clases es que nos permiten acceder al contenido de los ficheros en modo texto, que es lo que necesitaremos en la mayora de los casos. Tambin podremos indicar en el constructor de estas clases la codificacin a usar. Como ya hemos comentado en otras ocasiones, por defecto esa codificacin es UTF- 8, es decir caracteres Unicode de 8 bits. Pero si queremos leer y escribir ficheros que sean compatibles con otras aplicaciones de Windows, por ejemplo, nuestras aplicaciones de Visual Basic 6.0, deberamos usar la codificacin ANSI, que es la predeterminada de los sistemas Windows. De esta forma podremos leer y escribir caracteres "raros" como la letra ee o las vocales con tildes. Para indicar la codificacin a usar, lo tendremos que indicar como parmetro del constructor, por ejemplo, para abrir un fichero con la codificacin predeterminada de Windows, lo haremos de esta forma: Dim sr As AeJ Stream"ea%er(8ic1, nco%ing/De8a!lt) Es decir, usamos la codificacin Default, que es la predeterminada de Windows.
Nota3 Si no indicamos la codificacin que queremos usar, sta ser UTF-8. Pero debemos tener en cuenta que esta codificacin solamente leer correctamente los ficheros que se hayan guardado previamente usando esa misma codificacin, por tanto, si queremos leer ficheros creados con VB6, no deberamos usar UTF-8, (Encoding.UTF8), sino Encoding.Default.
288 Algunos tipos de ficheros (segn la codificacin usada), guardarn al principio del mismo una marca: BOM (Byte Order (ar6) indicando la codificacin usada para escribir en ellos. El propio .NET puede usar dicha marca a la hora de leer en los ficheros, para que as lo haga, podemos indicarlo en el constructor de la clase Stream'eader: Fi'ura 0<.0<.02 /etectar autom.ticamente la codificacin usada Aunque esta deteccin automtica solo funcionar con los tipos de ficheros que guardan dicha informacin, que son los tipos: UTF-8, UTF-16 y UTF-32, de estos dos ltimos existen dos marcas segn se utilice "big-endian" o "little-endian", es decir, si el byte ms significativo est al principio o al final respectivamente. Estas marcas, conocidas en .NET como "prembulos", se pueden averiguar tambin por medio del mtodo 1et+reamble de la clase %ncoding. En la siguiente tabla podemos ver los valores hexadecimales de las marcas usadas en los ficheros. Codificacin Nmero de bytes Secuencia de bytes (hex) UTF-8 3 EF BB BF UTF-16 Little-Endian 2 FF FE UTF-16 Big-Endian 2 FE FF UTF-32 Little-Endian 4 00 00 FF FE UTF-32 Big-Endian 4 00 00 FE FF Ta#la 0<.0<.0$ Marcas 9B7M: de los fic?eros GTF
Cuando leemos la informacin de un fichero abierto por medio de Stream'eader, podemos hacerlo lnea a lnea o bien todo el contenido de una sola vez. Como vimos en la leccin de los streams, podemos usar el mtodo 'eadLine o 'eadTo%nd para realizar estas dos operaciones de lectura.
Al usar la clase Stream4riter, tambin podemos indicar la codificacin a usar para guardar el texto del fichero: 289 Dim sJ As AeJ Stream<riter(8ic2, )alse, nco%ing/De8a!lt) El segundo parmetro del constructor lo usaremos para indicar si queremos aadir texto al contenido del fichero (True) o simplemente queremos sobrescribirlo, ()alse), de forma que cualquier contenido anterior desaparezca.
Como recomendacin final, y siguiendo con el tema de la codificacin, (que aunque parezca algo "trivial" a muchos usuarios les da algn que otro dolor de cabeza), decir que debemos tener precaucin a la hora de elegir la ms adecuada. Si queremos que los ficheros que manejemos con nuestra aplicacin de Visual Basic 2005 sean "compatibles" con los de VB6, deberamos usar la codificacin %ncoding.Default. Si los ficheros solo se usarn desde aplicaciones de .NET, podemos usar la codificacin predeterminada, es decir: UTF-8. Si vamos a guardar ficheros XML, deberamos usar siempre codificacin UTF- 8, que es la predeterminada para ese formato de ficheros.
El o#(eto M% Entre los objetivos de este curso no est hablar del objeto (y y de los objetos que contiene, los cuales nos facilitan un acceso rpido a la mayora de recursos con los que habitualmente trabajaremos en nuestras aplicaciones. 290 Pero no nos gustara dejar un "mal sabor de boca", el cual se quedara si no hablramos, aunque sea un poco de este objeto. Tambin es cierto que el objeto (y (y el resto de objetos que lo compone) ser uno de los que con ms frecuencia encontraremos documentacin y ejemplos, tanto en la Web como en la propia documentacin de Visual Basic 2005, por tanto quisiramos que el lector no deje de leer sobre este objeto, ya que en muchas ocasiones nos ayudar a hacer muchas de las tareas habituales en cualquier aplicacin, e incluso los desarrolladores de VB6 encontrarn muchos de los objetos a los que estn acostumbrados a utilizar, como el objeto *pp o la forma de acceder a la coleccin de los formularios cargados en la memoria.
El obeto %y.Computer.<ileSystem En cuanto a lo que respecta al sistema de archivos, la mayora de la funcionalidad est incluida en el objeto (y.Computer.)ileSystem. Si bien, debemos aclarar que esos objetos o propiedades, realmente son una especie de acceso directo a las clases del propio .NET Framework, y esa es la excusa de no haber tratado este objeto con ms profundidad, ya que si sabemos manejar el resto de clases de .NET, sabremos trabajar con el objeto (y. Entre las propiedades del objeto (y.Computer.)ileSystem, podemos encontrar muchos de los mtodos que exponen las clases )ile y Directory, aunque estos mtodos tendrn nombres diferentes, por ejemplo, en lugar de tener un mtodo %&ists, nos encontramos con uno para averiguar si existe un directorio: Directory%&ists, y otro para averiguar si existe un fichero: )ile%&ists.
Tambin quisiramos resaltar dos mtodos que pueden sernos de bastante utilidad cuando estemos buscando texto en los ficheros o cuando queramos abrir un fichero con una estructura determinada, nos estamos refiriendo a los mtodos: )indIn)iles y OpenTe&t)ield+arse respectivamente.
#uscar te5to en fic%eros con (indIn(iles )indIn)iles devuelve una coleccin "generic" de tipo String y de solo lectura con los ficheros que contienen el texto indicado. En el siguiente ejemplo buscamos ficheros que contenga el texto "hola", se ignore las maysculas y minsculas y se haga la bsqueda tambin en los subdirectorios: Dim list As S-stem/Collections/0b#ectHo%el/"ea%0nl-Collection(08 String) 4 ,!scar teGto en los 8icheros/ list = H-/Com2!ter/)ileS-stem/)in%In)iles( + 291 "DU?r!ebas", "hola", + 1r!e, )ileI0/Search02tion/SearchAllS!bDirectories) 4 Hostrar los 8icheros con el teGto in%ica%o/ )or ach name As String In list Console/<rite'ine(name) AeGt
5aminar el contenido de un fic%ero con formato con :pen$e5t(ield9arse En el siguiente ejemplo vamos a abrir un fichero con una estructura, realmente delimitado con tabuladores, leeremos el contenido y lo mostraremos examinando cada uno de los campos. En este ejemplo, podramos llamar al mtodo Te&t+arser de la siguiente forma: teGt?arser("DU?r!ebasU?r!eba 2arse/tGt", 5b1ab) S!b teGt?arser(,-$al 8ic As String, ,-$al se2 As String) 4 Creamos el "2arser"/ Dim rea%er As )ileI0/1eGt)iel%?arser rea%er = H-/Com2!ter/)ileS-stem/02en1eGt)iel%?arser(8ic) 4 'e in%icamos I!e b!scaremos %elimita%ores/ rea%er/1eGt)iel%1-2e = )ileI0/)iel%1-2e/Delimite% 4 Fn arra- con los %elimita%ores/ rea%er/Delimiters = AeJ String() =se2> Dim 8ila() As String 4 Hientras ha-a %atos/// <hile Aot rea%er/n%08Data 1r- 4 'eemos to%os los cam2os/ 8ila = rea%er/"ea%)iel%s() Console/<rite'ine("'os cam2os %e la 8ila sonD") 292 4 'os mostramos/ )or ach cam2o As String In 8ila Console/<rite("=0>, ", cam2o) AeGt Console/<rite'ine() Catch eG As Gce2tion Console/<rite'ine("rrorD " P eG/Hessage) n% 1r- n% <hile n% S!b
Introduccin Esta es otra de las reas con la que los desarrolladores de VB6 se vern beneficiados, al menos si nuestra intencin es la acceder a la red de redes: Internet. Aunque tambin tendremos clases para interactuar con nuestra red local o empresarial, adems de clases para comunicacin por "sockets" y acceso a FTP, etc. 293 Las mayora de las clases que necesitemos para acceder a la red, las encontraremos en el espacio de nombres System.$et.
4cceso a Internet System.Net: 'as clases para acceder a la red o Las clases de System.Net o Acceder a una pgina Web o Acceder a un servidor FTP o Acceso rpido a la red con My.Computer.Network Averiguar si tenemos red disponible Hacer Ping a un equipo Bajar un fichero desde una direccin Web
'as clases de System.Net En este espacio de nombres tenemos una gran cantidad de clases, entre las que podemos destacar las siguientes. *ut"entication(anager, administra los mdulos de autenticacin durante el proceso de autenticacin del cliente. *ut"ori.ation, contiene un mensaje de autenticacin de un servidor de Internet. Coo6ie, proporciona mtodos y propiedades para administrar los cookies. CredentialCac"e, proporciona almacenamiento para mltiples credenciales. Dns, proporciona funcionalidad simple para resolucin de nombres de dominios. Dns+ermission, controla los permisos de acceso a servidores DNS en la red. %nd+oint, clase abstracta que identifica una direccin de red. )ile4eb'e9uest, proporciona una implementacin del sistema de archivos de la clase 4eb'e9uest. )ile4eb'esponse, proporciona una implementacin del sistema de archivos de la clase 4eb'esponse. )tp4eb'e9uest, implementa un cliente FTP. )tp4eb'esponse, encapsula una respuesta desde una peticin a un servidor FTP. 7ttpListener, proporciona un protocolo de escucha HTTP simple. 7ttpListenerBasicIdentity, proporciona la identidad para la clase 7ttpListener. 7ttpListenerConte&t, proporciona acceso a las peticiones y respuestas utilizadas por la clase 7ttpListener. 7ttpListener'e9uest, describe una peticin HTTP a un objeto 7ttpListener. 7ttpListener'esponse, representa una respuesta a una peticin administrada por un objeto 7ttpListener. 294 7ttp#ersion, define las versiones HTTP soportadas por las clases 7ttp4eb'e9uest y 7ttp4eb'esponse. 7ttp4eb'e9uest, proporciona una implementacin HTTP especfica de la clase 4eb'e9uest. 7ttp4eb'esponse, proporciona una implementacin HTTP especfica de la clase 4eb'esponse. I+*ddress, proporciona una direccin IP (Internet +rotocol). I+%nd+oint, representa un punto final de red como una direccin IP y un nmero de puerto. I+7ost%ntry, proporciona una clase contenedora para la informacin de direccin de host de Internet. $et!or6Credential, proporciona credenciales para autenticacin basada en contrasea. Serice+oint, proporciona administracin de conexiones para las conexiones HTTP. Serice+oint(anager, administra la coleccin de objetos Serice+oint. Soc6et*ddress, almacena informacin serializada de las clases derivadas de EndPoint. Soc6et+ermission, controla los derechos para realizar o aceptar conexiones en una direccin de transporte. 4ebClient, proporciona mtodos comunes para enviar o recibir datos desde un recurso identificado por una URI (,niform 'esource Identifier). 4eb+ermission, controla los derechos de acceso a un recurso HTTP de Internet. 4eb+ro&y, contiene la configuracin del proxy HTTP de la clase 4eb'e9uest. 4eb'e9uest, realiza una peticin a una URI. 4eb'e9uest(et"ods, clase contenedora para las clases 4eb'e9uest(et"ods.)tp, 4eb'e9uest(et"ods.)ile, y 4eb'e9uest(et"ods.7ttp. 4eb'e9uest(et"ods.)ile, representa los tipos del protocolo de fichero que se pueden usar con una peticin FILE. 4eb'e9uest(et"ods.)tp, representa los tipos del protocolo FTP que se pueden usar con una peticin FTP. 4eb'e9uest(et"ods.7ttp, representa los tipos del protocolo HTTP que se pueden usar con una peticin HTTP. 4eb'esponse, proporciona una respuesta desde una URI.
Acceder a una p&gina Jeb Empezaremos viendo cmo acceder a una pgina Web. Para este tipo de acceso vamos a usar la clase abstracta 4eb'e9uest y de paso algunas otras clases como 4eb'esponse y otras para manejar el stream recibido con el contenido de la pgina solicitada. Veamos un pequeo ejemplo, (basado y simplificado de uno de la documentacin), para hacerlo: 295 S!b leer?agina<eb(,-$al laFrl As String) 4 Cear la solicit!% %e la F"'/ Dim reI!est As <eb"eI!est = <eb"eI!est/Create(laFrl) 4 0btener la res2!esta/ Dim res2onse As <eb"es2onse = reI!est/;et"es2onse() 4 Abrir el stream %e la res2!esta recibi%a/ Dim rea%er As AeJ Stream"ea%er(res2onse/;et"es2onseStream()) 4 'eer el conteni%o/ Dim res As String = rea%er/"ea%1on%() 4 Hostrarlo/ Console/<rite'ine(res) 4 Cerrar los streams abiertos/ rea%er/Close() res2onse/Close() n% S!b Si la direccin que estamos solicitando es una pgina "activa", el valor que recibiremos es el cdigo "cliente", es decir, el cdigo que se enviar al navegador. Si necesitamos hacer algunas peticiones ms especficas o necesitamos obtener alguna otra informacin podemos usar un objeto del tipo 7ttp4eb'e9uest el cual est basado en la clase 4eb'e9uest, pero que la ampla para ofrecer otros tipos de informacin y acciones ms adecuadas a una peticin HTTP. La forma de usar esta clase sera igual que 4eb'e9uest, adems de que la documentacin recomienda usar el mtodo Create de 4eb'e9uest para crear una nueva instancia de 7ttp4ebre9uest. En el siguiente ejemplo, le indicamos que estamos usando un navegador desde un "Smartphone": S!b leer?agina<eb2(,-$al laFrl As String) 4 Cear la solicit!% %e la F"'/ Dim h"eI!est As Htt2<eb"eI!est = + C1-2e(<eb"eI!est/Create(laFrl), Htt2<eb"eI!est) 4 2ara I!e lo %e5!el5a como si acce%iRramos con !n Smart2hone h"eI!est/FserAgent = + 296 "Ho&illaV./0 (com2atible[ HSI ./01[ <in%oJs C[ Smart2hone[ 1N6G220)" 4 0btener la res2!esta - abrir el stream %e la res2!esta recibi%a/ Dim rea%er As AeJ Stream"ea%er(h"eI!est/;et"es2onse/;et"es2onseStream) Dim res As String = rea%er/"ea%1on%() 4 Hostrarlo/ Console/<rite'ine(res) 4 Cerrar el stream abierto/ rea%er/Close() n% S!b
Acceder a un servidor <T, Por medio de la clase )tp4eb'e9uest podemos acceder a una direccin FTP de forma bastante fcil, como es habitual en este tipo de clases, este objeto se crea mediante el mtodo compartido Create, al que le debemos pasar la direccin FTP a la que queremos acceder. Debido a que los sitios FTP pueden estar protegidos por contrasea, es posible que necesitemos asignar la propiedad Credentials de este objeto, esto lo podemos hacer asignando el objeto creado a partir de la clase $et!or6Credential. Los distintos comandos que necesitemos enviar al FTP lo haremos mediante la propiedad (et"od. En el siguiente ejemplo utilizamos la clase )tp4eb'e9uest para listar el contenido de un directorio FTP pblico. S!b Hain() listar)1?("8t2D"VV8t2/re%iris/es", "anonimo!s\na%ie/com", "") 4 Console/"ea%'ine() n% S!b S!b listar)1?(,-$al %ir As String, ,-$al !ser As String, ,-$al 2ass As String) 297 Dim %ir)t2 As )t2<eb"eI!est = C1-2e()t2<eb"eI!est/Create(%ir), )t2<eb"eI!est) Dim cr As AeJ AetJorQCre%ential(!ser, 2ass) %ir)t2/Cre%entials = cr %ir)t2/Hetho% = "'IS1" Dim rea%er As AeJ Stream"ea%er(%ir)t2/;et"es2onse()/;et"es2onseStream()) Dim res As String = rea%er/"ea%1on%() 4 Hostrarlo/ Console/<rite'ine(res) 4 Cerrar el stream abierto/ rea%er/Close() n% S!b
Acceso r&pido a la red con %y.Computer.NetForI El objeto (y tambin contiene objetos con los que podemos acceder a ciertas caractersticas de la red, en particular mediante el objeto (y.Computer.$et!or6 tenemos acceso a una propiedad y tres mtodos con los que podemos hacer lo siguiente: Is*aillable, esta propiedad nos permite saber si la red est disponible. +ing, con este mtodo podemos hacer un "ping" a un equipo remoto. Do!nload)ile, nos permite bajar un fichero desde una direccin Web. ,pload)ile, nos permite enviar un fichero a una direccin Web. En el siguiente cdigo tenemos ejemplos de estos mtodos y propiedades: I8 H-/Com2!ter/AetJorQ/IsA5ailable = 1r!e 1hen Console/<rite'ine("l or%ena%or est6 conecta%o/") lse Console/<rite'ine("l or%ena%or no est6 conecta%o/") n% I8 4 298 I8 H-/Com2!ter/AetJorQ/?ing("1E2/16X/1/26") 1hen Console/<rite'ine("l eI!i2o est6 acti5o/") lse Console/<rite'ine("Se ha sobre2asa%o el tiem2o %e es2era/") n% I8 4 Dim 8icDest As String = "DU?r!ebasUrobots/tGt" H-/Com2!ter/AetJorQ/DoJnloa%)ile( + "htt2D"VVJJJ/elg!ille/in8oVrobots/tGt", + 8icDest) 4 I8 H-/Com2!ter/)ileS-stem/)ileGists(8icDest) 1hen Console/<rite'ine("l 8ichero se ha ba#a%o") lse Console/<rite'ine("l 8ichero no se ha ba#a%o") n% I8 4--ES7 4 /4T7S
299 %(dulo B ? -ntroducci(n En este mdulo, aprenderemos a trabajar con datos y fuentes de datos en Visual Basic 2005. Sin lugar a dudas, la mayora de los desarrolladores que vienen de Visual Basic 6, es aqu dnde encuentran las mayores dificultades de migracin, ya que el modelo de acceso a datos ha variado bastante y adecuarse a los cambios, es lo que a buen seguro representa el mayor esfuerzo en la curva de aprendizaje de Visual Basic 2005. An as y con todo esto, no se preocupa, no est solo o sola. En este tutorial, encontrar las cosas ms importantes que debe saber, para trabajar con fuentes de datos con Visual Basic 2005. De esta manera, aprender en poco tiempo, a encontrarse cmodo en el nuevo entorno de clases de ADO.NET y podr as, sacar el mximo provecho a sus desarrollos. Becomendacin3 Se acuerda de ADO, DAO, etc.? ,na recomendaci<n 9ue le "ago para seguir esta importantIsima parte del tutorial es pasar p@gina a *DO3 D*O y otros accesos a datos 9ue us@bamos en #isual Basic G. $o intente comparar *DO.$%T con los anteriores ni intente tampoco usar los anteriores con #isual Basic 2BBC. Debemos pasar p@gina y olidarnos por completo de lo 9ue sabemos "asta a"ora para centrarnos en los nueos "@bitos3 de lo contrario3 lo ;nico 9ue conseguir@ ser@ perder el tiempo y complicar m@s su aprendi.aje li@ndose. Productos Visual Studio Express Las partes que forman parte de este mdulo son las siguientes: Cap!tulo @
Descripcin de ADO.NET Cap!tulo 1
Acceso conectado a bases de datos Cap!tulo A
Acceso desconectado: DataSets y DataAdapters Cap!tulo B
DataSets tipados 300 Cap!tulo 3
Enlace a formularios
-ntroducci(n Si usted ha tenido amplia experiencia trabajando con ADO y Visual Basic 6, debe saber que ADO.NET no es ADO, de hecho, ADO.NET plantea tal cantidad de cambios, que lo mejor que puede hacer en el caso de tener amplia experiencia en ADO, es olvidarse de ste y aprender lo que ADO.NET le ofrece. Las comparaciones son humanas, y seguro que a medida que avancemos en nuestro aprendizaje con ADO.NET, har alguna en bastantes ocasiones. De hecho, hay estrechas similitudes entre ADO y ADO.NET, pero mi sugerencia, es tratar en lo posible, de no prestar atencin a esto. A continuacin veremos todo lo que debe saber sobre ADO.NET para crear aplicaciones que accedan a fuentes de datos desde Visual Basic 2005. Comprobar que ahora, es incluso mucho ms fcil y rpido, si bien, es necesario conocer el modelo con el que se trabaja para poder saber lo que hacemos en un momento dado. %(dulo B ? Cap!tulo @
1. Acercndonos a ADO.NET 2. System.Data 3. Los proveedores de acceso a datos 4. El concepto DataBinding 301 5. Otras consideraciones
%(dulo B ? Cap!tulo @ @. Acerc&ndonos a A"$.NET ADO.NET ha sufrido a lo largo de los ltimos aos diferentes mejoras y actualizaciones, desde que .NET apareci. El resumen de las diferentes versiones de ADO.NET podra quedar de la siguiente forma. ADO.NET 1.0 apareci con Microsoft .NET Framework 1.0. Posteriormente, ADO.NET 1.1 sufri una pequeas y casi inapreciables actualizaciones con la aparicin de Microsoft .NET Framework 1.1. En el caso del entorno Visual Basic 2005 Express, ste trabaja con Microsoft .NET Framework 2.0 y por lo tanto, utiliza ADO.NET 2.0, el cul aade algunas caractersticas nuevas adicionales. En nuestro caso, nos centraremos nica y exclusivamente en ADO.NET como modelo de objetos de acceso a datos para la plataforma .NET de Microsoft, ya que es el mismo para cualquier tipo de versin de ADO.NET.
78u0 es A"$.NET9 ADO.NET posee las funcionalidades de ADO con las posibilidades aadidas del uso de documentos XML de forma nativa, junto a una disposicin de objetos que aumentan el rendimiento de los desarrolladores, y posibilitan ms an, el trabajo y desarrollo de aplicaciones en n-capas. Para el que no sepa que es ADO ni ADO.NET, el resumen de ADO.NET sera el de un conjunto de clases que nos permite leer e interactuar con fuentes de datos almacenadas en bases de datos y otras fuentes de almacenamiento de datos. 302 ADO.NET proporciona diferentes clases del nombre de espacio S%stem./ata dentro de las cules, destacaremos por encima de todas, la clase /ataVie, la clase /ataSet y la clase /ataTa#le. Este conjunto de clases de carcter armnico, funcionan de igual forma con la capa inferior que es la que corresponde a los proveedores de acceso a datos con los que podemos trabajar. Esto facilita el trabajo en n-capas y la posible migracin de aplicaciones que utilicen una determinada fuente de datos y deseemos en un momento dado, hacer uso de otra fuente de datos.
78u0 pasa con A"$ EecordSet9 *DO 'ecordSet pertenece a ADO y como hemos dicho al principio, es una parte de ADO y por lo tanto lo debemos olvidar. La forma de trabajar de ADO a ADO.NET vara, pero para que vea que ADO.NET no es tan complicado y que complementa en muchas cosas lo que sabemos de ADO, diremos que la funcionalidad de *DO 'ecordSet ha sido sustituida por los objetos /ataSet y /ataBeader de ADO.NET. Cuando veamos ms adelante cada una de las partes tericas en un ejemplo prctico, observaremos que el trabajo con fuentes de datos es realmente sencillo.
78u0 capas o 6u0 partes #ay dentro de A"$.NET9 Dentro de ADO.NET tenemos dos partes importantes. La primera de ellas es la que corresponde con el nombre de espacio S%stem./ata y que constituye los objetos y clases globales de ADO.NET. La otra parte es la que corresponde con los objetos que permiten el acceso a datos a una determinada fuente de datos desde ADO.NET y que utilizan as mismo, las clases del nombre de espacio S%stem./ata. Esta ltima parte, queda constituida por las clases y objetos de los diferentes proveedores de acceso a datos como se muestra en la figura 1. &isin "eneral de las clases de AD:*;$ Figura 1 303 Para resumir de alguna forma lo que estamos comentando, diremos que el trabajo de conexin con la base de datos, la ejecucin de una instruccin SQL determinada, una vista, etc., la realiza el proveedor de acceso a datos. Recuperar esos datos para tratarlos, manipularlos o volcarlos a un determinado control o dispositivo, es accin de la capa superior que corresponde con el nombre de espacio S%stem./ata. A continuacin veremos todo esto con ms detalle y comprenderemos de una forma ms clara cada una de las partes que componen el modelo de trabajo con ADO.NET.
78u0 nos permite realmente A"$.NET cuando trabaamos con G%'9 El entorno de Microsoft .NET Framework nos proporciona el trabajo con estndares y con ello, la posibilidad de trabajar con diferentes tipos de aplicaciones, entornos, sistemas operativos y lenguajes sin necesidad de conocer lo que hay al otro lado de nuestra aplicacin. XML es sin lugar a dudas, el lenguaje de etiquetas por excelencia, vlido para llevar a cabo esta tarea sin tener un impacto relevante cuando trabajamos con diferentes soluciones en entornos dispares. Tanto la posibilidad de trabajar con Servicios Web XML como con documentos e informacin en XML, sobre todo al trabajar con fuentes de datos en ADO.NET, nos proporciona a los desarrolladores las posibilidades necesarias que nos permite hacer que la informacin con la que trabajamos, pueda ser tratada entre diferentes sistemas o entornos, sin que por ello nos preocupemos de lo que hay al otro lado.
304 %(dulo B ? Cap!tulo @ 1. System."ata Las clases del nombre de espacio S%stem./ata son bastantes extensas y variadas. Quizs las clases ms importantes son la clase /ataVie, la clase /ataSet y la clase /ataTa#le.
'a clase "ataSet Puede que sea la capa ms externa e importante del modelo ADO.NET, pero es la que contiene quizs, el modelo de colecciones y trabajo que ms le puede resultar familiar al programador de Visual Basic 6, sobre todo a los anteriormente comentados 'ecordSets. El DataSet contiene en s, un conjunto de datos que han sido volcados desde el proveedor de datos. De esa forma, podemos trabajar con ellos como lo hacamos en Visual Basic 6 por ejemplo. De hecho, por marcar una similitud con el 'ecordSet, el DataSet est preparado para trabajar con fuentes de datos desconectadas. La novedad quizs ms destacable, es que cuando trabajamos con DataSets, el origen de datos no es lo ms importante, ya que ste, puede ser cualquier tipo de origen de datos. No tiene porqu ser una base de datos. Un DataSet contiene colecciones de DataTables y Data'elations. El DataTable contiene una tabla o tablas, mientras que la Data'elation contiene las relaciones entre las DataTables. Sin embargo, no es necesario especificar todo esto hasta el ltimo detalle como veremos ms adelante.
'a clase "ataVieF Este objeto nos permite crear mltiples vistas de nuestros datos, adems de permitirnos presentar los datos. Es la clase que nos permite representar los datos de la clase DataTable, permitindonos editar, ordenar y filtrar, buscar y navegar por un conjunto de datos determinado.
'a clase "ataTable Este objeto nos permite representar una determinada tabla en memoria, de modo que podamos interactuar con ella. A la hora de trabajar con este objeto, debemos tener en cuenta el nombre con el cul definamos una determinada tabla, ya que los objetos declarados en en el DataTable es sensitivo a maysculas y minsculas.
7#servacin3 )Ijese 9ue mientras 9ue en #isual Basic G utili.@bamos el 'ecordSet y 305 9ue :ste3 era la representaci<n en memoria de una ;nica tabla3 en *DO.$%T3 podemos representar una base de datos o un conjunto de tablas en memoria mediate un DataSet :n pe6ue+o eemplo pr&ctico El siguiente ejemplo prctico, nos ensea a utilizar un DataSet y nos muestra como podemos acceder a los objetos que dependen de un DataSet para recuperar por ejemplo, los campos y propiedades de una determinada tabla o tablas. Im2orts S-stem/Data Im2orts S-stem/Data/SIlClient Im2orts S-stem/Lml ?!blic Class )orm1 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% Dim ConeGion As String = "ser5er=/[!i%=sa[2assJor%=$is!al,asic[%atabase=HSDA$i%eo" Dim Hi1abla As Data1able Dim HiCol!mna As DataCol!mn Dim HiDataSet As AeJ DataSet() Dim Coman%o As AeJ SIlDataA%a2ter("S'C1 O )"0H A']FI'"S", ConeGion) Coman%o/)ill(HiDataSet, "A']FI'"S") 4 "ecorremos las tablas )or ach Hi1abla In HiDataSet/1ables 1eGt,oG1/1eGt B= "1ablaD " P Hi1abla/1ableAame P 5bCr'8 P 5bCr'8 4 "ecorremos las Col!mnas %e ca%a 1abla )or ach HiCol!mna In Hi1abla/Col!mns 1eGt,oG1/1eGt B= HiCol!mna/Col!mnAame P 5b1ab P + "(" P HiCol!mna/Data1-2e/Aame P ")" P 5bCr'8 AeGt AeGt 306 Coman%o = Aothing n% S!b n% Class Nuestro ejemplo en ejecucin es el que se muestra en la figura 1. jemplo en ejecucin del uso de Data!et8 Data$able y DataColumn Figura 1
307 %(dulo B ? Cap!tulo @ A. 'os proveedores de acceso a datos Los proveedores de acceso a datos es la capa inferior de la parte correspondiente al acceso de datos y es la responsable de establecer la comunicacin con las fuentes de datos. En este conjunto de nombres de espacio, encontraremos casi siempre las clases -onnection, -ommand, /ata4dapter y /ataBeader como las clases ms generales, las cuales nos permiten establecer la conexin con la fuente de datos.
,roveedores de acceso a datos de .NET <rameForI Dentro del entorno .NET Framework, encontramos un nutrido conjunto de proveedores de acceso a datos. Estos son los siguientes: ODBC .$%T Data +roider OL% DB .$%T Data +roider Oracle Client .$%T Data +roider SEL Serer .$%T Data +roider Estos proveedores de acceso a datos incluidos en Microsoft .NET Framework, los podemos encontrar en los nombres de espacio: System.Data.Odbc System.Data.OleDb System.Data.OracleClient System.Data.S9lClient El proveedor ODBC .NET permite conectar nuestras aplicaciones a fuentes de datos a travs de ODBC. El programador de Visual Basic 6 est ampliamente familiarizado con este mtodo de acceso a datos, y nos permite acceder a un amplio conjunto de fuentes de datos. El proveedor OLE DB .NET permite conectar nuestras aplicaciones a fuentes de datos a travs de OLE DB. Al igual que en el caso anterior, el programador de Visual Basic 6 est ampliamente familiarizado con este mtodo de acceso a datos. El proveedor Oracle Client .NET es un proveedor de acceso a datos especialmente diseado para bases de datos Oracle. Por ltimo, el proveedor SQL Server .NET es un proveedor de acceso a datos nativo, que nos permite conectar nuestras aplicaciones a fuentes de datos Microsoft SQL Server 7 o posterior. Se trata de un proveedor especfico para bases de datos Microsoft SQL Server 7.0, Microsoft SQL Server 2000 y Microsoft SQL Server 2005. -onse(o3 Siempre 9ue pueda3 utilice para acceder a fuentes de datos3 un proeedor de acceso a datos natio. %sto le permitir@ aumentar considerablemente el rendimiento a la "ora de establecer la cone&i<n con una determinada fuente de datos 308 Los proveedores de acceso a datos que distribuye Microsoft en ADO.NET y algunos desarrollados por otras empresas o terceros, contienen los mismos objetos, aunque los nombres de stos, sus propiedades y mtodos, pueden ser diferentes. Ms adelante veremos algn ejemplo, y observar en la prctica cules son estas diferencias ms destacables.
$tros proveedores de acceso a datos Si bien el proveedor de acceso a datos es el mecanismo a travs del cul podemos establecer una comunicacin nativa con una determinada fuente de datos, y dado que Microsoft proporciona los proveedores de acceso a datos ms corrientes, es cierto que no los proporciona todos, si bien, con OLE DB y ODBC, podemos acceder a la inmensa totalidad de ellos. Sin embargo, hay muchos motores de bases de datos de igual importancia como Oracle, MySql, AS/400, etc. En estos casos, si queremos utilizar un proveedor de acceso a datos nativo, deberemos acudir al fabricante o a empresas o iniciativas particulares para que nos proporcionen el conjunto de clases necesarias que nos permitan abordar esta accin.
El obeto Connection Este objeto es el encargado de establecer una conexin fsica con una base de datos determinada. Para establecer la conexin con una determinada fuente de datos, no slo debemos establecer la cadena de conexin correctamente, sino que adems deberemos usar los parmetros de conexin y el proveedor de acceso a datos adecuado. Con este objeto, podremos adems abrir y cerrar una conexin.
El obeto Command Este objeto es el que representa una determinada sentencia SQL o un Stored Procedure. Aunque no es obligatorio su uso, en caso de necesitarlo, lo utilizaremos conjuntamente con el objeto Data*dapter que es el encargado de ejecutar la instruccin indicada.
El obeto "ataAdapter Este objeto es quizs el objeto ms complejo y a la vez complicado de todos los que forman parte de un proveedor de acceso a datos en .NET. Cuando deseamos establecer una comunicacin entre una fuente de datos y un DataSet, utilizamos como intermediario a un objeto Data*dapter. A su vez, un Data*dapter contiene 4 objetos que debemos conocer: SelectCommand es el objeto encargado de realizar los trabajos de seleccin de datos con una fuente de datos dada. En s, es el que se encarga de devolver y rellenar los datos de una fuente de datos a un DataSet. DeleteCommand es el objeto encardago de realizar las acciones de borrado de datos. InsertCommand es el objeto encardago de realizar las acciones de insercin de datos. 309 ,pdateCommand es el objeto encardago de realizar las acciones de actualizacin de datos. Los objetos DeleteCommand, InsertCommand y ,pdateCommand son los objetos que se utilizan para manipular y transmitir datos de una fuente de datos determinada, al contrario del objeto SelectCommand que tan slo interacta con la fuente de datos para recuperar una porcin o todos los datos indicados en el objeto Command anteriormente comentado.
El obeto "ataEeader Este objeto es el utilizado en una sla direccin de datos. Se trata de un objeto de acceso a datos muy rpido. Este objeto puede usar a su vez el objeto Command o el mtodo %&ecute'eader.
%(dulo B ? Cap!tulo @ B. El concepto "ata.inding Una palabra nueva para los desarrolladores y programadores de Visual Basic 6, es el concepto denominado DataBinding o DataBind. A continuacin, explicaremos en qu consiste este concepto.
El uso de "ata.ind El mtodo DataBind se utiliza para rellenar de datos un determinado control o clase. Muchos controles y clases, posee este mtodo al que le asignaremos un conjunto de datos para que se rellene con ellos, pudiendo despus interactuar con los datos de forma directa. 310 En s, un DataBinding es un enlace a datos que se encarga de rellenar de datos a un determinado control o clase. Como ejemplo de esto, veremos como rellenar un control Te;tBo; con un dato utilizando este mtodo. Iniciaremos una nueva aplicacin Windows y escribiremos el siguiente cdigo fuente: Im2orts S-stem/Data Im2orts S-stem/Data/SIlClient Im2orts S-stem/Lml ?!blic Class )orm1 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% Dim ConeGion As String = "ser5er=/[!i%=sa[2assJor%=$is!al,asic[%atabase=HSDA$i%eo" Dim HiDataSet As AeJ DataSet() Dim Coman%o As AeJ SIlDataA%a2ter("S'C1 1I1F'0 )"0H A']FI'"S, ?'ICF'AS <H" ?'ICF'AC0D,A""AS = C0D,A""AS AAD S0CI0AI) = 41111114", ConeGion) 4 "ellenamos el DataSet con el conteni%o 4 %e la sentencia S'C1 Coman%o/)ill(HiDataSet, "?'IS") 4 "ellenamos el control 1eGt,oG1 con el 4 %ato corres2on%iente a la 2rimera 8ila 4 %e la sentencia S'C1 e#ec!ta%a 1eGt,oG1/Data,in%ings/A%%("1eGt", HiDataSet, "?'IS/1I1F'0") Coman%o = Aothing n% S!b n% Class Nuestro ejemplo en ejecucin es el que se muestra en la figura 1. 311 jemplo en ejecucin del uso de Data#indin" Figura 1
%(dulo B ? Cap!tulo @ 3. $tras consideraciones Dentro de las conexiones a fuentes de datos, hay algunas partes de stas que permanecen a veces en el olvido y su importancia sin embargo, es bastante grande. 312 La accin ms pesada cuando realizamos un acceso a una fuente de datos, se encuentra en la conexin con la fuente de datos. Esa tarea, simple tarea, es la que ms recursos del sistema consume cuando accedemos a fuentes de datos. Esto lo debemos tener en cuenta, y por lo tanto, variante de esto que comentamos son las siguientes premisas: La conexin debe realizarse siempre que se pueda, con los proveedores de acceso a datos nativos, que por lo general salvo raras expceciones, sern ms rpidos que los accesos a fuentes de datos a travs de proveedores del tipo OLE DB y ODBC. La conexin con la fuente de datos (apertura de la conexin), debe realizarse lo ms tarde posible. Es recomendable definir todas las variables que podamos, antes de realizar la conexin. La conexin debe cerrarse lo antes posible, siempre y cuando no tengamos la necesidad de utilizar la conexin previamente abierta. Hay ms particularidades a tener en cuenta cuando trabajamos con fuentes de datos. El hecho de que con un DataSet podamos trabajar con datos desconectados, no significa que dentro de l, podamos abrir una tabla con una cantidad de registros enormes, y trabajemos sobre ella creyendo que esto nos beneficiar. Todo lo contrario. Aunque los desarrolladores de Visual Basic 6 ya saben que esta prctica penaliza seriamente el rendimiento de nuestras aplicaciones, conviene sealarlo nuevamente para que no haya ningn tipo de duda al respecto.
-ntroducci(n Ahora que ya tenemos un poco ms clara la estructura del modelo de acceso a datos ADO.NET, podemos adentrarnos en todo lo que rodea al acceso conectado de base de datos y la manipulacin y trabajo de datos conectados. A continuacin, encontrar el ndice detallado de este captulo. 313 %(dulo B ? Cap!tulo 1
1. El paradigma de la conexin 2. Conociendo el objeto DataReader 3. Un primer contacto con el objeto DataReader 4. Trabaja DataReader en un ambiente conectado realmente? 5. Usando DataSource con DataReader 6. Usando los componentes de acceso a datos de .NET
%(dulo B ? Cap!tulo 1 @. El paradigma de la cone;i(n Cuando abordamos un proyecto de acceso a fuentes de datos, siempre nos encontramos con una duda existencial. Debemos crear una conexin con la base de datos al principio de nuestra aplicacin y cerrarla cuando la aplicacin se cierre?, o debemos crear una conexin con la base de datos slo cuando vayamos a trabajar con la fuente de datos?. Y si estamos trabajando continuamente con una fuente de datos?, cmo penalizaran todas estas acciones?. Es difcil de asumir que accin tomar en cada caso, y es que dependiendo de lo que vayamos a realizar, a veces es ms efectiva una accin que otra, y en otras ocasiones, no est del todo claro, ya que no existe en s una regla clara que especifique qu accin tomar en un momento dado. 314 Lo que s est claro es que el modelo de datos de ADO.NET que hemos visto, quedara resumido en cuanto a la conectividad de la manera en la que se representa en la figura 1. &isin "eneral de AD:*;$ respecto a la conectividad con bases de datos Figura 1 El objeto DataSet nos ofrece la posibilidad de almacenar datos, tablas y bases de datos de una determinada fuente de datos. De esta manera, podemos trabajar con las aplicaciones estando desconectados de la fuente de datos. Sin embargo, a veces necesitamos trabajar con la fuente de datos estando conectados a ella. El objeto Data'eader nos ofrece precisamente la posibilidad de trabajar con fuentes de datos conectadas. Por otro lado, el objeto Data'eader tiene algunas particularidades que conviene conocer y que veremos a continuacin. 315
%(dulo B ? Cap!tulo 1 1. Conociendo el obeto "ataEeader El objeto Data'eader nos permite como hemos indicado anteriormente, establecer una conexin con una fuente de datos y trabajar con esta fuente de datos sin desconectarnos de ella, sin embargo, hay diferentes cualidades y particularidades que conviene conocer.
"ataEeader es de s(lo lectura Lo que hemos dicho anteriormente, requiere sin embargo, que esta conexin se establezca en un modo de s<lo lectura, al contrario de lo que se puede hacer con el objeto DataSet, con el que podemos interactuar con la fuente de datos en modo lectura y modo escritura.
"ataEeader se manea en una s(la direcci(n El objeto Data'eader slo permite que nos desplacemos por los datos en una sla direccin, sin vuelta atrs. Por el contrario, el objeto DataSet nos permite movernos por los registros para adelante y para atrs. Adems, slo podemos utilizar el objeto Data'eader con conexiones establecidas en una sentencia SQL por ejemplo, pero no podemos variar esta. Para hacerlo, debemos entonces modificar la conexin con el comando establecido.
"ataEeader es r&pido 316 Debido a su naturaleza y caractersticas, este objeto es bastante rpido a la hora de trabajar con datos. Como es lgico, consume adems menos memoria y recursos que un objeto DataSet por ejemplo. Sin embargo, dependiendo de las necesidades con las que nos encontremos, puede que este mtodo de acceso y trabajo no sea el ms idneo. El desarrollador Visual Basic 6, encontrar en este objeto, una gran similitud con el conocido y comentado 'ecordSet.
Anali*ando el fluo de trabao de "ataEeader Cuando trabajamos con fuentes de datos conectadas, trabajaremos con el objeto Data'eader. Para trabajar con este objeto, utilizaremos los objetos siguientes del proveedor de acceso a datos: Connection Command Data'eader Un resumen grfico de esto es lo que podemos ver en la figura 1. l flujo de conectividad de Data3eader Figura 1 317
%(dulo B ? Cap!tulo 1 A. :n primer contacto con el obeto "ataEeader A continuacin veremos un ejemplo simple, sencillo y que si usted est acostumbrado a utilizar Visual Basic 6 y ADO, le resultar especialmente familiar.
:n eemplo simple para entenderlo meor Tenga en cuenta que en el siguiente ejemplo nos conectaremos a Microsoft SQL Server y recorreremos los registros uno a uno en un ambiente conectado y volcaremos estos registros en un control Te;tBo;. Im2orts S-stem/Data Im2orts S-stem/Data/SIlClient Im2orts S-stem/Lml ?!blic Class )orm1 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% Dim ConeGion As String = "ser5er=/[!i%=sa[2assJor%=$is!al,asic[%atabase=HSDA$i%eo" 318 Dim HiConeGion As AeJ SIlConnection(ConeGion) Dim HiData"ea%er As SIlData"ea%er Dim Coman%o As AeJ SIlComman%("S'C1 1I1F'0 )"0H A']FI'"S, ?'ICF'AS <H" ?'ICF'AC0D,A""AS = C0D,A""AS AAD S0CI0AI) = 41111114", HiConeGion) HiConeGion/02en() HiData"ea%er = Coman%o/Gec!te"ea%er() <hile HiData"ea%er/"ea%() 1eGt,oG1/1eGt B= HiData"ea%er("1I1F'0") P 5bCr'8 n% <hile Coman%o = Aothing HiConeGion/Close() n% S!b n% Class El cdigo de ejemplo en ejecucin es el que se muestra en la figura 1. jemplo en ejecucin del uso simple de Data3eader Figura 1 Este es un ejemplo simple del uso de Data'eader. Sin embargo, el objeto Data'eader contiene un conjunto de propiedades y mtodos que nos proporcionan acciones determinadas. Por ejemplo, en el ejemplo anterior, hemos dado por hecho que la ejecucin de la instruccin Select nos devolver uno o ms valores, pero podramos tambin saber antes de manipular y trabajar con los posibles datos, si hay o no informacin. Esto lo conseguimos con el mtodo 7as'o!s. 319
%(dulo B ? Cap!tulo 1 B. 7Trabaa "ataEeader en un ambiente conectado realmente9 Pese a todo esto, que ocurre si trabajando en un ambiente conectado se desconecta el servidor de acceso a datos?. Imaginemos por un instante, que la conexin con SQL Server se establece correctamente y que en un momento dado se detiene el servicio del servidor de base de datos. Esto es lo que veremos en el siguiente ejemplo.
"esenc#unfando la fuente de datos usando "ataEeader Inicie un nuevo proyecto, inserte en el formulario de la aplicacin un control Te;tBo; y un control Button, y escriba el cdigo que se detalla a continuacin: Im2orts S-stem/Data Im2orts S-stem/Data/SIlClient Im2orts S-stem/Lml ?!blic Class )orm1 320 ?ri5ate ConeGion As String = "ser5er=/[!i%=sa[2assJor%=$is!al,asic[%atabase=HSDA$i%eo" ?ri5ate strS]' As String = "S'C1 1I1F'0 )"0H A']FI'"S, ?'ICF'AS <H" ?'ICF'AC0D,A""AS = C0D,A""AS AAD S0CI0AI) = 41111114" ?ri5ate HiConeGion As AeJ SIlConnection(ConeGion) ?ri5ate HiData"ea%er As SIlData"ea%er ?ri5ate Conta%or As 'ong = 0 ?ri5ate ?osicion As 'ong = 0 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 stablecemos la ConeGiSn con la base %e %atos stablecer+ConeGion(1r!e) 4 Si ha- %atos los mostramos, sino %eshabilitamos 4 la o2ciSn (botSn) 2ara recorrerlos I8 Aot HiData"ea%er/Has"oJs 1hen ,!tton1/nable% = )alse lse ,!tton1+ClicQ(sen%er, e) n% I8 n% S!b ?ri5ate S!b stablecer+ConeGion(,-$al bolAccion As ,oolean) Dim Coman%o As SIlComman% I8 bolAccion 1hen 4 1r!e =* stablecemos la coneGiSn Coman%o = AeJ SIlComman%(strS]', HiConeGion) 4 Abrimos la ConeGiSn HiConeGion/02en() 4 #ec!tamos la sentencia S]' HiData"ea%er = Coman%o/Gec!te"ea%er() 4 0btenemos la canti%a% %e registros obteni%os 321 Conta%or = HiData"ea%er/$isible)iel%Co!nt() B 1 lse 4 )alse =* )inali&amos la coneGiSn ,!tton1/nable% = )alse 4 Cerramos la ConeGiSn Coman%o = Aothing HiConeGion/Close() n% I8 n% S!b ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ 4 "ecorremos los registros - los mostramos ?osicion B= 1 HiData"ea%er/"ea%() 1eGt,oG1/1eGt = HiData"ea%er("1I1F'0") 4 Si hemos recorri%o el ob#eto 2or com2leto, 4 8inali&amos la ConeGiSn - %eshabilitamos el control 4 ,!tton I!e nos 2ermite rec!2erar los registros I8 ?osicion = Conta%or 1hen stablecer+ConeGion()alse) n% I8 n% S!b n% Class Suponiendo que tenemos en nuestra base de datos varios registros, ejecute la aplicacin. Si todo ha ido como se esperaba, observaremos que nuestra aplicacin tiene un aspecto como el que se muestra en la figura 1. 322 jemplo en ejecucin del uso de Data3eader en un ambiente conectado8 for4ando la descone5in de la fuente de datos Figura 1 En este punto, detenga el sevicio de SQL Server y pulse el botn Siguiente ??. Observar que la aplicacin sigue funcionando. En este punto se har la pregunta que todos nos hemos hecho, no es el objeto DataReader un objeto conectado?, cmo es posible que funcione si hemos detenido el servicio de SQL Server?. La respuesta es sencilla. El objeto Data'eader recupera un nutrido conjunto de valores llenando un pequeo buffer de datos e informacin. Si el nmero de registros que hay en el buffer se acaban, el objeto Data'eader regresar a la fuente de datos para recuperar ms registros. Si el servicio de SQL Server est detenido en ese momento o en su caso, la fuente de datos est parada, la aplicacin generar un error a la hora de leer el siguiente registro. En s, Data'eader es un objeto conectado, pero trabaja en bac6ground con un conjunto de datos, por lo que a veces nos puede resultar chocante su comportamiento como el ejemplo que comento. Pese a las dudas planetadas, si tiene profundos conocimientos sobre el objeto 'ecordSet, encontrar familiar estas caractersticas. 323
%(dulo B ? Cap!tulo 1 3. :sando "ataSource con "ataEeader Podemos usar el mtodo DataSource con el objeto Data'eader?.
"emostraci(n del uso de "ataSource con "ataEeader La respuesta es s, pero en ADO.NET 2.0, se ha incorporado un nuevo mtodo al objeto DataTable que le permite tener mayor independencia respecto al modo en el que nos conectemos y recuperemos datos de una fuente de datos. Recuerde que podemos recuperar datos en modo conectado Data'eader o en modo desconectado DataSet. Este mtodo que se ha incorporado a ADO.NET y que tiene por nombre *oad, nos permite cargar un Data'eader para volcarlo a continuacin dentro de un control como por ejemplo el control Data1rid#ie!. Lo mejor es que veamos como funciona esto con un ejemplo que nos ayude a comprender mejor la teora. Inserte en un formulario un control Data1rid#ie! y escriba el siguiente cdigo: Im2orts S-stem/Data Im2orts S-stem/Data/SIlClient Im2orts S-stem/Lml ?!blic Class )orm1 324 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 Declaramos las 5ariables a !tili&ar Dim ConeGion As String = "ser5er=/[!i%=sa[2assJor%=$is!al,asic[%atabase=HSDA$i%eo" Dim strS]' As String = "S'C1 1I1F'0 )"0H A']FI'"S, ?'ICF'AS <H" ?'ICF'AC0D,A""AS = C0D,A""AS AAD S0CI0AI) = 41111114" Dim HiConeGion As AeJ SIlConnection(ConeGion) Dim HiData"ea%er As SIlData"ea%er Dim HiData1able As AeJ Data1able Dim Coman%o As SIlComman% 4 stablecemos la ConeGiSn con la base %e %atos Coman%o = AeJ SIlComman%(strS]', HiConeGion) 4 Abrimos la ConeGiSn HiConeGion/02en() 4 #ec!tamos la sentencia S]' HiData"ea%er = Coman%o/Gec!te"ea%er() 4 Cargamos los res!lta%os en el ob#eto Data1able HiData1able/'oa%(HiData"ea%er, 'oa%02tion/05erJriteChanges) 4 $olcamos los %atos en el control Data;ri%$ieJ Data;ri%$ieJ1/DataSo!rce = HiData1able 4 Cerramos la ConeGiSn Coman%o = Aothing HiConeGion/Close() n% S!b n% Class Nuestro ejemplo en ejecucin es el que podemos ver en la figura 1. 325 jemplo en ejecucin del uso de Data3eader y Data!ource en un control Data<rid&ie7 Figura 1 Con todo y con esto, lo que realmente es curioso, es que hemos olvidado por un instante que el objeto Data'eader es un objeto de slo lectura que funciona en una nica direccin, hacia delante. Qu significa esto o como puede influir o como podemos aprovechar esta circunstancia en nuestros desarrollos?.
Carga segmentada de datos con "ataSource y "ataEeader Si recuperamos los datos de una fuente de datos con Data'eader y leemos algunos de sus datos y posteriormente, ejecutamos el mtodo DataSource, el resto de datos, aquellos datos que quedan en el Data'eader, sern los que se vuelquen en el control que definamos como destino de los datos. Imaginemos el ejemplo anterior, y el siguiente cdigo fuente. Im2orts S-stem/Data Im2orts S-stem/Data/SIlClient Im2orts S-stem/Lml ?!blic Class )orm1 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 Declaramos las 5ariables a !tili&ar 326 Dim ConeGion As String = "ser5er=/[!i%=sa[2assJor%=$is!al,asic[%atabase=HSDA$i%eo" Dim strS]' As String = "S'C1 1I1F'0 )"0H A']FI'"S, ?'ICF'AS <H" ?'ICF'AC0D,A""AS = C0D,A""AS AAD S0CI0AI) = 41111114" Dim HiConeGion As AeJ SIlConnection(ConeGion) Dim HiData"ea%er As SIlData"ea%er Dim HiData1able As AeJ Data1able Dim Coman%o As SIlComman% 4 stablecemos la ConeGiSn con la base %e %atos Coman%o = AeJ SIlComman%(strS]', HiConeGion) 4 Abrimos la ConeGiSn HiConeGion/02en() 4 #ec!tamos la sentencia S]' HiData"ea%er = Coman%o/Gec!te"ea%er() 4 'eemos el 2rimer registro - asK nos 2osicionamos 4 a 2artir %el seg!n%o %e ellos HiData"ea%er/"ea%() 4 Cargamos los res!lta%os en el ob#eto Data1able HiData1able/'oa%(HiData"ea%er, 'oa%02tion/05erJriteChanges) 4 $olcamos los %atos en el control Data;ri%$ieJ Data;ri%$ieJ1/DataSo!rce = HiData1able 4 Cerramos la ConeGiSn Coman%o = Aothing HiConeGion/Close() n% S!b n% Class En este caso, lo que ocurre como ya hemos comentado, es que los datos que se cargan son los que an no han sido leidos en el objeto Data'eader, por lo que se mostrarn todos los datos desde el ltimo ledo hasta llegar al final del objeto.
327
%(dulo B ? Cap!tulo 1 C. :sando los componentes de acceso a datos de .NET Los componentes del entorno .NET nos proporcionan las caractersticas necesarias para poder acceder a fuentes de datos de forma rpida y sencilla. Imaginemos por lo tanto, una situacin lo ms parecida o similar posible a la que podramos tener con un programa Visual Basic 6 y un 'ecordSet. El mejor ejemplo de esto que comento es el que veremos a continuacin. "emostraci(n del uso de .indingSource y .indingNavigator Para ello, crearemos un proyecto nuevo e insertaremos un control BindingSource y un control Binding$aigator dentro del formulario. Tambin insertaremos un control Te&tBo& al formulario, dnde presentaremos la informacin sobre la que navegaremos. Nuestro formulario con los controles insertados en l, tendr un aspecto similar al que se presenta en la figura 1. 328 Controles de nave"acin y acceso a datos dispuestos en el formulario Figura 1 Una vez llegado a este punto, lo que tendremos que hacer a continuacin ser escribir el cdigo fuente necesario para poder representar los datos de la sentencia SQL en el control Bingind$aigator, para que a su vez los presente en el control Te&tBo&. A continuacin se indica el cdigo fuente de esta parte de demostracin de la aplicacin. Im2orts S-stem/Data Im2orts S-stem/Data/SIlClient Im2orts S-stem/Lml ?!blic Class )orm1 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 Declaramos las 5ariables a !tili&ar 329 Dim ConeGion As String = "ser5er=/[!i%=sa[2assJor%=$is!al,asic[%atabase=HSDA$i%eo" Dim strS]' As String = "S'C1 1I1F'0 )"0H A']FI'"S, ?'ICF'AS <H" ?'ICF'AC0D,A""AS = C0D,A""AS AAD S0CI0AI) = 41111114" Dim HiConeGion As AeJ SIlConnection(ConeGion) Dim HiData"ea%er As SIlData"ea%er Dim HiData1able As AeJ Data1able Dim Coman%o As SIlComman% 4 stablecemos la ConeGiSn con la base %e %atos Coman%o = AeJ SIlComman%(strS]', HiConeGion) 4 Abrimos la ConeGiSn HiConeGion/02en() 4 #ec!tamos la sentencia S]' HiData"ea%er = Coman%o/Gec!te"ea%er() 4 Cargamos los res!lta%os en el ob#eto Data1able HiData1able/'oa%(HiData"ea%er, 'oa%02tion/05erJriteChanges) 4 $olcamos los %atos en el control 1eGt,oG ,in%ingSo!rce1/DataSo!rce = HiData1able ,in%ingAa5igator1/,in%ingSo!rce = ,in%ingSo!rce1 1eGt,oG1/Data,in%ings/A%%(AeJ ,in%ing("1eGt", ,in%ingSo!rce1, "1I1F'0", 1r!e)) 4 Cerramos la ConeGiSn Coman%o = Aothing HiConeGion/Close() n% S!b n% Class
330
-ntroducci(n Ya tenemos claro el funcionamiento con fuentes de datos conectadas, sin embargo, trabajar con datos conectados slo es necesario en algunos mbitos, lo ms habitual, ser que nos encontremos trabajando con ambientes y accesos a datos desconectados, como ocurrir en la inmensa mayora de la veces. A continuacin, aprender a utilizar el DataSet y DataAdapter para sacar el mximo provecho a un ambiente desconectado de datos. El ndice detallado de este captulo es el que se indica a continuacin. %(dulo B ? Cap!tulo A
1. Esquema general de la estructura desconectada de acceso a datos 2. Conociendo el objeto DataAdapter 3. Insertando datos a travs del objeto DataAdapter 4. Actualizando datos a travs del objeto DataAdapter 5. Eliminando datos a travs del objeto DataAdapter
331
%(dulo B ? Cap!tulo A @. Es6uema general de la estructura desconectada de acceso a datos En los captulos anteriores de este mdulo, hemos visto ya el uso de la clase DataSet. Incluso lo hemos visto con algn ejemplo. La clase DataSet est pensada y diseada para trabajar con fuentes de datos desconectadas. Indudablemente, en este punto, debemos tener clara la estructura general de cmo funciona el acceso desconectado con fuentes de datos. En la figura 1, podemos observar el diagrama general de esta parte structura "eneral del uso de Data!et en el acceso desconectado a datos Figura 1 332
Connection/ "ataAdapter y "ataSet Como podemos observar en la figura 1, para comunicarnos con una fuente de datos, siempre deberemos establecer una conexin, independientemente de si la conexin con la fuente de datos va a permanecer a lo largo del tiempo o no. El objeto Connection nos permite por lo tanto, establecer la conexin con la fuente de datos. El objeto DataSet nos permite por otro lado, recoger los datos de la fuente de datos y mandrselos a la aplicacin. Entre medias de estos dos objetos, encontramos el objeto Data*dapter que hace las funciones de puente o nexo de unin entre la conexin y el objeto DataSet. Esto es lo que veremos a continuacin, como funciona el objeto Data*dapter, y como encaja todo esto en el acceso a fuentes de datos desconectadas.
%(dulo B ? Cap!tulo A 1. Conociendo el obeto "ataAdapter El objeto Data*dapter forma parte del proveedor de acceso a datos, tal y como se muestra en la figura 1. 333 &isin "eneral de las clases de AD:*;$ Figura 1 Cada proveedor de acceso a datos posee su propio objeto Data*dapter. Cuando realizamos alguna modificacin o accin sobre la fuente de datos, utilizaremos siempre el objeto Data*dapter a caballo entre el objeto DataSet y la fuente de datos establecida a travs de la conexin con el objeto Connection. Con el objeto Data*dapter, podremos adems realizar diferentes acciones sobre nuestras bases de datos, acciones como la ejecucin general de sentencias de SQL no slo para seleccionar un conjunto de datos, sino para alterar el contenido de una base de datos o de sus tablas.
Connection/ "ataAdapter y "ataSet Antes de entrar en materia ms profundamente, diremos que en lo que respecta a los proveedores de acceso a datos que vienen integrados con .NET, encontramos dos formas de usar un Data*dapter. La primera de ellas es utilizando los componentes del proveedor de acceso a datos. La segunda de ellas es utilizando las clases del nombre de espacio del proveedor de acceso a datos. La mejor forma de entender todo esto que comentamos, es trabajando con un ejemplo prctico que nos ensee a usar el objeto Data*dapter en Visual Basic 2005 Express.
:tili*ando las clases de .NET En este primer ejemplo de demostracin del uso de Data*dapter a travs de cdigo usando para ello las clases de .NET, estableceremos una conexin con SQL Server y mostraremos los datos recogidos en un control Te&tBo&. Iniciaremos Visual Basic 2005 Express y seleccionaremos un proyecto de formulario de Windows. Dentro del formulario, insertaremos un control Te&tBo& y aadiremos dos referencias al proyecto. Las referencias aadidas sern a las libreras System.Data y System.J(L, como se muestra en la figura 2. 334 3eferencias a las clases de acceso a datos de *;$ Figura 2 Una vez que hemos aadido las referencias necesarias para utilizar las clases que queremos en nuestro proyecto, iremos al cdigo y escribiremos las siguientes instrucciones: Im2orts S-stem/Data Im2orts S-stem/Data/SIlClient ?!blic Class )orm1 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 stablecemos la ca%ena %e coneGiSn con la ,,DD Dim ConeGion As String = "ser5er=/[!i%=sa[2assJor%=$is!al,asic[%atabase=HSDA$i%eo" 4 Declaramos el DataSet %Sn%e 5olcaremos los %atos Dim HiDataSet As AeJ DataSet() 4 Declaramos el DataA%a2ter establecien%o 4 la coneGiSn con la 8!ente %e %atos 335 Dim Coman%o As AeJ SIlDataA%a2ter("S'C1 SocioAI), )echaAlI!iler )"0H A']FI'"S", ConeGion) 4 "ellenamos el DataSet con el conteni%o %e la instr!cciSn S]' Coman%o/)ill(HiDataSet) 4 Cerramos la coneGiSn con la ,,DD Coman%o = Aothing 4 Declaramos la 2ro2ie%a% "oJ 2ara recorrer 4 las 8ilas conteni%as en el DataSet Dim "oJ 4 "ecorremos to%as las 8ilas - las tratamos )or ach "oJ In HiDataSet/1ables(0)/"oJs 1eGt,oG1/1eGt B= "oJ("SocioAI)")/1oString P 5b1ab P "oJ(")echaAlI!iler") P 5bCr'8 AeGt 4 $aciamos el DataSet 2ara liberar memoria HiDataSet = Aothing n% S!b n% Class El ejemplo en ejecucin del uso de Data*dapter junto con las clases de .NET es el que se muestra en la figura 3. jemplo del acceso a datos con DataAdapter a travs de las clases de *;$ Figura 3 336
:tili*ando los componentes de .NET Sin embargo y como ya hemos comentado, existe otro mtodo de acceso a fuentes de datos diferente a las clases de .NET, el acceso a travs de componentes que nos faciliten esa tarea. Sin embargo, los componentes de acceso a datos, utilizan por detrs las clases de .NET que hemos visto, lo que ocurre, es que simplifica enormemente el trabajo y ahorra tiempo a la hora de desarrollar aplicaciones. De todos los modos, todo depende de la utilidad o necesidades con las que nos encontremos en un momento dado. Iniciaremos un proyecto Windows nuevamente, e insertaremos en l un control Te&tBo& como hicimos en el caso anterior. A continuacin, aadiremos los componentes de acceso a fuentes de datos SQL Server que es la fuente de datos origen. Como hemos visto, para conectar a fuentes de datos SQL Server, hemos utilizado el nombre de espacio System.Data y hemos importado en el proyecto los nombres de espacio System.Data y System.Data.S9lClient. Los componentes .NET de acceso a fuentes de datos de SQL Server, se identifican por el nombre S9l&&&, siendo xxx el tipo de componente a utilizar. Para poder utilizarlos, deberemos aadirlo a la barra de herramientas. Para aadir los componentes a nuestro proyecto, haremos soble clic sobre el formulario y posteriormente haremos clic con el botn derecho del ratn sobre la barra de herramientas y seleccionaremos la opcin %legir elementos... como se muestra en la figura 4. :pcin de la barra de %erramientas para a2adir componentes al entorno Figura 4 337 Una vez que hemos hecho esto, seleccionaremos los componentes S9lCommand, S9lCommandBuilder, S9lConnection, S9lData*dapter y S9lDataSource, tal y como se muestra en la figura 5. Componentes a a2adir al entorno Figura 5 Una vez que hemos aadido los componentes al entorno, estos quedarn dispuestos dentro de la barra de herramientas como se indica en la figura 6. 338 Componentes a2adidos en la barra de %erramientas Figura 6 Lo primero que haremos ser insertar un componente S9lConnection dentro del formulario. Acudiremos a la ventana de propiedades del componente y modificaremos la propiedad "onnection.trin' dentro de la cul escribiremos la instruccin: sererS.TuidSsaTpass!ordS#isualBasicTdatabaseS(SD$#ideo entendiendo que sta, es la cadena de conexin vlida con nuestra base de datos. A continuacin aadiremos el componente S9lData*dapter a nuestro formulario. Si se abre alguna ventana, cirrela. Vamos a configurar el control con la ventana +ropiedades. Podramos haberlo hecho desde el asistente que se nos ha abierto, pero lo vamos a hacer de otra forma menos sencilla. Sitese sobre la propiedad SelectCommand, y dentro de sta, en la propiedad Connection. Lo que vamos a hacer, es asignar al componente S9lData*dapter el componente de conexin que vamos a usar para establecer la comunicacin entre la fuente de datos y nuestra aplicacin. Despliegue la propiedad Connection indicada, y seleccione el componente de conexin S9lConnectionH anteriormente configurado, tal y como se muestra en la figura 7. 339 Componente !qlDataAdapter con la cone5in establecida para ser usada en la ejecucin de nuestra aplicacin Figura 7 Por ltimo, inserte un componente DataSet al formulario. Todos los componentes quedarn por lo tanto insertados, tal y como se indica en la figura 8. 340 Componentes a2adidos en el formulario de nuestra aplicacin Figura 8 Una vez que tenemos todo preparado, tan slo nos queda escribir la parte de cdigo fuente necesario para poder realizar todas las operaciones y acciones que necesitamos. A continuacin, se expone el cdigo fuente de nuestra aplicacin de demostracin: ?!blic Class )orm1 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 stablecemos la ca%ena S]' a !tili&ar SIlDataA%a2ter1/SelectComman%/Comman%1eGt = "S'C1 SocioAI), )echaAlI!iler )"0H A']FI'"S" 4 Abrimos la ConeGiSn SIlConnection1/02en() 4 "ellenamos el DataSet con el conteni%o %e la instr!cciSn S]' 341 SIlDataA%a2ter1/)ill(DataSet1) 4 Cerramos la ConeGiSn SIlConnection1/Close() 4 Declaramos la 2ro2ie%a% "oJ 2ara recorrer 4 las 8ilas conteni%as en el DataSet Dim "oJ 4 "ecorremos to%as las 8ilas - las tratamos )or ach "oJ In DataSet1/1ables(0)/"oJs 1eGt,oG1/1eGt B= "oJ("SocioAI)")/1oString P 5b1ab P "oJ(")echaAlI!iler") P 5bCr'8 AeGt n% S!b n% Class Ahora nos queda nicamente ejecutar nuestra aplicacin para estudiar el resultado final. Este es el que se puede ver en la figura 9. jemplo en ejecucin del uso de componentes Figura 9 Pese a todo lo que hemos visto, quizs se pregunte como es que en el caso del primer ejemplo que hemos visto y en el que hemos declarado el uso de un Data*dapter sin usar componentes, hemos tenido la obligatoriedad de aadir las referencias a los nombres de espacio System.Data y System.Jml, mientras que en este segundo ejemplo, no hemos hecho referencia a ellos. En realidad nosotros no hemos hecho referencia a ellos, pero al insertar los componentes dentro del formulario, el entorno Visual Basic 2005 Express se ha 342 encargado por nosotros de aadir esas referencias al proyecto, tal y como puede verse en la figura 10. 3eferencias a2adidas autom>ticamente al trabajar con componentes de acceso a datos Figura 10
343 %(dulo B ? Cap!tulo A A. -nsertando datos a trav0s del obeto "ataAdapter Hasta ahora, todos los ejemplos que hemos visto del objeto Data*dapter, han sido ejemplos del uso de seleccin de datos, pero an no hemos visto como debemos trabajar cuando realicemos otras acciones sobre la fuente de datos. A continuacin, veremos como realizar acciones de actualizacin de datos, utilizando para ello el objeto Data*dapter. Becuerde3 %l DataSet permanece desconectado de la fuente de datos y si reali.amos una modificaci<n o alteraci<n de los datos de un DataSet3 estos no son propagados a la fuente de datos. +ara ello3 el Data*dapter debe recibir la orden 9ue 9ueramos ejecutar.
7C(mo se insertan datos con el obeto DataAdapter Suponiendo que hemos recogido un conjunto de datos y que trabajando con el objeto DataSet hemos realizado una insercin de datos y que a continuacin, queremos propagar dicha insercin a la base de datos, deberemos hacer uso del mtodo Insert del objeto Data*dapter. El objeto Data*dapter se encargar de llamar al comando apropiado para cada una de las filas que han sido modificadas en un determinado DataSet. Esto lo realizar siempre a travs del mtodo ,pdate.
Trabaando con un eemplo La mejor manera de ver esto es con un ejemplo que nos ayude a entender mejor como funciona la insercin de datos a travs del objeto Data*dapter. Tenga en cuenta adems, que la actualizacin y el borrado de datos funciona de la misma manera. Iniciaremos un nuevo proyecto de formulario Windows y en l insertamos los componentes S9lConnection, S9lData*dapter, DataSet y S9lCommand. Para el componente S9lConnection, estableceremos la propiedad ConnectionString con el valor: server=.;uid=sa;password=VisualBasic;database=MSDNVideo A continuacin seleccionaremos el componente S9lData*dapter y modificaremos la propiedad .elect"o))and > "onnection como vimos en el captulo anterior. De la lista de posibles conexiones que le aparezca, seleccione la conexin S9lConnectionH. Finalmente, inserte un control Button y un control Data1rid#ie! en el formulario. ste quedar como se indica en la figura 1. 344 (ormulario con los componentes y controles insertados en l Figura 1 Finalmente, escribiremos el cdigo necesario para ejecutar nuestra aplicacin tal y como queremos. Este es el que se detalla a continuacin: ?!blic Class )orm1 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 stablecemos la ca%ena S]' a !tili&ar SIlDataA%a2ter1/SelectComman%/Comman%1eGt = "S'C1 AI), Aombre, A2elli%o1, A2elli%o2, 1ele8ono, mail, Direccion, Ci!%a%, ?ro5incia, C? )"0H S0CI0S" 4 Abrimos la ConeGiSn SIlConnection1/02en() 4 "ellenamos el DataSet con el conteni%o %e la instr!cciSn S]' SIlDataA%a2ter1/)ill(DataSet1, "#em2lo") 4 Cerramos la ConeGiSn SIlConnection1/Close() 345 4 Asociamos el control Data;ri%$ieJ al DataSet Data;ri%$ieJ1/DataSo!rce = DataSet1/1ables("#em2lo") n% S!b ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ 4 Declaramos !n ob#eto Data"oJ 2ara 4 insertar en Rl los n!e5os %atos Dim HiData"oJ As Data/Data"oJ 4 Creamos !na n!e5a 8ila en el DataSet HiData"oJ = DataSet1/1ables("#em2lo")/AeJ"oJ() 4 Insertamos los %atos en el DataSet HiData"oJ("AI)") = "222222" HiData"oJ("Aombre") = "HarKa" HiData"oJ("A2elli%o1") = "@!6re&" HiData"oJ("A2elli%o2") = ")ern6n%e&" HiData"oJ("1ele8ono") = "1112233" HiData"oJ("mail") = "marKa\c!enta%email/com" HiData"oJ("Direccion") = "CU )ern6n%e& %e los "Kos, NN" HiData"oJ("Ci!%a%") = "$alla%oli%" HiData"oJ("?ro5incia") = "$alla%oli%" HiData"oJ("C?") = "11111" DataSet1/1ables("#em2lo")/"oJs/A%%(HiData"oJ) 4 Si el DataSet tiene cambios ^ I8 DataSet1/HasChanges 1hen 4 In%icamos la instr!cciSn S]' corres2on%iente SIlComman%1/Comman%1eGt = "IAS"1 IA10 S0CI0S(AI), Aombre, A2elli%o1, A2elli%o2, 1ele8ono, mail, Direccion, Ci!%a%, ?ro5incia, C?) $A'FS(\AI), \Aombre, \A2elli%o1, \A2elli%o2, \1ele8ono, \mail, \Direccion, \Ci!%a%, \?ro5incia, \C?)" 4 stablecemos 2ara el coman%o, 4 la (coneGiSn) I!e !tili&aremos SIlComman%1/Connection = SIlConnection1 346 4 'e in%icamos al DataA%a2ter, c!6l es el 4 coman%o %e inserciSn I!e !saremos SIlDataA%a2ter1/InsertComman% = SIlComman%1 4 ATa%imos los 2ar6metros - coman%os corres2on%ientes 4 2ara ca%a cam2o a aTa%ir en la base %e %atos SIlComman%1/?arameters/A%%("\AI)", Data/SIlDb1-2e/AChar, 10, "AI)") SIlComman%1/?arameters/A%%("\Aombre", Data/SIlDb1-2e/A$arChar, 50, "Aombre") SIlComman%1/?arameters/A%%("\A2elli%o1", Data/SIlDb1-2e/A$arChar, 50, "A2elli%o1") SIlComman%1/?arameters/A%%("\A2elli%o2", Data/SIlDb1-2e/A$arChar, 50, "A2elli%o2") SIlComman%1/?arameters/A%%("\1ele8ono", Data/SIlDb1-2e/A$arChar, 13, "1ele8ono") SIlComman%1/?arameters/A%%("\mail", Data/SIlDb1-2e/A$arChar, 50, "mail") SIlComman%1/?arameters/A%%("\Direccion", Data/SIlDb1-2e/A$arChar, 100, "Direccion") SIlComman%1/?arameters/A%%("\Ci!%a%", Data/SIlDb1-2e/A$arChar, 50, "Ci!%a%") SIlComman%1/?arameters/A%%("\?ro5incia", Data/SIlDb1-2e/A$arChar, 50, "?ro5incia") SIlComman%1/?arameters/A%%("\C?", Data/SIlDb1-2e/AChar, 5, "C?") 4 Abrimos la coneGiSn SIlConnection1/02en() 4 "eali&amos la inserciSn %e %atos %es%e el DataSet 4 a tra5Rs %el DataA%a2ter SIlDataA%a2ter1/F2%ate(DataSet1, "#em2lo") 4 Cerramos la coneGiSn SIlConnection1/Close() 4 In%icamos con !n mensa#e I!e la inserciSn 4 %e %atos se ha reali&a%o con RGito Hessage,oG/ShoJ("Datos inserta%os correctamente") n% I8 n% S!b 347 n% Class Por ltimo, ejecute la aplicacin. Si todo ha ido correctamente, los datos habrn quedado correctamente insertados en la base de datos. Un ejemplo de nuestra aplicacin en ejecucin es la que puede verse en la figura 2. Aplicacin de ejemplo de insercin de datos con DataAdapter y Data!et en ejecucin Figura 2 Como vemos, el uso de Data*dapter en el caso de manipular datos, vara ligeramente. Sin embargo, no es mucho ms complicado con la actualizacin y borrado de datos. De hecho, la forma de actuar es la misma como veremos a continuacin.
348
%(dulo B ? Cap!tulo A B. Actuali*ando datos a trav0s del obeto "ataAdapter De la misma manera que hemos insertado datos en nuestra base de datos, debemos hacer a la hora de actualizar los mismos. Sobre la base del ejemplo anterior (componentes y controles), escriba o modifique el siguiente cdigo: ?!blic Class )orm1 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 stablecemos la ca%ena S]' a !tili&ar SIlDataA%a2ter1/SelectComman%/Comman%1eGt = "S'C1 AI), Aombre, A2elli%o1, A2elli%o2, 1ele8ono, mail, Direccion, Ci!%a%, ?ro5incia, C? )"0H S0CI0S" 4 Abrimos la ConeGiSn SIlConnection1/02en() 4 "ellenamos el DataSet con el conteni%o %e la instr!cciSn S]' SIlDataA%a2ter1/)ill(DataSet1, "#em2lo") 4 Cerramos la ConeGiSn SIlConnection1/Close() 4 Asociamos el control Data;ri%$ieJ al DataSet Data;ri%$ieJ1/DataSo!rce = DataSet1/1ables("#em2lo") n% S!b ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ 4 n n!estro e#em2lo, sabemos I!e I!eremos mo%i8icar 4 la 8ila 2, col!mna . (to%os los elementos em2ie&an 2or 0) DataSet1/1ables("#em2lo")/"oJs(1)(.) = "111223." 4 Si el DataSet tiene cambios ^ I8 DataSet1/HasChanges 1hen 349 4 In%icamos la instr!cciSn S]' corres2on%iente SIlComman%1/Comman%1eGt = "F?DA1 S0CI0S S1 1ele8ono=\1ele8ono <H" AI)=\AI)" 4 stablecemos 2ara el coman%o, 4 la (coneGiSn) I!e !tili&aremos SIlComman%1/Connection = SIlConnection1 4 'e in%icamos al DataA%a2ter, c!6l es el 4 coman%o %e act!ali&aciSn I!e !saremos SIlDataA%a2ter1/F2%ateComman% = SIlComman%1 4 ATa%imos los 2ar6metros - coman%os corres2on%ientes 4 2ara ca%a cam2o a act!ali&ar en la base %e %atos SIlComman%1/?arameters/A%%("\AI)", Data/SIlDb1-2e/AChar, 10, "AI)") SIlComman%1/?arameters/A%%("\Aombre", Data/SIlDb1-2e/A$arChar, 50, "Aombre") SIlComman%1/?arameters/A%%("\A2elli%o1", Data/SIlDb1-2e/A$arChar, 50, "A2elli%o1") SIlComman%1/?arameters/A%%("\A2elli%o2", Data/SIlDb1-2e/A$arChar, 50, "A2elli%o2") SIlComman%1/?arameters/A%%("\1ele8ono", Data/SIlDb1-2e/A$arChar, 13, "1ele8ono") SIlComman%1/?arameters/A%%("\mail", Data/SIlDb1-2e/A$arChar, 50, "mail") SIlComman%1/?arameters/A%%("\Direccion", Data/SIlDb1-2e/A$arChar, 100, "Direccion") SIlComman%1/?arameters/A%%("\Ci!%a%", Data/SIlDb1-2e/A$arChar, 50, "Ci!%a%") SIlComman%1/?arameters/A%%("\?ro5incia", Data/SIlDb1-2e/A$arChar, 50, "?ro5incia") SIlComman%1/?arameters/A%%("\C?", Data/SIlDb1-2e/AChar, 5, "C?") 4 Abrimos la coneGiSn SIlConnection1/02en() 4 "eali&amos la act!ali&aciSn %e %atos %es%e 4 el DataSet a tra5Rs %el DataA%a2ter SIlDataA%a2ter1/F2%ate(DataSet1, "#em2lo") 4 Cerramos la coneGiSn 350 SIlConnection1/Close() 4 In%icamos con !n mensa#e I!e la act!ali&aciSn 4 %e %atos se ha reali&a%o con RGito Hessage,oG/ShoJ("Datos act!ali&a%os correctamente") n% I8 n% S!b n% Class Nuestro ejemplo en ejecucin es el que se puede ver en la figura 1. Aplicacin de ejemplo de actuali4acin de datos con DataAdapter y Data!et Figura 1
351
%(dulo B ? Cap!tulo A 3. Eliminando datos a trav0s del obeto "ataAdapter De igual forma sucede con la eliminacin de datos utilizando para ello el objeto Data*dapter junto al objeto DataSet. Utilizaremos nuevamente en este caso, la base del ejemplo anterior (componentes y controles), y escribiremos el siguiente cdigo: ?!blic Class )orm1 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 stablecemos la ca%ena S]' a !tili&ar SIlDataA%a2ter1/SelectComman%/Comman%1eGt = "S'C1 AI), Aombre, A2elli%o1, A2elli%o2, 1ele8ono, mail, Direccion, Ci!%a%, ?ro5incia, C? )"0H S0CI0S" 4 Abrimos la ConeGiSn SIlConnection1/02en() 4 "ellenamos el DataSet con el conteni%o %e la instr!cciSn S]' SIlDataA%a2ter1/)ill(DataSet1, "#em2lo") 4 Cerramos la ConeGiSn 352 SIlConnection1/Close() 4 Asociamos el control Data;ri%$ieJ al DataSet Data;ri%$ieJ1/DataSo!rce = DataSet1/1ables("#em2lo") n% S!b ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ 4 n n!estro e#em2lo, sabemos I!e I!eremos eliminar 4 la 8ila 2 (to%os los elementos em2ie&an 2or 0) 4 2or lo I!e la 8ila 2 es aI!K la 1 DataSet1/1ables("#em2lo")/"oJs(1)/Delete() 4 Si el DataSet tiene cambios ^ I8 DataSet1/HasChanges 1hen 4 In%icamos la instr!cciSn S]' corres2on%iente SIlComman%1/Comman%1eGt = "D'1 S0CI0S <H" AI)=\AI)" 4 stablecemos 2ara el coman%o, 4 la (coneGiSn) I!e !tili&aremos SIlComman%1/Connection = SIlConnection1 4 'e in%icamos al DataA%a2ter, c!6l es el 4 coman%o %e eliminaciSn I!e !saremos SIlDataA%a2ter1/DeleteComman% = SIlComman%1 4 ATa%imos los 2ar6metros - coman%os corres2on%ientes 4 2ara ca%a cam2o a act!ali&ar en la base %e %atos SIlComman%1/?arameters/A%%("\AI)", Data/SIlDb1-2e/AChar, 10, "AI)") SIlComman%1/?arameters/A%%("\Aombre", Data/SIlDb1-2e/A$arChar, 50, "Aombre") SIlComman%1/?arameters/A%%("\A2elli%o1", Data/SIlDb1-2e/A$arChar, 50, "A2elli%o1") SIlComman%1/?arameters/A%%("\A2elli%o2", Data/SIlDb1-2e/A$arChar, 50, "A2elli%o2") SIlComman%1/?arameters/A%%("\1ele8ono", Data/SIlDb1-2e/A$arChar, 13, "1ele8ono") 353 SIlComman%1/?arameters/A%%("\mail", Data/SIlDb1-2e/A$arChar, 50, "mail") SIlComman%1/?arameters/A%%("\Direccion", Data/SIlDb1-2e/A$arChar, 100, "Direccion") SIlComman%1/?arameters/A%%("\Ci!%a%", Data/SIlDb1-2e/A$arChar, 50, "Ci!%a%") SIlComman%1/?arameters/A%%("\?ro5incia", Data/SIlDb1-2e/A$arChar, 50, "?ro5incia") SIlComman%1/?arameters/A%%("\C?", Data/SIlDb1-2e/AChar, 5, "C?") 4 Abrimos la coneGiSn SIlConnection1/02en() 4 "eali&amos la eliminaciSn %e %atos %es%e 4 el DataSet a tra5Rs %el DataA%a2ter SIlDataA%a2ter1/F2%ate(DataSet1, "#em2lo") 4 Cerramos la coneGiSn SIlConnection1/Close() 4 In%icamos con !n mensa#e I!e la eliminaciSn 4 %e %atos se ha reali&a%o con RGito Hessage,oG/ShoJ("Datos elimina%os correctamente") n% I8 n% S!b n% Class Nuestro ejemplo en ejecucin es el que se puede ver en la figura 1. 354 Aplicacin de ejemplo de eliminacin de datos con DataAdapter y Data!et Figura 1
-ntroducci(n Otra particularidad de .NET a la hora de trabajar con fuentes de datos y con los componentes y controles que nos permiten acceder a ellas, es el trabajo con dos tipos de DataSets. Sin quererlo ya hemos visto como trabajar con uno de ellos, me refiero a los DataSets no tipados, sin embargo, hay otro tipo de DataSet diferente denominado as, DataSet tipado. 355 En qu consiste un DataSet tipado y como utilizarlos, es lo que vamos a ver a continuacin. %(dulo B ? Cap!tulo B
1. Qu son los DataSets tipados? 2. Generando nuestros DataSets tipados 3. Generando un DataSet tipado con Visual Basic 2005 Express 4. Generando un DataSet tipado con la lnea de comandos 5. Usando los DataSets tipados
%(dulo B ? Cap!tulo B @. 78u0 son los "ataSets tipados9 De forma genrica, podemos definir como DataSet tipado, a aquel DataSet que posee un esquema de datos, a diferencia del DataSet no tipado, que no necesita de ese esquema. Respecto a los DataSet tipados, diremos que en el entorno de desarrollo, encontramos muchas utilidades y herramientas que nos facilitan el trabajo de este tipo de DataSets. Inclusive, el propio SDK posee herramientas de comandos que nos permite y nos facilita la preparacin para trabajar con DataSets tipados. El esquema al que un DataSet tipado hace referencia, es un documento XML. Se trata de un documento XML con extensin .&sd. Trabajar con un esquema en lugar de trabajar con una tabla directamente, es mucho ms gil, fcil, rpido y seguro, como veremos ms adelante.
356 C(mo trabaar con un "ataSet tipado Evidentemente, lo que tenemos que tener claro y tomarlo as, como punto de partida, es que si queremos trabajar con un DataSet tipado, tenemos que crear su correspondiente esquema XML. Esto lo podemos hacer con una herramienta externa, manualmente, o con las herramientas que el entorno de desarrollo de Visual Basic 2005 Express o el propio SDK nos ofrece. En nuestro caso, ser estas ltimas acciones lo que usaremos. Obviamente, hacerlo manualmente es en mi opinin para fri6is, mxime cuando sabemos que tenemos herramientas que nos facilitan su creacin y nos ahorran mucho tiempo y recursos. Cuando tengamos ya creado el esquema XML, deberamos generar la clase del DataSet, algo que a estas alturas, ya lo tenemos dominado. En uno de los mtodos de creacin del esquema, el entorno hace ese trabajo por nosotros, en el otro, que es un proceso ms manual como veremos, deberamos crear esa clase con posterioridad, pero an as, esto ltimo no lo veremos con excesiva profundidad.
78u0 ventaas nos aportan los "ataSets tipados9 Ya hemos enumerado algunas de ellas, rapidez, seguridad, agilidad, fcilidad de trabajo, todo ello aplicable a los datos. An as, existen muchas ms razones para interesarnos por los DataSets tipados. A travs de los DataSets tipados, podemos realizar acciones que comunmente son costosas, de manera rpida y sencilla. Acciones como actualizacin de datos, modificacin, insercin o eliminacin de datos, o bsquedas de datos, son algunas de las acciones que en apenas un par de lneas de cdigo, estarn resueltas gracias al uso de DataSet tipados. Esto lo veremos ms adelante con los ejemplos de este captulo. Otras acciones adicionales vinculadas con los DataSets tipados son la posibilidad de filtrar datos, ordenar los datos, y trabajar con datos jerrquicos y relaciones de datos. Una diferencia notable entre los DataSets no tipados y los DataSets tipados, es que los primeros no saben ni tienen conocimiento alguno de lo que almacenan. Los segundos tienen cierta dosis de inteligencia y conocen el tipo de datos que almacena dentro. Adems de todo esto, el acceso a los datos que guarda un DataSet tipado, as como la manipulacin de los mismos, es mucho ms simple y requiere menos cdigo, haciendo que el acceso a los datos y el mantenimiento de la aplicacin sea ms cmoda y sencilla. 357
%(dulo B ? Cap!tulo B 1. 5enerando nuestros "ataSets tipados Dentro de Visual Basic 2005 Express y de .NET en general, tenemos varias formas de generar nuestros propios DataSets tipados. Una de las formas de generar DataSets tipados es utilizando el entorno de desarrollo rpido Visual Basic 2005 E;press. La otra es utilizando la herramienta LS/.e;e que encontraremos en el directorio SDK del entorno. Ambas formas, las veremos a continuacin.
"iagrama de datos Antes de continuar, repasaremos el diagrama de datos con el cul vamos a trabajar. Este es el que se muestra en la figura 1. 358 Dia"rama de datos con el que vamos a trabajar Figura 1 Este diagrama nos ayudar a interpretar los datos y a trabajar con ellos, en los ejemplos que veremos a continuacin. A continuacin, veremos como generar DataSets tipados desde el entorno de trabajo rpido, es decir #isual Basic 2BBC %&press, y desde la lnea de comandos.
359
%(dulo B ? Cap!tulo B A. 5enerando un "ataSet tipado con Visual .asic 1223 E;press La forma ms rpida para generar DataSets tipados es utilizando las herramientas automticas del entorno #isual Basic 2BBC %&press. A continuacin veremos como hacer esto de manera rpida y sencilla.
:sando el entorno de desarrollo r&pido Visual .asic 1223 E;press Cree un nuevo proyecto de *plicaci<n para 4indo!s y haga clic con el botn derecho del ratn sobre la ventana %&plorador de soluciones, y del men emergente, seleccione la opcin 4're'ar 6 Nuevo elemento..., tal y como se muestra en la figura 1. 360 Men? para a"re"ar un elemento al proyecto Figura 1 Aparecer entonces una ventana de *gregar nueo elemento, dentro de la cul seleccionaremos la plantilla de -on(unto de datos, tal y como se muestra en la figura 2. 361 &entana para a"re"ar un nuevo elemento al proyecto Figura 2 Haga clic en el botn 4're'ar. La plantilla del esquema del DataSet, tendr un aspecto similar al que se muestra en la figura 3. 362 squema por defecto del Data!et en &isual #asic ,++- 5press Figura 3 El siguiente paso que deberemos abordar, ser la de aadir al esquema, las tablas que queremos que formen parte del DataSet tipado. Para hacer esto, abra la ventana E;plorador de #ase de datos y seleccione la base de datos de prueba. Pulse sobre la tabla .ocios y arrstrela sobre el esquema del DataSet como se indica en la figura 4. 363 Arrastramos la tabla !ocios sobre el esquema del Data!et Figura 4 El esquema de la tabla quedar entonces aadido a la aplicacin, tal y como se indica en la figura 5. squema de la tabla !ocios a2adido al entorno de trabajo Figura 5 En este punto, tendremos listo nuestro DataSet tipado, pero an no tendremos una clase o cdigo asociado al DataSet tipado. An no lo hemos dicho, pero en este punto, #isual Basic 2BBC %&press ha generado para nosotros, el cdigo relacionado con el DataSet tipado creado. Este cdigo, est dentro del directorio de la aplicacin, y puede ser accedido a l pulsando el botn de la ventana E;plorador de soluciones y representado por el siguiente icono Esto es lo que se representa en la figura 6. :pcin de mostrar todos los arc%ivos Figura 6 364 En este punto, veremos que los archivos relacionados con nuestro proyecto y en el caso del elemento DataSetH.&sd, son los que se detallan en la figura 7. Arc%ivos del proyecto Figura 7 Observamos que el archivo DataSetH.Designer.b depende del archivo DataSetH.&sd, y es el que contiene el cdigo necesario para utilizar el DataSet tipado. De hecho, si vemos el cdigo generado por #isual Basic 2BBC %&press, observaremos que coincide con el que se detalla a continuacin: 4CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCC 4 (a!toCgenerate%* 4 Fna herramienta generS este cS%igo/ 4 $ersiSn %el motor en tiem2o %e e#ec!ciSnD2/0/50215/.. 4 4 'os cambios en este archi5o 2o%rKan ca!sar !n com2ortamiento incorrecto - se 2er%er6n si 4 el cS%igo se 5!el5e a generar/ 4 (Va!toCgenerate%* 4CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCC 365 02tion Strict 088 02tion G2licit 0n Im2orts S-stem (Seriali&able(), + S-stem/Com2onentHo%el/DesignerCategor-Attrib!te("co%e"), + S-stem/Com2onentHo%el/1oolboGItem(tr!e), +
?ri5ate )!nction Sho!l%Seriali&eSocios() As ,oolean "et!rn 8alse n% )!nction
?ri5ate S!b SchemaChange%(,-$al sen%er As 0b#ect, ,-$al e As S-stem/Com2onentHo%el/CollectionChange5entArgs) I8 (e/Action = S-stem/Com2onentHo%el/CollectionChangeAction/"emo5e) 1hen He/Init$ars n% I8 n% S!b
?!blic Share% )!nction ;et1-2e%DataSetSchema(,-$al Gs As S-stem/Lml/Schema/LmlSchemaSet) As S-stem/Lml/Schema/LmlSchemaCom2leG1-2e Dim %s As DataSet1 = AeJ DataSet1 Dim t-2e As S-stem/Lml/Schema/LmlSchemaCom2leG1-2e = AeJ S-stem/Lml/Schema/LmlSchemaCom2leG1-2e Dim seI!ence As S-stem/Lml/Schema/LmlSchemaSeI!ence = AeJ S-stem/Lml/Schema/LmlSchemaSeI!ence Gs/A%%(%s/;etSchemaSeriali&able) Dim an- As S-stem/Lml/Schema/LmlSchemaAn- = AeJ S-stem/Lml/Schema/LmlSchemaAn- an-/Aames2ace = %s/Aames2ace seI!ence/Items/A%%(an-) t-2e/?article = seI!ence "et!rn t-2e n% )!nction 372
?!blic Delegate S!b Socios"oJChange5entHan%ler(,-$al sen%er As 0b#ect, ,-$al e As Socios"oJChange5ent)
(S-stem/Seriali&able(), +
S-stem/Lml/Seriali&ation/LmlSchema?ro5i%erAttrib!te(";et1-2e%1ableSchema")* + ?artial ?!blic Class SociosData1able Inherits S-stem/Data/Data1able Im2lements S-stem/Collections/In!merable
?!blic ?ro2ert- AI)() As String ;et "et!rn C1-2e(He(He/tableSocios/AI)Col!mn),String) n% ;et Set He(He/tableSocios/AI)Col!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- Aombre() As String ;et 1r- "et!rn C1-2e(He(He/tableSocios/AombreCol!mn),String) Catch e As S-stem/In5ali%CastGce2tion 383 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4Aombre4 %e la tabla 4Socios4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableSocios/AombreCol!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- A2elli%o1() As String ;et 1r- "et!rn C1-2e(He(He/tableSocios/A2elli%o1Col!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4A2elli%o14 %e la tabla 4Socios4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableSocios/A2elli%o1Col!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- A2elli%o2() As String ;et 1r- "et!rn C1-2e(He(He/tableSocios/A2elli%o2Col!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4A2elli%o24 %e la tabla 4Socios4 es D,A!ll/", e) n% 1r- n% ;et 384 Set He(He/tableSocios/A2elli%o2Col!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- 1ele8ono() As String ;et 1r- "et!rn C1-2e(He(He/tableSocios/1ele8onoCol!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 41ele8ono4 %e la tabla 4Socios4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableSocios/1ele8onoCol!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- mail() As String ;et 1r- "et!rn C1-2e(He(He/tableSocios/mailCol!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4mail4 %e la tabla 4Socios4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableSocios/mailCol!mn) = 5al!e n% Set n% ?ro2ert- 385
?!blic ?ro2ert- Direccion() As String ;et 1r- "et!rn C1-2e(He(He/tableSocios/DireccionCol!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4Direccion4 %e la tabla 4Socios4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableSocios/DireccionCol!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- Ci!%a%() As String ;et 1r- "et!rn C1-2e(He(He/tableSocios/Ci!%a%Col!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4Ci!%a%4 %e la tabla 4Socios4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableSocios/Ci!%a%Col!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- ?ro5incia() As String ;et 1r- 386 "et!rn C1-2e(He(He/tableSocios/?ro5inciaCol!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4?ro5incia4 %e la tabla 4Socios4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableSocios/?ro5inciaCol!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- C?() As String ;et 1r- "et!rn C1-2e(He(He/tableSocios/C?Col!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4C?4 %e la tabla 4Socios4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableSocios/C?Col!mn) = 5al!e n% Set n% ?ro2ert-
?!blic )!nction IsAombreA!ll() As ,oolean "et!rn He/IsA!ll(He/tableSocios/AombreCol!mn) n% )!nction
(S-stem/Com2onentHo%el/Data0b#ectHetho%Attrib!te(S-stem/Com2onentHo%el/Data 0b#ectHetho%1-2e/Insert, tr!e)* + ?!blic 05erloa%s 05erri%able )!nction Insert(,-$al AI) As String, ,-$al Aombre As String, ,-$al A2elli%o1 As String, ,-$al A2elli%o2 As String, ,-$al 1ele8ono As String, ,-$al mail As String, ,-$al Direccion As String, ,-$al Ci!%a% As String, ,-$al ?ro5incia As String, ,-$al C? As String) As Integer I8 (AI) Is Aothing) 1hen 1hroJ AeJ S-stem/Arg!mentA!llGce2tion("AI)") lse He/A%a2ter/InsertComman%/?arameters(0)/$al!e = C1-2e(AI),String) n% I8 I8 (Aombre Is Aothing) 1hen He/A%a2ter/InsertComman%/?arameters(1)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/InsertComman%/?arameters(1)/$al!e = C1-2e(Aombre,String) n% I8 I8 (A2elli%o1 Is Aothing) 1hen He/A%a2ter/InsertComman%/?arameters(2)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/InsertComman%/?arameters(2)/$al!e = C1-2e(A2elli%o1,String) n% I8 406 I8 (A2elli%o2 Is Aothing) 1hen He/A%a2ter/InsertComman%/?arameters(3)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/InsertComman%/?arameters(3)/$al!e = C1-2e(A2elli%o2,String) n% I8 I8 (1ele8ono Is Aothing) 1hen He/A%a2ter/InsertComman%/?arameters(.)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/InsertComman%/?arameters(.)/$al!e = C1-2e(1ele8ono,String) n% I8 I8 (mail Is Aothing) 1hen He/A%a2ter/InsertComman%/?arameters(5)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/InsertComman%/?arameters(5)/$al!e = C1-2e(mail,String) n% I8 I8 (Direccion Is Aothing) 1hen He/A%a2ter/InsertComman%/?arameters(6)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/InsertComman%/?arameters(6)/$al!e = C1-2e(Direccion,String) n% I8 I8 (Ci!%a% Is Aothing) 1hen He/A%a2ter/InsertComman%/?arameters(N)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/InsertComman%/?arameters(N)/$al!e = C1-2e(Ci!%a%,String) n% I8 I8 (?ro5incia Is Aothing) 1hen 407 He/A%a2ter/InsertComman%/?arameters(X)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/InsertComman%/?arameters(X)/$al!e = C1-2e(?ro5incia,String) n% I8 I8 (C? Is Aothing) 1hen He/A%a2ter/InsertComman%/?arameters(E)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/InsertComman%/?arameters(E)/$al!e = C1-2e(C?,String) n% I8 Dim 2re5io!sConnectionState As S-stem/Data/ConnectionState = He/A%a2ter/InsertComman%/Connection/State He/A%a2ter/InsertComman%/Connection/02en 1r- "et!rn He/A%a2ter/InsertComman%/Gec!teAon]!er- )inall- I8 (2re5io!sConnectionState = S-stem/Data/ConnectionState/Close%) 1hen He/A%a2ter/InsertComman%/Connection/Close n% I8 n% 1r- n% )!nction
(S-stem/Com2onentHo%el/Data0b#ectHetho%Attrib!te(S-stem/Com2onentHo%el/Data 0b#ectHetho%1-2e/F2%ate, tr!e)* + ?!blic 05erloa%s 05erri%able )!nction F2%ate( + ,-$al AI) As String, + ,-$al Aombre As String, + ,-$al A2elli%o1 As String, + ,-$al A2elli%o2 As String, + ,-$al 1ele8ono As String, + 408 ,-$al mail As String, + ,-$al Direccion As String, + ,-$al Ci!%a% As String, + ,-$al ?ro5incia As String, + ,-$al C? As String, + ,-$al 0riginal+AI) As String, + ,-$al 0riginal+Aombre As String, + ,-$al 0riginal+A2elli%o1 As String, + ,-$al 0riginal+A2elli%o2 As String, + ,-$al 0riginal+1ele8ono As String, + ,-$al 0riginal+mail As String, + ,-$al 0riginal+Direccion As String, + ,-$al 0riginal+Ci!%a% As String, + ,-$al 0riginal+?ro5incia As String, + ,-$al 0riginal+C? As String) As Integer I8 (AI) Is Aothing) 1hen 1hroJ AeJ S-stem/Arg!mentA!llGce2tion("AI)") lse He/A%a2ter/F2%ateComman%/?arameters(0)/$al!e = C1-2e(AI),String) n% I8 I8 (Aombre Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(1)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(1)/$al!e = C1-2e(Aombre,String) n% I8 I8 (A2elli%o1 Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(2)/$al!e = S-stem/D,A!ll/$al!e lse 409 He/A%a2ter/F2%ateComman%/?arameters(2)/$al!e = C1-2e(A2elli%o1,String) n% I8 I8 (A2elli%o2 Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(3)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(3)/$al!e = C1-2e(A2elli%o2,String) n% I8 I8 (1ele8ono Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(.)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(.)/$al!e = C1-2e(1ele8ono,String) n% I8 I8 (mail Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(5)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(5)/$al!e = C1-2e(mail,String) n% I8 I8 (Direccion Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(6)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(6)/$al!e = C1-2e(Direccion,String) n% I8 I8 (Ci!%a% Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(N)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(N)/$al!e = C1-2e(Ci!%a%,String) 410 n% I8 I8 (?ro5incia Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(X)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(X)/$al!e = C1-2e(?ro5incia,String) n% I8 I8 (C? Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(E)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(E)/$al!e = C1-2e(C?,String) n% I8 I8 (0riginal+AI) Is Aothing) 1hen 1hroJ AeJ S-stem/Arg!mentA!llGce2tion("0riginal+AI)") lse He/A%a2ter/F2%ateComman%/?arameters(10)/$al!e = C1-2e(0riginal+AI),String) n% I8 I8 (0riginal+Aombre Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(11)/$al!e = C1-2e(1,Integer) He/A%a2ter/F2%ateComman%/?arameters(12)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(11)/$al!e = C1-2e(0,Integer) He/A%a2ter/F2%ateComman%/?arameters(12)/$al!e = C1-2e(0riginal+Aombre,String) n% I8 I8 (0riginal+A2elli%o1 Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(13)/$al!e = C1-2e(1,Integer) He/A%a2ter/F2%ateComman%/?arameters(1.)/$al!e = S-stem/D,A!ll/$al!e 411 lse He/A%a2ter/F2%ateComman%/?arameters(13)/$al!e = C1-2e(0,Integer) He/A%a2ter/F2%ateComman%/?arameters(1.)/$al!e = C1-2e(0riginal+A2elli%o1,String) n% I8 I8 (0riginal+A2elli%o2 Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(15)/$al!e = C1-2e(1,Integer) He/A%a2ter/F2%ateComman%/?arameters(16)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(15)/$al!e = C1-2e(0,Integer) He/A%a2ter/F2%ateComman%/?arameters(16)/$al!e = C1-2e(0riginal+A2elli%o2,String) n% I8 I8 (0riginal+1ele8ono Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(1N)/$al!e = C1-2e(1,Integer) He/A%a2ter/F2%ateComman%/?arameters(1X)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(1N)/$al!e = C1-2e(0,Integer) He/A%a2ter/F2%ateComman%/?arameters(1X)/$al!e = C1-2e(0riginal+1ele8ono,String) n% I8 I8 (0riginal+mail Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(1E)/$al!e = C1-2e(1,Integer) He/A%a2ter/F2%ateComman%/?arameters(20)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(1E)/$al!e = C1-2e(0,Integer) He/A%a2ter/F2%ateComman%/?arameters(20)/$al!e = C1-2e(0riginal+mail,String) 412 n% I8 I8 (0riginal+Direccion Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(21)/$al!e = C1-2e(1,Integer) He/A%a2ter/F2%ateComman%/?arameters(22)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(21)/$al!e = C1-2e(0,Integer) He/A%a2ter/F2%ateComman%/?arameters(22)/$al!e = C1-2e(0riginal+Direccion,String) n% I8 I8 (0riginal+Ci!%a% Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(23)/$al!e = C1-2e(1,Integer) He/A%a2ter/F2%ateComman%/?arameters(2.)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(23)/$al!e = C1-2e(0,Integer) He/A%a2ter/F2%ateComman%/?arameters(2.)/$al!e = C1-2e(0riginal+Ci!%a%,String) n% I8 I8 (0riginal+?ro5incia Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(25)/$al!e = C1-2e(1,Integer) He/A%a2ter/F2%ateComman%/?arameters(26)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(25)/$al!e = C1-2e(0,Integer) He/A%a2ter/F2%ateComman%/?arameters(26)/$al!e = C1-2e(0riginal+?ro5incia,String) n% I8 I8 (0riginal+C? Is Aothing) 1hen He/A%a2ter/F2%ateComman%/?arameters(2N)/$al!e = C1-2e(1,Integer) 413 He/A%a2ter/F2%ateComman%/?arameters(2X)/$al!e = S-stem/D,A!ll/$al!e lse He/A%a2ter/F2%ateComman%/?arameters(2N)/$al!e = C1-2e(0,Integer) He/A%a2ter/F2%ateComman%/?arameters(2X)/$al!e = C1-2e(0riginal+C?,String) n% I8 Dim 2re5io!sConnectionState As S-stem/Data/ConnectionState = He/A%a2ter/F2%ateComman%/Connection/State He/A%a2ter/F2%ateComman%/Connection/02en 1r- "et!rn He/A%a2ter/F2%ateComman%/Gec!teAon]!er- )inall- I8 (2re5io!sConnectionState = S-stem/Data/ConnectionState/Close%) 1hen He/A%a2ter/F2%ateComman%/Connection/Close n% I8 n% 1r- n% )!nction n% Class n% Aames2ace Ms adelante veremos como usarlo, pero antes veremos otra forma de crear nuestro DataSet tipado. Esta que hemos visto, es la forma ms sencilla y habitual de crearlo, pero existe otra manera que es un procedimiento manual que a modo general, conviene que la conozcamos.
414
%(dulo B ? Cap!tulo B B. 5enerando un "ataSet tipado con la l!nea de comandos Otra manera de generar nuestros propios DataSets tipados es mediante la lnea de comandos de MS-DOS con el uso de la herramienta ;sd.e;e. A continuacin veremos como realizar esta tarea con este comando.
:sando la #erramienta GS".e;e XSD no es otra cosa que un fichero ejecutable, que encontraremos en el SDK de .NET y que nos permitir crear nuestros DataSet tipados de manera rpida y sencilla. Para llevar a cabo nuestra tarea, abra una ventana MS-DOS y sitese en el directorio en el cul tenemos instalado nuestro entorno #isual Basic 2BBC %&press, como se indica en la figura 1. 415 &entana de M!/D:! situada en el directorio de &isual #asic ,++- 5press Figura 1 Una vez situados sobre este directorio, nos moveremos al subdirectorio .D?@$2*0@in tal y como se indica en la figura 2. &entana de M!/D:! situada en el subdirectorio #in de &isual #asic ,++- 5press Figura 2 416 A continuacin, lo que deberemos hacer es el fichero &sd correspondiente. Para eso, deberemos hacerlo mediante un editor de textos o bien, desde una pequea aplicacin Windows que generaremos para tal propsito. En nuestro caso lo haremos con una aplicacin Windows, para lo cul, iniciaremos un nuevo proyecto de 4plicacin para Pindos. En el formulario Windows, insertaremos un control Button tal y como se muestra en la figura 3. Control #utton insertado en el formulario de "eneracin del esquema Figura 3 A continuacin, escribiremos el siguiente cdigo que lo que nos permitir, ser crear el fichero o documento XML de extensin &sd correspondiente, el cul contiene la informacin para generar el DataSet tipado. El cdigo de la aplicacin de generacin del esquema es el que se detalla a continuacin: Im2orts S-stem/Data Im2orts S-stem/Data/SIlClient Im2orts S-stem/Lml ?!blic Class )orm1 ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ 417 4 stablecemos los 2ar6metros %e la coneGiSn Dim HiSIlConnection As AeJ SIlConnection("ser5er=/[!i%=sa[2assJor%=$is!al,asic[%atabase=HSDA$i%eo") 4 In%icamos la sentencia S'C1 2ara la coneGiSn anterior Dim HiSIlDataA%a2ter As AeJ SIlDataA%a2ter("S'C1 10? 1 O )"0H S0CI0S", HiSIlConnection) 4 Declaramos !n ob#eto DataSet 2ara almacenar el conteni%o Dim HiDataSet As AeJ DataSet() 4 "ellenamos el DataSet HiSIlDataA%a2ter/)ill(HiDataSet, "S0CI0S") 4 Declaramos !n ob#eto Data1able Dim HiData1able As Data1able = HiDataSet/1ables("S0CI0S") 4 Com2letamos alg!nos 2ar6metros %e los cam2os %e la tabla HiData1able/Col!mns("AI)")/FniI!e = 1r!e HiData1able/Col!mns("Aombre")/HaG'ength = 50 HiData1able/Col!mns("A2elli%o1")/HaG'ength = 50 HiData1able/Col!mns("A2elli%o2")/HaG'ength = 50 HiData1able/Col!mns("1ele8ono")/HaG'ength = 13 HiData1able/Col!mns("mail")/HaG'ength = 50 HiData1able/Col!mns("Direccion")/HaG'ength = 100 HiData1able/Col!mns("Ci!%a%")/HaG'ength = 50 HiData1able/Col!mns("?ro5incia")/HaG'ength = 50 HiData1able/Col!mns("C?")/HaG'ength = 50 4 scribimos el esI!ema %e la tabla al %isco %!ro %el ?C HiDataSet/<riteLmlSchema("cDUHisI!ema/Gs%") 4 Cerramos la coneGiSn HiSIlConnection/Close() 4 Hostramos !n mensa#e %e esI!ema crea%o correctamente Hessage,oG/ShoJ("sI!ema crea%o correctamente") n% S!b n% Class 418 Una vez que hemos escrito el cdigo fuente necesario para generar el esquema, ejecutaremos nuestra aplicacin, obteniendo una ejecucin afirmativa como la que se indica en la figura 4. jecucin del pro"rama de "eneracin del esquema Figura 4 El cdigo de nuestro esquema, ser una vez que se ha creado, como se indica en el siguiente cdigo: (^Gml 5ersion="1/0" stan%alone="-es"^* (GsDschema i%="AeJDataSet" Gmlns="" GmlnsDGs="htt2D"VVJJJ/J3/orgV2001VLH'Schema" GmlnsDms%ata="!rnDschemasC microso8tCcomDGmlCms%ata"* (GsDelement name="AeJDataSet" ms%ataDIsDataSet="tr!e" ms%ataDFseC!rrent'ocale="tr!e"* (GsDcom2leG1-2e* (GsDchoice min0cc!rs="0" maG0cc!rs="!nbo!n%e%"* (GsDelement name="S0CI0S"* (GsDcom2leG1-2e* (GsDseI!ence* (GsDelement name="AI)" t-2e="GsDstring" min0cc!rs="0" V* (GsDelement name="Aombre" min0cc!rs="0"* (GsDsim2le1-2e* (GsDrestriction base="GsDstring"* 419 (GsDmaG'ength 5al!e="50" V* (VGsDrestriction* (VGsDsim2le1-2e* (VGsDelement* (GsDelement name="A2elli%o1" min0cc!rs="0"* (GsDsim2le1-2e* (GsDrestriction base="GsDstring"* (GsDmaG'ength 5al!e="50" V* (VGsDrestriction* (VGsDsim2le1-2e* (VGsDelement* (GsDelement name="A2elli%o2" min0cc!rs="0"* (GsDsim2le1-2e* (GsDrestriction base="GsDstring"* (GsDmaG'ength 5al!e="50" V* (VGsDrestriction* (VGsDsim2le1-2e* (VGsDelement* (GsDelement name="1ele8ono" min0cc!rs="0"* (GsDsim2le1-2e* (GsDrestriction base="GsDstring"* (GsDmaG'ength 5al!e="13" V* (VGsDrestriction* (VGsDsim2le1-2e* (VGsDelement* (GsDelement name="mail" min0cc!rs="0"* (GsDsim2le1-2e* (GsDrestriction base="GsDstring"* (GsDmaG'ength 5al!e="50" V* (VGsDrestriction* 420 (VGsDsim2le1-2e* (VGsDelement* (GsDelement name="Direccion" min0cc!rs="0"* (GsDsim2le1-2e* (GsDrestriction base="GsDstring"* (GsDmaG'ength 5al!e="100" V* (VGsDrestriction* (VGsDsim2le1-2e* (VGsDelement* (GsDelement name="Ci!%a%" min0cc!rs="0"* (GsDsim2le1-2e* (GsDrestriction base="GsDstring"* (GsDmaG'ength 5al!e="50" V* (VGsDrestriction* (VGsDsim2le1-2e* (VGsDelement* (GsDelement name="?ro5incia" min0cc!rs="0"* (GsDsim2le1-2e* (GsDrestriction base="GsDstring"* (GsDmaG'ength 5al!e="50" V* (VGsDrestriction* (VGsDsim2le1-2e* (VGsDelement* (GsDelement name="C?" min0cc!rs="0"* (GsDsim2le1-2e* (GsDrestriction base="GsDstring"* (GsDmaG'ength 5al!e="50" V* (VGsDrestriction* (VGsDsim2le1-2e* (VGsDelement* 421 (VGsDseI!ence* (VGsDcom2leG1-2e* (VGsDelement* (VGsDchoice* (VGsDcom2leG1-2e* (GsD!niI!e name="Constraint1"* (GsDselector G2ath="/"VVS0CI0S" V* (GsD8iel% G2ath="AI)" V* (VGsD!niI!e* (VGsDelement* (VGsDschema* Lo que haremos a continuacin, ser utilizar la herramienta &sd*e&e que est dentro del directorio .D?@$2*0@in. Con la herramienta &sd*e&e, generaremos la clase que podremos usar para el DataSet tipados. Una vez por lo tanto, que estamos situados en la lnea de comandos de MS-DOS, escribiremos la instruccin: ;sd c3TMiEs&uema.;sd 2d 2l3VB 2n3Mi/ataSet./ST%pedSocios 2o3c3T Esto lo haremos tal y como se indica en la figura 5. Comandos de creacin de la clase del esquema creado Figura 5 422 Si todo ha ido correctamente, la clase para usar el DataSet tipado, se habr creado como se indica en la figura 6. Clase de Data!et tipado creada con la aplicacin 5sd*e5e Figura 6 El cdigo de nuestra clase tendr un aspecto similar al que se indica en el siguiente cdigo fuente: 4CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCC 4 (a!toCgenerate%* 4 Fna herramienta generS este cS%igo/ 4 $ersiSn %el motor en tiem2o %e e#ec!ciSnD2/0/50215/.. 4 4 'os cambios en este archi5o 2o%rKan ca!sar !n com2ortamiento incorrecto - se 2er%er6n si 4 el cS%igo se 5!el5e a generar/ 4 (Va!toCgenerate%* 4CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCC 02tion Strict 088 423 02tion G2licit 0n Im2orts S-stem 4 4ste cS%igo 8!ente 8!e genera%o a!tom6ticamente 2or Gs%, $ersiSn=2/0/50215/../ 4 Aames2ace HiDataSet/DS1-2e%Socios
?ri5ate )!nction Sho!l%Seriali&eS0CI0S() As ,oolean "et!rn 8alse n% )!nction
?ri5ate S!b SchemaChange%(,-$al sen%er As 0b#ect, ,-$al e As S-stem/Com2onentHo%el/CollectionChange5entArgs) I8 (e/Action = S-stem/Com2onentHo%el/CollectionChangeAction/"emo5e) 1hen He/Init$ars n% I8 n% S!b
?!blic Share% )!nction ;et1-2e%DataSetSchema(,-$al Gs As S-stem/Lml/Schema/LmlSchemaSet) As S-stem/Lml/Schema/LmlSchemaCom2leG1-2e Dim %s As AeJDataSet = AeJ AeJDataSet Dim t-2e As S-stem/Lml/Schema/LmlSchemaCom2leG1-2e = AeJ S-stem/Lml/Schema/LmlSchemaCom2leG1-2e Dim seI!ence As S-stem/Lml/Schema/LmlSchemaSeI!ence = AeJ S-stem/Lml/Schema/LmlSchemaSeI!ence Gs/A%%(%s/;etSchemaSeriali&able) Dim an- As S-stem/Lml/Schema/LmlSchemaAn- = AeJ S-stem/Lml/Schema/LmlSchemaAn- 430 an-/Aames2ace = %s/Aames2ace seI!ence/Items/A%%(an-) t-2e/?article = seI!ence "et!rn t-2e n% )!nction
?!blic Delegate S!b S0CI0S"oJChange5entHan%ler(,-$al sen%er As 0b#ect, ,-$al e As S0CI0S"oJChange5ent)
(S-stem/Seriali&able(), +
S-stem/Lml/Seriali&ation/LmlSchema?ro5i%erAttrib!te(";et1-2e%1ableSchema")* + ?artial ?!blic Class S0CI0SData1able Inherits S-stem/Data/Data1able Im2lements S-stem/Collections/In!merable
?ri5ate col!mnAI) As S-stem/Data/DataCol!mn
?ri5ate col!mnAombre As S-stem/Data/DataCol!mn
?ri5ate col!mnA2elli%o1 As S-stem/Data/DataCol!mn
?ri5ate col!mnA2elli%o2 As S-stem/Data/DataCol!mn
?ri5ate col!mn1ele8ono As S-stem/Data/DataCol!mn
?ri5ate col!mnmail As S-stem/Data/DataCol!mn
?ri5ate col!mnDireccion As S-stem/Data/DataCol!mn
?ri5ate col!mnCi!%a% As S-stem/Data/DataCol!mn 431
?!blic De8a!lt "ea%0nl- ?ro2ert- Item(,-$al in%eG As Integer) As S0CI0S"oJ ;et "et!rn C1-2e(He/"oJs(in%eG),S0CI0S"oJ) n% ;et n% ?ro2ert-
?!blic 5ent S0CI0S"oJChange% As S0CI0S"oJChange5entHan%ler
?!blic 5ent S0CI0S"oJChanging As S0CI0S"oJChange5entHan%ler
?!blic 5ent S0CI0S"oJDelete% As S0CI0S"oJChange5entHan%ler
?!blic 5ent S0CI0S"oJDeleting As S0CI0S"oJChange5entHan%ler
?!blic 05erloa%s S!b A%%S0CI0S"oJ(,-$al roJ As S0CI0S"oJ) He/"oJs/A%%(roJ) 435 n% S!b
?!blic 05erloa%s )!nction A%%S0CI0S"oJ(,-$al AI) As String, ,-$al Aombre As String, ,-$al A2elli%o1 As String, ,-$al A2elli%o2 As String, ,-$al 1ele8ono As String, ,-$al mail As String, ,-$al Direccion As String, ,-$al Ci!%a% As String, ,-$al ?ro5incia As String, ,-$al C? As String) As S0CI0S"oJ Dim roJS0CI0S"oJ As S0CI0S"oJ = C1-2e(He/AeJ"oJ,S0CI0S"oJ) roJS0CI0S"oJ/ItemArra- = AeJ 0b#ect() =AI), Aombre, A2elli%o1, A2elli%o2, 1ele8ono, mail, Direccion, Ci!%a%, ?ro5incia, C?> He/"oJs/A%%(roJS0CI0S"oJ) "et!rn roJS0CI0S"oJ n% )!nction
?!blic ?ro2ert- AI)() As String ;et 1r- "et!rn C1-2e(He(He/tableS0CI0S/AI)Col!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4AI)4 %e la tabla 4S0CI0S4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableS0CI0S/AI)Col!mn) = 5al!e n% Set n% ?ro2ert-
441 ?!blic ?ro2ert- Aombre() As String ;et 1r- "et!rn C1-2e(He(He/tableS0CI0S/AombreCol!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4Aombre4 %e la tabla 4S0CI0S4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableS0CI0S/AombreCol!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- A2elli%o1() As String ;et 1r- "et!rn C1-2e(He(He/tableS0CI0S/A2elli%o1Col!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4A2elli%o14 %e la tabla 4S0CI0S4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableS0CI0S/A2elli%o1Col!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- A2elli%o2() As String ;et 1r- "et!rn C1-2e(He(He/tableS0CI0S/A2elli%o2Col!mn),String) 442 Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4A2elli%o24 %e la tabla 4S0CI0S4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableS0CI0S/A2elli%o2Col!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- 1ele8ono() As String ;et 1r- "et!rn C1-2e(He(He/tableS0CI0S/1ele8onoCol!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 41ele8ono4 %e la tabla 4S0CI0S4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableS0CI0S/1ele8onoCol!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- mail() As String ;et 1r- "et!rn C1-2e(He(He/tableS0CI0S/mailCol!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4mail4 %e la tabla 4S0CI0S4 es D,A!ll/", e) n% 1r- 443 n% ;et Set He(He/tableS0CI0S/mailCol!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- Direccion() As String ;et 1r- "et!rn C1-2e(He(He/tableS0CI0S/DireccionCol!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4Direccion4 %e la tabla 4S0CI0S4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableS0CI0S/DireccionCol!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- Ci!%a%() As String ;et 1r- "et!rn C1-2e(He(He/tableS0CI0S/Ci!%a%Col!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4Ci!%a%4 %e la tabla 4S0CI0S4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableS0CI0S/Ci!%a%Col!mn) = 5al!e n% Set 444 n% ?ro2ert-
?!blic ?ro2ert- ?ro5incia() As String ;et 1r- "et!rn C1-2e(He(He/tableS0CI0S/?ro5inciaCol!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4?ro5incia4 %e la tabla 4S0CI0S4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableS0CI0S/?ro5inciaCol!mn) = 5al!e n% Set n% ?ro2ert-
?!blic ?ro2ert- C?() As String ;et 1r- "et!rn C1-2e(He(He/tableS0CI0S/C?Col!mn),String) Catch e As S-stem/In5ali%CastGce2tion 1hroJ AeJ S-stem/Data/Strong1-2ingGce2tion("l 5alor %e la col!mna 4C?4 %e la tabla 4S0CI0S4 es D,A!ll/", e) n% 1r- n% ;et Set He(He/tableS0CI0S/C?Col!mn) = 5al!e n% Set n% ?ro2ert-
?!blic )!nction IsAI)A!ll() As ,oolean "et!rn He/IsA!ll(He/tableS0CI0S/AI)Col!mn) 445 n% )!nction
?!blic "ea%0nl- ?ro2ert- Action() As S-stem/Data/Data"oJAction ;et "et!rn He/e5entAction n% ;et n% ?ro2ert- n% Class n% Class n% Aames2ace En este punto, podemos incluir el archivo (i%s9uema.b a nuestro proyecto, o bien compilarlo desde la lnea de comandos o desde el entorno de desarrollo. Sin embargo, no realizaremos esa accin. Tan slo comentar que si quisiramos compilar desde la lnea de comandos el fichero con el compilador de Visual Basic, utilizaramos el fichero ejecutable v#c.e;e, que corresponde con las iniciales de #isual Basic compiler. Ahora que ya sabemos como generar nuestros DataSet tipados, aprenderemos a usarlos en nuestras aplicaciones.
449
%(dulo B ? Cap!tulo B 3. :sando los "ataSets tipados A continuacin veremos un ejemplo del uso de DataSets tipados utilizando #isual Basic 2BBC %&press. Para esto nos servir lo que ya hemos visto en la generacin de un DataSet tipado con #isual Basic 2BBC %&press. Recordemos que ya habamos aadido un elemento DataSetH.&sd a nuestro proyecto, tal y como se muestra en la figura 1. 450 Data!et.*5sd de ejemplo8 a2adido al formulario8 para utili4arlo como Data!et tipado Figura 1
:so r&pido de nuestro "ataSet tipado El uso de un DataSet tipado, no tiene muchas diferencias respecto a un DataSet no tipado. Este ltimo ya lo hemos visto, y el DataSet tipado lo veremos a continuacin de forma prctica. Este primer ejemplo, muestra de forma sencilla, como trabajar con el esquema que hemos creado y como hacerlo rpidamente a travs de nuestra aplicacin de prueba. El cdigo de ejemplo que nos sirve de toma de contacto, es el que se indica a continuacin: Im2orts s-stem/%ata Im2orts S-stem/Data/SIlClient Im2orts S-stem/Lml ?!blic Class )orm1 ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ 451 4 stablecemos la coneGiSn Dim HiSIlConnection As AeJ SIlConnection("ser5er=/[!i%=sa[2assJor%=$is!al,asic[%atabase=HSDA$i%eo") 4 Declaramos !n ob#eto DataA%a2ter - le in%icamos la coneGiSn Dim HiSIlDataA%a2ter As AeJ SIlDataA%a2ter("S'C1 O )"0H S0CI0S", HiSIlConnection) 4 Declaramos !n ob#eto DataSet con el esI!ema %el DataSet ti2a%o Dim HiDt1-2e% As AeJ DataSet1 4 "ellenamos el DataSet ti2a%o con la in8ormaciSn %e la tabla %el S'C1 HiSIlDataA%a2ter/)ill(HiDt1-2e%, "S0CI0S") 4 Declaramos !n ob#eto 2ara recorrer los %atos %el DataSet Dim HisDatos As DataSet1/Socios"oJ 4 "ecorremos los %atos %el DataSet ti2a%o - los mostramos )or ach HisDatos In HiDt1-2e%/Socios Hessage,oG/ShoJ(HisDatos/Aombre P " " P HisDatos/A2elli%o1 P " " P HisDatos/A2elli%o2) AeGt n% S!b n% Class Nuestro ejemplo en ejecucin, es el que se puede ver en la figura 2. jemplo en ejecucin del uso sencillo de Data!ets tipados Figura 2 452 A continuacin veremos otros tipos de ejecucin de DataSets tipados mucho ms complejos.
Atenci(n especial al Cuadro de #erramientas Cuando trabajamos con DataSets tipados como lo hemos hecho hasta ahora, habremos notado que entre otras cosas, tenemos las capacidades o posibilidades de trabajar con el DataSet como objetos. Obviamente, estos objetos estn incluidos en el Cuadro de "erramientas, tal y como puede verse en la figura 3. Componentes creados por el entorno para trabajar con Data!ets tipados Figura 3 Para trabajar con ellos, podemos arrastrarlos sobre el formulario como hacemos con cualquier control o componente.
:sando las #erramientas autom&ticas para trabaar con "ataSets tipados An as, vamos a arrastrar sobre el formulario, un componente DataSet como se indica en la figura 4. !eleccin de un control Data!et del Cuadro de %erramientas Figura 4 453 En este punto, recordemos que tenemos nuestro DataSet tipado o esquema ya creado y que para usar este esquema desde nuestro objeto DataSet, podemos utilizar las herramientas del entorno .NET. Cuando arrastramos el componente DataSet sobre el formulario, aparecer una ventana como que se muestra en la figura 5, y que nos permitir indicar si se trata de un DataSet tipado o un DataSet no tipado. &entana para a"re"ar un conjunto de datos Figura 5 Por defecto, aparecer seleccionada la opcin de Conjunto de datos con tipo y el DataSet o esquema que hemos creado. Pulsaremos el botn 4ceptar y de esta manera, nuestro objeto DataSet habr quedado insertado y preparado en el formulario, para utilizar el esquema del DataSet indicado. Para no complicarlo, he decidido renombrar el control DataSet como dtSet. El DataSet quedar insertado en nuestro entorno como se indica en la figura 6. 454 Control Data!et Cdt!etD insertado en el formulario de ejemplo Figura 6 Para usar el objeto DataSet insertado, deberemos acceder a l a travs de cdigo, de forma muy parecida a lo que lo hacamos anteriormente. Im2orts s-stem/%ata Im2orts S-stem/Data/SIlClient Im2orts S-stem/Lml ?!blic Class )orm1 ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ 4 stablecemos la coneGiSn Dim HiSIlConnection As AeJ SIlConnection("ser5er=/[!i%=sa[2assJor%=$is!al,asic[%atabase=HSDA$i%eo") 4 Declaramos !n ob#eto DataA%a2ter - le in%icamos la coneGiSn 455 Dim HiSIlDataA%a2ter As AeJ SIlDataA%a2ter("S'C1 O )"0H S0CI0S", HiSIlConnection) 4 "ellenamos el DataSet ti2a%o con la in8ormaciSn %e la tabla %el S'C1 HiSIlDataA%a2ter/)ill(%tSet, "S0CI0S") 4 Declaramos !n ob#eto 2ara recorrer los %atos %el DataSet Dim HisDatos As DataSet1/Socios"oJ 4 "ecorremos los %atos %el DataSet ti2a%o - los mostramos )or ach HisDatos In %tSet/Socios Hessage,oG/ShoJ(HisDatos/Aombre P " " P HisDatos/A2elli%o1 P " " P HisDatos/A2elli%o2) AeGt n% S!b n% Class El funcionamiento de nuestra aplicacin de ejemplo, es igual al que hemos visto en la figura 2.
:sando "ataAdapter con "ataSets tipados Escribiremos a continuacin una pequea aplicacin Windows como la hemos hecho anteriormente. Crearemos nuestro esquema como lo hemos hecho hasta ahora y aadiremos dos botones como se indica en la figura 7.DataSet como se indica en la figura 4. 456 #otones del ejemplo a2adidos a la ventana 6indo7s Figura 7 A continuacin aadiremos un DataSet al cul le asignaremos el nombre del DataSet tipado correspondiente al esquema creado, tal y como se indica en la figura 8. Data!et tipado del esquema asi"nado al objeto Data!et Figura 8 A este objeto DataSet, le he llamado dtSet. A continuacin, aadiremos un componente S9lData*dapter al formulario Windows. En este punto, aparecer una ventana como la que se muestra en la figura 9. 457 Asistente del !qlDataAdapter Figura 9 Elegiremos la conexin adecuada y pulsaremos el botn Si'uiente. A continuacin, el asistente nos mostrar una informacin como la que se indica en la figura 10. 458 :pcin para ele"ir el tipo de comando a utili4ar Figura 10 Elegiremos la primera de las opciones que es la que se indica en la figura 10, y pulsaremos nuevamente el botn Si'uiente. El asistente continuar presentndonos ventanas de informacin para configurar nuestro componente S9lData*dapter. Dentro de esta ventana y dentro de los datos que deseamos cargar, escribiremos la instruccin SQL S%L%CT U )'O( SOCIOS como se indica en la figura 11. 459 :pcin de "eneracin de las instrucciones !HL Figura 11 A continuacin, pulsaremos el botn Si'uiente. De esta manera, el asistente terminar de crear las instrucciones necesarias para trabajar con el componente S9lData*dapter. Si todo ha ido correctamente, aparecer una ventana como la que se indica en la figura 12. 460 3esultados de la confi"uracin del componente !qlDataAdapter Figura 12 Para concluir, haremos clic sobre el botn Finali@ar. Automticamente, en #isual Basic 2BBC %&press, aparecer aadido al formulario Windows el componente S9lConnection, como vemos en la figura 13. 461 Componentes a2adidos al formulario 6indo7s Figura 13 A continuacin, escribiremos el cdigo fuente de la aplicacin, que se encarga de recoger los datos de la base de datos, insertarlos en un DataSet, para modificarlos y actualizar las modificaciones en la base de datos. El cdigo fuente de la aplicacin, quedar como se detalla a continuacin: Im2orts s-stem/%ata Im2orts S-stem/Data/SIlClient Im2orts S-stem/Lml ?!blic Class )orm1 Dim HiSIlConnection As SIlConnection Dim HiSIlDataA%a2ter As SIlDataA%a2ter 462 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 "ellenamos el DataSet ti2a%o con la in8ormaciSn %e la tabla %el S'C1 SIlDataA%a2ter1/)ill(%tSet, "S0CI0S") n% S!b ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ 4 Declaramos !n ob#eto 2ara traba#ar con los %atos %el DataSet Dim HisDatos As DataSet1/Socios"oJ 4 Almacenamos en Rl, la in8ormaciSn %el DataSet 2ara 4 el AI) = "111111" HisDatos = %tSet/Socios/)in%,-AI)("111111") 4 Ho%i8icaremos el cam2o C? HisDatos/C? = "2X022" 4 Deshabilitamos como me%i%a %e seg!ri%a% el botSn ,!tton1/nable% = )alse n% S!b ?ri5ate S!b ,!tton2+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton2/ClicQ I8 %tSet/HasChanges 1hen 4 l DataSet tiene cambios 4 Declaramos !n ob#eto DataSet Dim %tSetHo%i8ica%o As DataSet 4 'e 2asamos los %atos mo%i8ica%os en el DataSet original %tSetHo%i8ica%o = %tSet/;etChanges 4 Act!ali&amos el DataSet I!e ha cambia%o a tra5Rs %el DataA%a2ter SIlDataA%a2ter1/F2%ate(%tSetHo%i8ica%o) 4 Ace2tamos los cambios en el DataSet 2ara 4 seg!ir traba#an%o con Rl 2or e#em2lo %tSet/Acce2tChanges() 463 4 Hostramos !n mensa#e en 2antalla in%ican%o I!e 4 se han mo%i8ica%o los %atos Hessage,oG/ShoJ("'os cambios %el DataSet han si%o" P $bCr'8 P "act!ali&a%os en la base %e %atos") 4 Deshabilitamos como me%i%a %e seg!ri%a% el botSn ,!tton2/nable% = )alse lse 4 l DataSet no tiene cambios Hessage,oG/ShoJ("Ao ha- cambios en el DataSet") n% I8 n% S!b n% Class Nuestro ejemplo en ejecucin, es el que puede verse en la figura 14. jemplo del DataAdapter y Data!et tipado en ejecucin Figura 14 Nota3 Cuando trabajamos con un componente Data*dapter, aparece un asistente. En el caso de elegir la opcin de generacin de sentencias SELECT, UPDATE, DELETE e INSERT, el asistente crear a partir de la SELECT, esas instrucciones por nosotros. 464 Dentro de este mismo ejemplo, aada otros dos botones como se muestra en la figura 15. jemplo del DataAdapter y Data!et tipado en ejecucin Figura 15 Estos botones nos servirn para aadir una fila nueva a nuestra base de datos y para eliminar una fila existente (en nuestro caso la nueva fila aadida). El cdigo fuente de nuestra aplicacin de ejemplo, es el que se detalla a continuacin: Im2orts s-stem/%ata Im2orts S-stem/Data/SIlClient Im2orts S-stem/Lml ?!blic Class )orm1 Dim HiSIlConnection As SIlConnection Dim HiSIlDataA%a2ter As SIlDataA%a2ter 465 ?ri5ate S!b )orm1+'oa%(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les H-,ase/'oa% 4 Deshabilitamos el botSn %e ,orrar registro, 4 2orI!e hasta I!e no se cree no lo borraremos ,!tton./nable% = )alse 4 "ellenamos el DataSet ti2a%o con la in8ormaciSn %e la tabla %el S'C1 SIlDataA%a2ter1/)ill(%tSet, "S0CI0S") n% S!b ?ri5ate S!b ,!tton1+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton1/ClicQ 4 Declaramos !n ob#eto 2ara traba#ar con los %atos %el DataSet Dim HisDatos As DataSet1/Socios"oJ 4 Almacenamos en Rl, la in8ormaciSn %el DataSet 2ara 4 el AI) = "111111" HisDatos = %tSet/Socios/)in%,-AI)("111111") 4 Ho%i8icaremos el cam2o C? HisDatos/C? = "2X022" 4 Deshabilitamos como me%i%a %e seg!ri%a% el botSn ,!tton1/nable% = )alse n% S!b ?ri5ate S!b ,!tton2+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton2/ClicQ I8 %tSet/HasChanges 1hen 4 l DataSet tiene cambios 4 Declaramos !n ob#eto DataSet Dim %tSetHo%i8ica%o As DataSet 4 'e 2asamos los %atos mo%i8ica%os en el DataSet original %tSetHo%i8ica%o = %tSet/;etChanges 4 Act!ali&amos el DataSet I!e ha cambia%o a tra5Rs %el DataA%a2ter SIlDataA%a2ter1/F2%ate(%tSetHo%i8ica%o) 466 4 Ace2tamos los cambios en el DataSet 2ara 4 seg!ir traba#an%o con Rl 2or e#em2lo %tSet/Acce2tChanges() 4 Hostramos !n mensa#e en 2antalla in%ican%o I!e 4 se han mo%i8ica%o los %atos Hessage,oG/ShoJ("'os cambios %el DataSet han si%o" P 5bCr'8 P "act!ali&a%os en la base %e %atos") 4 Deshabilitamos como me%i%a %e seg!ri%a% el botSn ,!tton2/nable% = )alse lse 4 l DataSet no tiene cambios Hessage,oG/ShoJ("Ao ha- cambios en el DataSet") n% I8 n% S!b ?ri5ate S!b ,!tton3+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton3/ClicQ 4 ATa%imos !na n!e5a 8ila con %atos al DataSet ti2a%o %tSet/Socios/A%%Socios"oJ("111112", "HarKa", "S6nche&", ""o%rKg!e&", "123123.", "maria\c!enta%ecorreo/com", "CU San )ernan%o, 13", ",arcelona", ",arcelona", "0X001") 4 Act!ali&amos los %atos a tra5Rs %el DataA%a2ter SIlDataA%a2ter1/F2%ate(%tSet) 4 Deshabilitamos el botSn %e aTa%ir %atos ,!tton3/nable% = )alse 4 Habilitamos el botSn %e eliminar %atos ,!tton./nable% = 1r!e 4 Hostramos !n mensa#e in%ican%o I!e la 4 acciSn se ha reali&a%o correctamente Hessage,oG/ShoJ(""egistro aTa%i%o correctamente") n% S!b 467 ?ri5ate S!b ,!tton.+ClicQ(,-$al sen%er As S-stem/0b#ect, ,-$al e As S-stem/5entArgs) Han%les ,!tton./ClicQ 4 ,orramos la 8ila aTa%i%a %tSet/Socios/)in%,-AI)("111112")/Delete() 4 Act!ali&amos los %atos a tra5Rs %el DataA%a2ter SIlDataA%a2ter1/F2%ate(%tSet) 4 Hostramos !n mensa#e in%ican%o I!e la 4 acciSn se ha reali&a%o correctamente Hessage,oG/ShoJ(""egistro elimina%o correctamente") 4 Deshabilitamos el botSn %e eliminar ,!tton./nable% = )alse n% S!b n% Class Nuestro ejemplo en ejecucin es el que se muestra en la figura 16. jemplo del uso de DataAdapter y Data!et tipado con todas las acciones de insercin y eliminacin de datos incluidas Figura 16 Como vemos, el uso del DataSet tipado, posee grandes ventajas sobre los DataSets no tipados, permitindonos trabajar con datos de forma muy rpida y sencilla. La parte ms compleja quizs, es la preparacin del esquema de datos, pero una vez realizada esta tarea, el trabajo con los datos es asombrosamente rpida y segura.
468
-ntroducci(n Visual Basic 2005 Express proporciona a los desarrolladores un conjunto de herramientas y asistentes que facilitan enormemente el trabajo y ahorran grandes cantidades de tiempo en el desarrollo de diferentes aplicaciones. De los asistentes o herramientas que nos proporcionan mayor rendimiento en el entorno de desarrollo rpido de Microsoft, est la que nos permite trabajar con fuentes de datos como los datos de una aplicacin tpica maestro detalle. Entre otras cosas, nos centraremos en esta accin, que ser la que veremos en detalle a continuacin. %(dulo B ? Cap!tulo 3
1. Que son los datos maestro detalle? 2. Configurando la fuente de datos 3. Preparando el origen de datos 4. Incrustando los datos maestro detalle 5. Manipulando los datos maestro detalle 469
-ntroducci(n Visual Basic 2005 Express proporciona a los desarrolladores un conjunto de herramientas y asistentes que facilitan enormemente el trabajo y ahorran grandes cantidades de tiempo en el desarrollo de diferentes aplicaciones. De los asistentes o herramientas que nos proporcionan mayor rendimiento en el entorno de desarrollo rpido de Microsoft, est la que nos permite trabajar con fuentes de datos como los datos de una aplicacin tpica maestro detalle. Entre otras cosas, nos centraremos en esta accin, que ser la que veremos en detalle a continuacin. %(dulo B ? Cap!tulo 3
1. Que son los datos maestro detalle? 2. Configurando la fuente de datos 3. Preparando el origen de datos 4. Incrustando los datos maestro detalle 5. Manipulando los datos maestro detalle
470
%(dulo B ? Cap!tulo 3 @. 78u0 son los datos %aestro "etalle9 El desarrollador de aplicaciones que debe trabajar con datos y fuentes de datos relacionadas entre s, encuentra con frecuencia problemas de desarrollo en aplicaciones con datos interrelacionados. Adems, este tipo de aplicaciones, consumen gran parte del tiempo de desarrollo y son por lo general, acciones repetitivas. Supongamos como ejemplo general, la tabla Socios de un videoclub. Adems, relacionemos los socios del videoclub, con una tabla *l9uileres, para saber si un socio determinado tiene pelculas alquiladas, y en ese caso, saber cules. Este sencillo ejemplo, es un claro exponente de una aplicacin que relaciona datos maestro detalle. Ambas tablas deben estar relacionadas para recopilar la informacin que se necesite en un momento dado. Los datos maestros seran expuestos por los socios del videoclub, mientras que los datos detalle estaran relacionados con los datos de los alquileres de los socios. En nuestro caso, vamos a cambiar las palabras maestro y detalle por padre e hijo, y a partir de ahora, nos referiremos a padre como la tabla Socios, e hijo como la tabla *l9uileres. De esta forma, ubicaremos sin problemas ambos conceptos dentro del entorno de #isual Basic 2BBC %&press, ya que ste tiene alguna ligera connotacin que podra infundirnos a error como observar ms adelante. Por suerte, #isual Basic 2BBC %&press nos proporciona un conjunto de herramientas, que hace que realizar una aplicacin Windows con todas las caractersticas de una aplicacin maestro detalle, sea un autntico juego de nios, que nos llevar aproximadamente un minuto de nuestro tiempo como mucho. No me cree?. Contine el captulo, y se sorprender de lo que #isual Basic 2BBC %&press puede hacer por usted. 471
%(dulo B ? Cap!tulo 3 1. Configurando la fuente de datos Trabajar con fuentes de datos requiere como tarea inicial, que tengamos listo y preparado un orgen de fuentes de datos vlido. Para esta tarea, deberemos configurar la fuente de datos que vamos a utilizar, algo que vamos a aprender a hacer a continuacin.
Configurando el origen de la fuente de datos Iniciaremos una nueva aplicacin Windows con #isual Basic 2BBC %&press y seleccionaremos el men /atos 6 Mostrar or,'enes de datos como se indica en la figura 1. Men? para mostrar los or@"enes de datos Figura 1 En este punto, aparecer una ventana como la que se muestra en la figura 2. 472 &entana de or@"enes de datos Figura 2 Como podemos apreciar, la ventana no tiene por defecto ningn origen de datos asignado, adems, esta ventana inicialmente, no est anclada a ningn sitio del entorno. Para situarla en un lugar especfico del entorno, haga clic sobre la ventana y arrstrela sobre la parte en la que desee situarla, como se indica por ejemplo, en la figura 3. 473 La ventana or@"enes de datos podemos situarla dnde deseemos dentro de &isual #asic ,++- 5press Figura 3 En este punto, la ventana de orgenes de datos, quedar anclada en el entorno de desarrollo, como se muestra en la figura 4. 474 La ventana or@"enes de datos anclada en &isual #asic ,++- 5press Figura 4 Cada uno, puede situar esta ventana dnde considere oportuno. En mi caso la he situado entre las ventanas del %&plorador de soluciones y de +ropiedades, pero podemos situarla dnde consideremos opotuno. Sobre la configuracin del orgen de datos, haga clic sobre la opcin *gregar nueo origen de datos... como se indica en la figura 5. :pcin para a"re"ar un nuevo ori"en de datos Figura 5 Aparecer una ventana como la que se muestra en la figura 6 en la cul indicaremos el lugar de dnde la aplicacin obtendr los datos, y que en nuestro caso ser de una Base de datos. 475 Como tipo de ori"en de datos ele"iremos una #ase de datos Figura 6 Una vez seleccionado la opcin de Base de datos como tipo de origen de datos, pulsaremos el botn Si'uiente. En la siguiente ventana, eligiremos la conexin de la base de datos que vamos a utilizar, o pulsaremos sobre el botn Nueva cone;in... sino tenemos seleccionada la conexin que queremos utilizar. En la figura 7, podemos ver como hemos establecido la conexin con nuestra fuente de datos, que utilizaremos en nuestro ejemplo de creacin de la aplicacin de acceso a datos maestro detalle. 476 &entana dnde ele"imos la cone5in de datos Figura 7 A continuacin, haremos clic en el botn Si'uiente. En la nueva ventana que aparece ahora dentro del asistente para la creacin del origen de datos, indicaremos el nombre de la cadena de conexin que crearemos. En nuestro caso, no modificaremos el nombre de la cadena de conexin, dejndola por defecto como se muestra en la figura 8. 477 Asi"namos un nombre para el nombre de la cadena de cone5in Figura 8 A continuacin, haremos clic sobre el botn Si'uiente. En este punto, el asistente se conecta a la base de datos para recuperar la informacin de la base de datos y mostrarla en la ventana del asistente como se muestra en la figura 9. 478 &entana con los objetos de la base de datos seleccionada Figura 9 A continuacin, despliegue el objeto Ta#las y seleccione las tablas *l9uileres y Socios como se indica en la figura 10. 479 !eleccin de los objetos de la base de datos seleccionada Figura 10 Una vez que hemos seleccionado los objetos de la base de datos, haremos clic sobre el botn Finali@ar para que el asistente concluya las opciones aadidas al asistente. La ventana del origen de datos, quedar ahora configurado de una forma similar a la que se presenta en la figura 11. &entana de ori"en de datos con la confi"uracin a2adida Figura 11 A partir de aqu, deberemos preparar las tablas del origen de datos para que se comporten como autnticos datos e informaciones maestro detalle. Esto es lo que veremos en el siguiente mdulo. 480
%(dulo B ? Cap!tulo 3 A. ,reparando el origen de datos Ya hemos aprendido a aadir nuestro origen de datos, y ahora aprenderemos a prepararlo para poder utilizarlo posteriormente en la aplicacin Windows. La preparacin del origen de datos, nos permitir seleccionar que tabla o datos queremos que acten como maestro y cuales como detalle, o dicho de otra forma, que datos queremos que sean padre y cuales hijo. Nuestro objetivo principal es mostrar la tabla *l9uileres como tabla hijo y la tabla Socios como padre de la tabla anterior. Prepararemos e incrustaremos primero la tabla Socios dentro del formulario Windows como Detalle de la informacin, y posteriormente insertaremos la tabla *l9uileres dentro del formulario. Por ltimo, asignaremos alguna relacin que permita trabajar con las dos tablas en nuestro formulario Windows sin perder la conexin entre ambas tablas y permitindonos acceder a la informacin que nos proporciona dicha relacin.
,reparando la tabla padre Lo primero que haremos ser preparar la tabla padre para poderla aadir al formulario Windows. Por esa razn, haremos clic sobre la tabla Socios de la ventana de OrIgenes de datos como se indica en la figura 1. 481 $abla !ocios de la ventana de or@"enes de datos Figura 1 En la ventana de OrIgenes de datos y en concreto con la tabla Socios desplegada, centraremos nuestra atencin en el campo +I: como se indica en la figura 2. Campo ;I( de la tabla !ocios Figura 2 Pulse sobre la lista desplegable que aparece a la derecha del campo +I: y seleccione la opcin Label como se indica en la figura 3. Lista desple"able con la opcin Label seleccionada como campo de la tabla desple"ada Figura 3 En este caso, la ventana de OrIgenes de datos quedar informada tal y como se indica en la figura 4. 482 Campo ;I( modificado en la ventana de :r@"enes de datos Figura 4 A continuacin, haremos clic sobre la tabla Socios como se indica en la figura 5, y posteriormente pulsaremos sobre la lista desplegable que aparece a la derecha de la tabla. $abla !ocios seleccionada en la ventana de :r@"enes de datos Figura 5 Si hacemos clic sobre la lista desplegable, aparecer una lista de opciones o posibilidades, para indicar cmo queremos que sean los campos de la tabla seleccionada con respecto a que tipo de controles queremos que sean. Esto es lo que se indica en la figura 6. Lista desple"able de la tabla !ocios de la ventana de :r@"enes de datos Figura 6 Por defecto, una tabla queda determinada con un icono que representa el control Data1rid#ie!, aunque se puede modificar la representacin que deseamos tengan los datos dentro de un formulario seleccionando cualquiera de las opciones que tenemos de la lista desplegable. Estas opciones pueden ser cualquiera de las siguientes: Representa los datos volcados dentro de un control Data1rid#ie! 483 Representa los datos volcados dentro de controles estndar como Te&tBo& u otros controles para reflejarla como Detalle de la informacin No representa ningn control como tipo de control de volcado de datos En el caso de la tabla Socios que usaremos como tabla padre, cambiaremos la representacin por defecto de Data1rid#ie! para indicarle que nos represente la informacin en controles, tal y como se indica en la figura 7. $abla !ocios seleccionada en la ventana de :r@"enes de datos como Detalle Figura 7 Ahora que tenemos la tabla maestra ya preparada, pasaremos a preparar la tabla hija.
,reparando la tabla #ia Ahora que ya tenemos preparada la tabla tabla padre, prepararemos la tabla hija de los alquileres de las pelculas de video, para poder usar su informacin dentro del formulario Windows. Por esa razn, haga clic sobre la tabla *l9uileres de la ventana de OrIgenes de datos como se indica en la figura 8. $abla Alquileres de la ventana de or@"enes de datos Figura 8 Dentro de la ventana de OrIgenes de datos y en concreto de la tabla *l9uileres desplegada, centraremos nuestra atencin en el campo AlAuilerID y .ocio+I: como se indica en la figura 9. 484 Campos AlquilerID y !ocio;I( de la tabla Alquileres Figura 9 Sobre el campo AlAuilerID y .ocio+I:, haremos una pequea modificacin. Pulse sobre la lista desplegable que aparece a la derecha del campo AlAuilerID y .ocio+I: y seleccione la opcin Label como se indica en la figura 10. Lista desple"able de opciones de un campo de la tabla desple"ada Figura 10 De esta manera, el campo AlAuilerID y .ocio+I: quedarn modificados en la ventana OrIgenes de datos como se indica en la figura 11. Campo AlquilerID y !ocio;I( modificados en la ventana de :r@"enes de datos Figura 11 A continuacin, haremos clic sobre la tabla *l9uileres como se indica en la figura 12, y posteriormente pulsaremos sobre la lista desplegable que aparece a la derecha de la tabla. 485 $abla Alquileres seleccionada en la ventana de :r@"enes de datos Figura 12 Nos aseguraremos que est seleccionada la opcin Data1rid#ie! que es la que aparece por defecto. Esto es lo que se indica en la figura 13. n la tabla Alquileres8 nos ase"uraremos de seleccionar la opcin Data<rid&ie7 Figura 13 Una vez que tenemos las tabla maestra y detalle preparadas para utilizarlas, las aadiremos al formulario Windows para que tengan el funcionamiento esperado.
486
%(dulo B ? Cap!tulo 3 B. -ncrustando los datos maestro detalle Ya sabemos como crear un origen de datos, tambin sabemos como preparar los datos maestro y detalle, y por ltimo, lo que nos queda es insertar esos datos en el formulario, y relacionar todos sus datos para que funcionen de la forma esperada. A continuacin, veremos como llevar a cabo esta tarea y aprenderemos a hacerlo posible de forma muy rpida y sencilla.
-ncrustando la tabla padre en el formulario Nuestra primera accin, ser incrustar en el formulario los datos o tabla padre, que en nuestro caso es la formada por la tabla Socios. Para situar los datos de la tabla Socios dentro de un formulario y en concreto como una informacin de detalle, bastar con arrastrar y soltar la tabla Socios sobre el formulario como se indica en la figura 1. 487 9ara presentar la informacin como detalle8 arrastraremos la tabla !ocios de la ventana :r@"enes de datos sobre el formulario 6indo7s Figura 1 Observaremos que #isual Basic 2BBC %&press, generar por nosotros un conjunto de componentes y controles, que por defecto tendr una apariencia similar a la que se presenta en la figura 2. 488 Controles y Componentes de la tabla maestra a2adidos al formulario 6indo7s Figura 2 Observe por un momento, que el campo +I: que declaramos como Label en la ventana de OrIgenes de datos, aparece como tal en el formulario, tal y como se indica en la figura 3. Campo ;I( de la tabla8 representado como un control Label en el formulario 6indo7s Figura 3 Si ejecutamos nuestra aplicacin, observaremos que esta acta como una tpica aplicacin de acceso a datos que nos permite navegar a travs de los campos de la tabla Socios, tal y como se indica en la figura 4. 489 Aplicacin en ejecucin de la tabla de detalle incrustada en el formulario 6indo7s Figura 4 A continuacin, insertaremos en el formulario la tabla *l9uileres y relacionaremos ambas tablas para que se muestren los datos relacionados, dentro del formulario Windows.
-ncrustando la tabla #ia en el formulario Ya tenemos la tabla padre insertada en nuestro formulario Windows. Nuestra segunda accin, ser la de incrustar en el formulario los datos o tabla hoja, que en nuestro caso es la formada por la tabla *l9uileres, la cul adems, posee una relacin entre campos con la tabla Socios insertada anteriormente. Para llevar a cabo esta accin arrastraremos y soltaremos la tabla *l9uileres sobre el formulario como se indica en la figura 5. 490 9ara presentar la informacin de la tabla Alquileres8 arrastraremos la tabla de la ventana :r@"enes de datos sobre el formulario 6indo7s Figura 5 Observe que #isual Basic 2BBC %&press, genera por nosotros ms componentes y controles, que por defecto tendr una apariencia similar a la que se presenta en la figura 6. 491 Controles y Componentes de la tabla maestra y detalle a2adidos al formulario 6indo7s Figura 6 Como podemos observar, el entorno de trabajo ha hecho por nosotros el trabajo ms complejo para representar los datos de forma rpida y sencilla. El esquema de datos tipados, apareca ya en nuestro proyecto cuando asignamos el correspondiente origen de datos. Ahora lo que ha ocurrido, es que al arrastrar y soltar la tabla padre Socios de la ventana de OrIgenes de datos, en el entorno se ha aadido un componente de nombre (SD$#ideoDataSet que es el que permitir relacionar el DataSet tipado con nuestros datos. Este componente ser usado por la relacin maestro detalle de las dos tablas aadidas al formulario. En la figura 7, podemos ver el esquema aadido a nuestro proyecto, y el componente del que estamos hablando. 492 squema del Data!et tipado a2adido al proyecto y su componente de relacin Figura 7 Ejecute la aplicacin y observe el comportamiento de la misma. Observar por lo tanto, que los datos entre detalle y maestra, no estn relacionados. Si navegamos a travs de los datos de detalle a travs del objeto SociosBinding$aigator, el control Data1rid#ie! no representa la relacin de los datos seleccionados. Esto es lo que se muestra en la figura 8. jecucin de la aplicacin confirmando que los datos mostrados no est>n relacionados Figura 8 A continuacin, la tarea que nos queda para completar el correcto funcionamiento de nuestra aplicacin, es la de relacionar la tabla detalle y la tabla maestra entre s, para que los datos que se muestran en la aplicacin, estn relacionados entre s.
Eelacionando la tabla padre con la tabla #ia La tarea ms sencilla es la de relacionar la tabla detalle con la tabla maestra. Es una tarea sencilla, porque #isual Basic 2BBC %&press nos proporciona las herramientas necesarias para simplificar al mximo esta tarea. 493 Para llevar a cabo esta tarea, haga clic sobre el control Data1rid#ie! que corresponde a los datos de la tabla maestra, y acceda a la ventana de +ropiedades. Dentro de la ventana de +ropiedades, acceda a la propiedad DataSource como se indica en la figura 9. 9ropiedad Data!ource del control Data<rid&ie7 de la informacin maestra Figura 9 Despliegue esta propiedad, y de la lista desplegable que aparece, seleccione la opcin :?BAlAuileresB.ocios como se indica en la figura 10. Asi"nacin de la clave de relacin entre las tablas Figura 10 494 Cuando se asigna el campo de relacin de las tablas, dentro de la aplicacin se aade esta relacin para que cuando naveguemos entre los datos de la tabla Socios aparezca toda la informacin de la tabla *l9uileres relacionada con la tabla Socios. Esto de lo que hablamos, est supeditado por el componente )K-*l9uileres-SociosBindingSource que es lo que se indica en la figura 11. Controles y componentes incluido el de relacin entre tablas8 a2adidos al formulario 6indo7s Figura 11 Para finalizar, ejecutaremos nuestra aplicacin y comprobaremos que el funcionamiento de esta, incluida la relacin entre tablas, funciona como esperbamos. En la figura 12, podemos observar el comportamiento de nuestra aplicacin en ejecucin. 495 Aplicacin en ejecucin8 mostrando la correcta relacin entre las tablas Figura 12
496 %(dulo B ? Cap!tulo 3 3. %anipulando los datos maestro detalle Obviamente, los datos maestro detalle no nos sirve nicamente para insertar las tablas de datos en un formulario, mostrarlos y navegar por ellos. Adems de esto, podemos tambin manipular los datos maestro detalle, modificarlos, actualizarlos, borrarlos, sin hacer ninguna accin adicional. El control Binding$aigator ya proporciona todas las caractersticas necesarias para realizar estas acciones. Podemos personalizar el control para permitir o denegar estas acciones. Adems, dentro de la ventana de OrIgenes de datos, podemos seleccionar diferentes campos de las tablas y cambiar el tipo de control en el que queremos representar sus datos. A continuacin veremos un breve ejemplo de como manipular datos para que nos sirva de aprendizaje de cmo hacer esto posible.
%odificando datos Ejecute la aplicacin de ejemplo que hemos diseado hasta ahora y sitese en alguno de sus campos. Centrndonos en la informacin de la tabla Socios, cambiaremos un campo determinado, como el que se muestra en la figura 1. Modificaremos el valor de un campo para que nos sirva de ejemplo Figura 1 Acto seguido, cuando hayamos realizado la modificacin, haremos clic sobre la opcin de 1uardar datos, tal y como se muestra en la figura 1. 497 :pcin del control #indin";avi"ator para "uardar los datos modificados Figura 2 Como vemos, la manipulacin de datos es realmente sencilla y en la relacin de datos mostrada, no tiene porqu presentarnos ninguna dificultad.
-nsertando y eliminando datos Si queremos agregar datos, deberemos hacer clic sobre la opcin *gregar nueo del control Binding$aigator como se muestra en la figura 3. A2adir un re"istro nuevo es realmente sencillo Figura 3 De la misma manera funciona el mecanismo para eliminar un registro, tal y como se muestra en la figura 4. liminar un re"istro de forma r>pida Figura 4 Recuerde pulsar sobre el icono si quiere que los cambios y modificaciones realizadas se mantengan. Pulsando sobre ese icono, la accin de manipulacin de datos se lanza contra la base de datos. 498