You are on page 1of 230

Guía del Usuario

Libro 3
Framework
2013 Release 3 Xojo, Inc.
Guía Xojo
Libro 3: Framework

©2013 Xojo, Inc.

Version 2013 Release 1

ii
Section 1

Acerca de la Guía de Usuario Xojo

Esta Guía del Usuario Xojo está dirigida a describir Xojo tanto Multimedia, Bases de datos, Impresión e Informes,
para los nuevos desarrolladores de Xojo como aquellos otros Comunicación y Redes, Concurrencia y Depuración. Termina con
que tengan experiencia en su uso. dos capítulos sobre Crear tus Aplicaciones y el capítulo sobre
Características Avanzadas del Framework.
La Guía del Usuario está dividida en varios “libros”, cada uno de
los cuales se centra en una área específica de Xojo: El libro de Despliegue cubre estas áreas: Desplegar tus
Fundamentos, Interfaz de Usuario, Framework y Despliegue. Aplicaciones, Desarrollo Multiplataforma, Desarrollo Web,
Migración desde otras Herramientas, Gestión de Código y
La Guía del Usuario está organizada de tal modo que se
Aplicaciones de Ejemplo.
introducen los temas en el orden en el que se utilizan
generalmente. Copyright
Todos los contenidos están protegidos mediante copyright 2013
El libro Fundamentos comienza con el Entorno de Desarrollo
de Xojo, Inc. Todos los derechos reservados. Queda prohibida la
Integrado de Xojo (IDE) y continúan con el Lenguaje de
reproducción parcial o total de este documento o de los archivos
Programación Xojo, Módulos y Clases; finalizando con el
relacionados, y también está prohibida su transmisión mediante
capítulo sobre Estructura de una Aplicación.
cualquier medio (electrónico, fotocopiado, grabado y cualquier
El libro sobre la Interfaz de Usuario cubre los Controles y Clases otro), sin el consentimiento previo por escrito por parte del editor.
utilizados para crear aplicaciones de Escritorio y Web.
Marcas registradas
El libro Framework se asienta sobre lo aprendido en los libros Xojo es una marca registrada de Xojo, Inc. Todos los derechos
Interfaz del Usuario y Fundamentos. Cubre las principales áreas reservados.
del Framework de Xojo, incluyendo: Archivos, Texto, Gráficos y

3
This book identifies product names and services known to be
trademarks, registered trademarks, or service marks of their
respective holders. They are used throughout this book in an
editorial fashion only. In addition, terms suspected of being
trademarks, registered trademarks, or service marks have been
appropriately capitalized, although Xojo, Inc. cannot attest to the
accuracy of this information. Use of a term in this book should
not be regarded as affecting the validity of any trademark,
registered trademark, or service mark. Xojo, Inc. is not associated
with any product or vendor mentioned in this book.

4
Section 2

Convenciones

La Guía utiliza capturas de pantalla obtenidas desde las versio- OS X y pulsar a continuación la tecla “O”. Has de soltar la te-
nes de Xojo de Windows, OS X y Linux. El diseño de la interfaz y cla modificadora solo después de haber pulsado la tecla de
el conjunto de características son idénticos en todas las platafor- atajo.
mas, de modo que las diferencias entre las plataformas son mera-
• Encontrarás entrecomillado aquello que deba ser escrito,
mente cosméticas y relativas a las diferencias intrínsecas en las
como por ejemplo “GoButton”.
interfaces de usuario de Windows, OS X y Linux.
• Algunos pasos te pedirán que introduzcas líneas de código en
• Negrita. Se utiliza para resaltar un nuevo término usado por
Editor de Código. Estas aparecerán en un recuadro
primera vez y destacar conceptos relevantes. Además, los títu-
sombreado:
los de los libros como Guía de Usuario Xojo se muestran en
cursiva.
ShowURL(SelectedURL.Text)
• Cuando se indique que has de seleccionar un elemento de los
menús, verás algo como “selecciona Archivo ↠ Nuevo
Proyecto”. Esto es equivalente a “Selecciona Nuevo Proyecto Cuando introduzcas código, observa estas indicaciones:
en el menú Archivo.”
• Escribe cada línea impresa en una línea separada en el Editor
• Los atajos de teclado consisten en una secuencia de teclas de Código. No intentes unir dos o más líneas impresas en la
que deben de pulsarse en el orden listado. En Windows y misma línea, o bien dividir una línea larga en dos diferentes.
Linux la tecla Ctrl es el modificador; en OS X, la tecla ⌘ (Co-
• No añadas espacios adicionales donde no se indique en el
mando) es el modificador. Por ejemplo, cuando veas el atajo
código impreso.
de teclado “Ctrl+O” o “⌘ -O”, significa que debes de mantener
pulsada la tecla Control en Windows o Linux y pulsar a con- • Por supuesto, puedes copiar y pegar el código.
tinuación la tecla “O”, o bien mantener pulsada la tecla ⌘ en

5
Cada vez que ejecutes tu aplicación, Xojo comprobará el código
para buscar errores de sintáxis y ortográficos. Si esta comproba-
ción resulta en algún error, entonces aparecerá un panel de error
en la parte inferior de la ventana principal para que puedas re-
visarlo.

6
Sección 1

Acceder a Archivos

Un FolderItem es cualquier cosa que pueda almacenarse en un convenciones del sistema operativo, ShellPath es la ruta POSIX
disco, como volúmenes, carpetas, archivos, aplicaciones y path, URLPath es una ruta del tipo file:///.
documentos.
Alias
Mediante el uso de la clase FolderItem, puedes obtener una Indica que el FolderItem es en realidad un alias o un atajo.
referencia de cualquiera de estos elementos en tus discos. Para Count
leer un archivo necesitas un FolderItem que lo represente. Para Si el FolderItem es una carpeta, devuelve el número de
escribir un archivo, necesitas un FolderItem. Cuando indiques a FolderItems en ella.
los usuarios que elijan un archivo utilizando cualquiera de los
CreationDate, ModificationDate
selectores de archivo, obtendrás un FolderItem que será una
Las fechas de creación y modificación para el archivo o carpeta.
referencia del archivo seleccionado.
Directory
Una vez que tengas un FolderItem, puedes acceder a sus
Indica si el FolderItem es una carpeta.
propiedades (como su nombre o su ruta) y realizar acciones
como su borrado o bien su copiado. DisplayName, Name
El DisplayName es el nombre que ve generalmente el usuario (y
Folder Item en muchos casos se omite la extensión, por ejemplo). El Name
Clase: FolderItem es el nombre real del archivo o carpeta.
Propiedades
Exists
NativePath, ShellPath, URLPath Si True, el FolderItem existe sobre la unidad.
Devuelve la ruta del FolderItem. NativePath utiliza las

8
ExtensionVisible CopyFileTo
Permite comprobar el ajuste del OS para ver si las extensiones Copia el FolderItem al destino (indicado como un FolderItem).
son visibles.
CreateAsFolder
Group, Owner, Permissions Crea una carpeta utilizando la ubicación y nombre del FolderItem.
Utilizado por OS X y Linux para cambiar los atributos de archivo.
CreateVirtualVolume
LastErrorCode
Crea un archivo de Volumen Virtual. Dirígete a la sección sobre
Devuelve un código de error distinto de cero en el caso de que
Volúmenes Virtuales para obtener información adicional.
ocurra un error con una operación sobre el FolderItem.
Delete
Length
Borra el FolderItem de inmediato. No se utiliza la Papelera o
El tamaño del archivo.
Carpeta de Reciclaje.
Locked
Un archivo que está bloqueado no puede ser modificado. GetRelative, GetSaveInfo
GetRelative devuelve un FolderItem basado en el GetSaveInfo
VirtualVolume proporcionado.
Si el FolderItem es un VirtualVolume, entonces esto devuelve el
VIrtualVolumen usado para acceder a él. Dirígete a la sección Utiliza GetSaveInfo para crear una cadena que pueda utilizarse
sobre Volúmenes Virtuales para obtener más información. para crear un FolderItem sin necesidad de utilizar un
AbsolutePath. Este método puede encontarr un archivo en el
Visible
caso de que se mueva, algo que no puede hacerse con un
Indica si el archivo está visible u oculto en la unidad.
AbsolutePath.
Métodos
Item
Child
Si el FolderItem es un directorio, el método te permite iterar a
Si el FolderItem es una carpeta, entonces esto devuelve un
través de sus contenidos. (Basado en 1).
FolderItem en ella con el nombre indicado.

9
Launch obtener de forma rápida una referencia a un archivo, tal y como
Si el FolderItem es una aplicación, este método ejecuta la se ven en el siguiente fragmento de código:
aplicación. Si el FolderItem es un documento, se abre el
documento usando la aplicación por defecto.
Dim f As FolderItem
TrueChild, TrueItem f = GetFolderItem("Schedule")
Utilizado para resolver los FolderItem que apuntan a alias.

Shortcuts and Aliases Para obtener la carpeta en la que reside tu aplicación:


Los atajos (alias en OS X) son archivos que representan en
realidad un volumen, aplicación, carpeta o un archivo Dim f As FolderItem
almacenado en otra ubicación y, posiblemente, mediante otro f = GetFolderItem("")
nombre. La clase FolderItem contiene propiedades y métodos
que te permiten resolver el atajo y trabajar con el objeto real o
bien trabajar con el objeto directamente. La función La ruta completa (en ocasiones denominada la ruta absoluta) de
GetFolderItem resuelve automáticamente un atajo cuando lo un volumen, directorio, aplicación o documento empieza con el
encuentra, mientras que la función GetTrueFolderItem trabaja con nombre del volumen, seguido por el caracter utilizado como
el atajo propiamente dicho. delimitador de ruta (una barra invertida en Windows, dos puntos
en Macintosh, y una barra en Linux), los nombres de cualquiera
Acceder a un Archivo de una Ubicación de las carpetas en la ruta (cada una de ellas separada mediante
Concreta el delimitador de ruta) y finalizando con el nombre del archivo.
Si sabes cuál es la ruta completa de un archivo y quieres acceder
al archivo, puedes hacerlo especificando la ruta del archivo. Para crear una ruta absoluta a un archivo o carpeta, debes de
utilizar la función global Volume para crear una ruta completa del
Por ejemplo, supongamos que tienes un documento llamado elemento, comenzando con el disco duro sobre el que se
“Schedule” almacenado en la misma carpeta que tu aplicación. encuenta. A continuación utilizas el método Child de la clase
La ruta relativa comienza con la carpeta en la que se encuentra tu FolderItem para navegar hasta el elemento. Volume devuelve un
aplicación. La función global GetFolderItem puede utilizarse para FolderItem para cualquiera de los volúmenes montados. Has de
10
indicar el volumen pasando un entero, que indica el volumen. directorio en curso, de modo que el padre de dicho directorio
Volume 0 es el volumen que contiene el sistema operativo — el está un nivel por arriba en la jerarquía.
volumen de “arranque”.

Dim f As Folderitem
Dim f As FolderItem f = GetFolderItem("").Parent
f = Volume(0) // el volumen de arran-

Una vez que tienes un FolderItem, puedes (en función del tipo de
Parent devuelve el FolderItem para el siguiente elemento hacia elemento del que se trate) copiarlo, borrarlo, cambiar su nombre,
arriba en la ruta absoluta correspondiente al actual FolderItem. leer o escribir sobre él, etc. Aprenderás como leer y escribir
No funciona si intentas obtener el padre de un volumen. El archivos usando FolderItems más adelante en este capítulo.
método Child te permite acceder a cualquier elemento que este
GetFolderItem tiene un parámetro opcional que te permite pasar
inmediatamente por encima del FolderItem actual.
una ruta absoluta, una ruta del shell o una ruta URL. Utiliza las
Puedes crear una ruta completa comenzando con un volumen siguientes constantes de la clase FolderItem: PathTypeShell,
con el método Child. Por ejemplo, si quieres obtener un PathTypeURL, y PathTypeAbsolute. El usado por omisión es una
FolderItem para el archivo “Schedule” en el directorio “Stuff” del ruta absoluta.
volumen de arranque, la instrucción debería de ser:
Puedes indicar el tipo de ruta pasando una de las constantes de
clase como segundo parámetro en una llamada a GetFolderItem.
Dim f As FolderItem Por ejemplo, lo siguiente utiliza la ruta del shell sobre Linux.
f = Volume(0).Child("Stuff").Child("Schedule")
Devuelve un FolderItem para la carpeta “Documentos” en el
directorio de inicio de un usuario llamado “Joe.”

El siguiente ejemplo funciona con una ruta relativa. Utiliza la


propiedad Parent para obtener el FolderItem para el directorio
que contiene el directorio en el que se encuentra la aplicación.
Pasando una cadena vacía a GetFolderItem se obtiene el

11
Acceder a Carpetas del Sistema
Dim f As FolderItem
f = GetFolderItem("/home/Joe/Documents",_
Los sistemas operativos tienen ubicaciones concretas para varias
FolderItem.PathTypeShell) carpetas que contienen información, como pueda ser la carpeta
If f.Exists Then Documentos del usuario.
TextField1.Text = f.AbsolutePath
Else Utiliza el módulo SpecialFolder para obetner FolderItems que
MsgBox("The FolderItem does not exist.") representen dichas carpetas especiales del sistema. La ventaja
End If
de utilizar este módulo frente a la creación de una ruta
manualmente es que SpecialFolder siempre funciona y es
correcto entre plataformas (en la mayoría de los casos) y también
Una ruta URL debe de comenzar con “file:///” El siguiente
entre diferentes idiomas.
ejemplo utiliza una ruta URL que apunta a la carpeta
“Documents” del usuario en Windows: Obtienes el FolderItem deseado utilizando la sintaxis:

result = SpecialFolder.FolderName
Dim f As FolderItem
f =
GetFolderItem("file:///C:/Documents%20and%20Sett
Donde result es el FolderItem que quieres obtener y FolderName
ings/" _
+ "Joe%20User/My%20Documents/", es el nombre de la función SpecialFolder que devulve dicho
FolderItem.PathTypeURL) FolderItem. Por ejemplo, lo siguiente obtiene un FolderItem para
If f.Exists Then
la carpeta Application Support en OS X y el directorio Application
MsgBox(f.AbsolutePath)
Else Data en Windows:
MsgBox("The FolderItem does not exist.")
End If
Dim f As FolderItem
f = SpecialFolder.ApplicationData
Las propiedades AbsolutePath, URLPath y ShellPath de la clase
FolderItem contienen los tipos de rutas.

12
Dirígete a SpecialFolder en la Referencial del Lenguaje para ruta errónea contiene una referencia del volumen y/o un nombre
obtener un listado completo de las funciones soportadas y los de directorio que no existe. Por ejemplo, si indicas Volume (1)
FolderItems disponibles para OS X, Windows y Linux. cuando el usuario sólo tiene un volumen, la función Volume
devuelve un valor Nil en la instancia del FolderItem, f. Si intentas
Nota: No todas las funciones devuelven FolderItems en todas las utilizar cualquiera de las propiedades o métodos de la clase
plataformas. Si un FolderItem no está definido en todas las plataformas
debes utilizar una función alternativa que devuelva un FolderItem en cada FolderItem sobre un FolderItem Nil, entonces ocurrirá una
plataforma. Comprueba si el resultado es distinto de Nil antes de usar el NilObjectException. Si la excepción no se gestiona, la aplicación
FolderItem. Por ejemplo, SpecialFolder.Documents devuelve la carpeta de
Documentos del usuario en OS X y Windows, pero devuelve Nil en Linux. En se colgará.
Linux debes llamar a SpecialFolder.Home. Por ejemplo:
En segundo lugar, puede que la ruta sea váñlida pero que el
archivo al que intentas acceder no exista. El siguiente código
Dim f As FolderItem
comprueba estas dos situaciones:
#If Not TargetLinux
f = SpecialFolder.Documents
#Else Dim f As FolderItem
f = SpecialFolder.Home f =
SpecialFolder.Documents.Child("Schedule")
#EndIf
If f <> Nil Then
If f <> Nil Then
If f.Exists Then
If f.Exists Then
MsgBox(f.AbsolutePath)
// use the FolderItem Else
End if MsgBox("File does not exist!")
Else End If
MsgBox("FolderItem is Nil!") Else
MsgBox("Invalid path!")
End If
Verifi cando el FolderItem
Cuando intentas obtener un FolderItem, pueden ir mal un par de
Si la ruta es válida, el código consulta la propiedad Exits del
cosas. En primer lugar, puede que la ruta no sea correcta. Una
FolderItem para asegurarse de que el archivo exista realmente; si
13
el archivo no existe o la ruta no es válida se muestra un mensaje
de advertencia. Dim f, f2 As FolderItem
f =
También puedes gestionar una ruta inválida usando un Bloque de SpecialFolder.Documents.Child("Schedule")
Excepción. Estos están cubiertos en la sección Gestión de If f <> Nil Then
Excepciones en el capítulo Depuración. f2 = New FolderItem(f)
End If
Crear Nuevos FolderItems
Puedes crear un FolderItem para un elemento existente
pasándolo en el pathname. Cuando creas un FolderItem con el Borrar FolderItems
comando New, puedes pasar la ruta del nuevo FolderItem como Una vez que tienes un FolderItem que representa un ítem que
un parámetro opcional. Por ejemplo: puede ser borrado, puedes llamar al método Delete. El siguiente
ejemplo borra el archivo representado por el FolderItem devuelto:

Dim f As FolderItem Si el FolderItem está bloqueado, ocurrirá un error. Puedes


f = New FolderItem("myDoc.txt") comprobar si el FolderItem está bloqueado consultando la
propiedad Locked del FolderItem.

indica el nombre del nuevo FolderItem y está ubicado en la Borrar un FolderItem no se limita a mover el FolderItem a la
misma carpeta que tu aplicación (si estás ejecutándola en el IDE) papelera. El FolderItem se borra de forma permanente del
o la misma carpeta de tu aplicación compilada. volumen.

Si pasas un FolderItem en vez de una ruta, New creará una copia


del FolderItem pasado. En este ejemplo, el FolderItem “f2” se
refiere a una copia del FolderItem original, no una referencia de
él.

14
Dim f As FolderItem Dim f As FolderItem
f = f = GetFolderItem("")
SpecialFolder.Documents.Child("Schedule") For i As Integer = 1 To f.Count
If f <> Nil Then ListBox1.AddRow(Str(i))
If f.Exists Then ListBox1.Cell(ListBox1.LastIndex, 1) =
f.Delete f.Item(i).AbsolutePath
End If Next
End If

El siguiente ejemplo devuelve un FolderItem que representa un


Encontrar la Carpeta por Defecto de tu Aplicación archivo llamado “My Template” en una carpeta denominada
Al pasar una cadena vacía (dos comillas sin caracteres entre “Templates” que está ubicada en la misma carpeta que la
ellas) a la función GetFolderItem se obtiene un FolderItem que aplicación:
representa la carpeta en la que reside tu aplicación. Puedes
utilizar encontes el método Item del FolderItem para acceder a Dim f As FolderItem
todos los elementos de la carpeta en la que reside tu aplicación. f = GetFolderItem("Templates").Child("My
Template")
El método Item devuelve un array de FolderItems en el directorio.
El array está basado en uno. Obtienes un FolderItem para un
elemento pasando al método Item el índice de dicho elemento. Iterar por los Contenidos del FolderItem
Por ejemplo, el siguiente método obtiene un FolderItem para el Es posible que necesites iterar por un elemento de carpeta (es
directorio y puebla un ListBox con las rutas absolutas de cada decir, una carpeta o directorio) para procesar todos los archivos
elemento en el directorio. Dicho método utiliza la propiedad contenidos en ella. He aquí un ejemplo que borra todos los
archivos en una carpeta:
Count para obtener la cantidad de elementos, y el método Item
para obtener un FolderItem de cada elemento.

15
Abrir Archivos
Dim itemsToRemove() As FolderItem
El método más sencillo para permitir que un usuario seleccione el
Dim n As Integer = dir.Count
archivo a abrir es usar la función global GetOpenFolderItem,
If n > 0 Then como sigue:
For i As Integer = 1 To n
If dir.Item(i).Exists And Not
dir.Item(i).Directory Then
Dim f As FolderItem
itemsToRemove.Append(dir.Item(i)) f = GetOpenFolderItem(FileTypes1.jpeg)
End If MsgBox(f.ModificationDate.ShortDate)
Next
End If
La función
For i As Integer = 0 To Ubound(itemsToRemove)
GetOpenFolderItem
itemsToRemove(i).Delete Figura 1.1
muestra el selector
Next
de Abrir archivo y
devuelve un objeto
Este código guarda los archivos a borrar en un array, de modo FolderItem que
que puedan ser borrados una vez que se hayan procesado todos representa el
los archivos, con objeto de mejorar el rendimiento. archivo
seleccionado por el
Solicitar Archivos y Carpetas al Usuario usuario. Es preciso
Existen varios métodos disponibles para permitir que el usuario pasar a la función
seleccione los archivos a utilizar con tus aplicaciones. Es posible GetOpenFolderItem
que quieras que tu usuario pueda indicar el archivo a abrir, indicar uno o más tipos de
el nombre del archivo a guardar o bien dejar que elija una archivos (que han
carpeta. de definirse en el
Editor de Conjuntos

16
de Tipos de Archivo, o bien mediante la clase FileType mediante • Directorio por defecto (propiedad Initial Directory)
el lenguaje). Ésta presenta al usuario sólo los tipos de archivo
• Tipos de archivo admitidos (propiedad Filter)
indicados en el navegador. De eset modo, el usuario sólo puede
abrir los archivos del tipo correcto. Para pasar más de un tipo de • Texto de los botones Validar y Cancelar (propiedades
archivo, sepáralos con punto y coma. ActionButtonCaption y CancelButtonCaption)

Si el usuario hace clic en el botón Cancelar en vez de sobre el • Texto que aparece sobre el navegador de archivos (propiedad
botón Abrir en el selector de Abrir Archivo, entonces Title)
GetOpenFolderItem devuelve Nil. Tendrás que asegurarte de que
el valor devuelto no sea Nil, antes de usarlo. Si no lo haces tu • Texto que aparece bajo el navegador de archivos (propiedad
aplicación podría colgarse con un NilObjectException. El PromptText)
siguiente ejemplo muestra cómo debería de haberse escrito el
Cuando utilizas la clase OpenDialog, creas un nuevo objeto
código del anterior ejemplo para verificar un objeto Nil:
basado en esta clase y asignas los valores a sus propiedades
para personalizar su aspecto. El siguiente ejemplo utiliza un panel
Dim f As FolderItem
personalizado y muestra sólo un tipo de archivo:
f = GetOpenFolderItem(FileTypes1.jpeg)
If f <> Nil Then
MsgBox(f.ModificationDate.ShortDate)
End If

Para un control más preciso, puedes utilizar la clase OpenDialog


para crear un selector de Abrir Archivo. La clase permite crear un
cuadro de diálogo de apertur de archivo personalizable, sobre la
que puedes indicar los siguientes aspectos del diálogo:

• Posición (propiedades Left y Top)

17
encargado de crear el archivo y de escribir los datos sobre el
Dim dlg As New OpenDialog archivo. Aprenderás a crear archivos y escribir datos
dlg.InitialDirectory = posteriormente en este mismo capítulo.
SpecialFolder.Documents
Cuando llamas a la función GetSaveFolderItem, defines el tpo de
archivo y el nombre por defecto para el archivo (que aparece en
dlg.Title = "Select a MIF file"
el campo Nombre del selector Guardar Como). El tipo de archivo
dlg.Filter = FileTypes1.pdf
(que es el primer parámetro de la función) es el nombre de
cualquier tipo de archivo definido para el proyecto en el cuadro
Dim f As FolderItem
de diálogo Tipos de Archivo.
f = dlg.ShowModal
If f <> Nil Then Como las otras funciones que devuelve FolderItems, debes de
// Proceed normally asegurarte de que el FolderItem devuelto por
Else GetSaveFolderItems no sea Nil antes de usarlo (lo que puede
// User Cancelled ocurrir si el usuario hace clic en Cancelar).

Este ejemplo muestra un selector Guardar como con el nombre


de archivo por defecto “Untitled”:
Guardar un Archivo
El selector Guardar Como se utiliza para dejar que el usuario elija
la ubicación en la cual guardar el archivo, así como para Dim f As FolderItem

proporcionar el nombre del archivo guardado. f =


GetSaveFolderItem(FileTypes1.jpeg,"Untitled")
La función GetSaveFolderItem presenta el cuadro de diálogo If f <> Nil and f.Exists Then

Guardar Como. La clase SavaAsDialog te permite crear una MsgBox(f.Name)


End If
versión personalizada de este diálogo. Ambos objetos devuelven
un FOlderItem que representa el archivo que desea guardar el
usuario. Esta es una distinción importante, dado que archivo aun
no existe como taal. Debes de proporcionar el código adicional
18
Cuando utilizas la clase SaveAsDialog, creas un nuevo objeto
basado en esta clase y personalizas el diálogo asignando valores Dim dlg As New OpenDialog
a sus propiedades. Puedes personalizar los siguientes aspectos dlg.InitialDirectory =
del diálogo: SpecialFolder.Documents
dlg.Title = "Select a pdf file"
• Posición (propiedades Left y Top)
dlg.Filter = FileTypes1.Pdf
• Directorio por defecto (propiedad Initial Directory)
Dim f As FolderItem
• Tipos válidos de archivo a mostrar (propiedad Filter) f = dlg.ShowModal
If f <> Nil Then
• Nombre de archivo por defecto (propiedad SuggestedFileName)
// Proceed normally
• Texto de los botones Validar y Cancelar (propiedades Else
ActionButtonCaption y CancelButtonCaption) // User Cancelled
End If
• Texto que aparecer sobre el navegador de archivos (propiedad
Title)
Seleccionar una Carpeta
• Texto que aparece bajo el navegador de archivos (propiedad
PromptText) Algunas veces necesitas que el usuario seleccione una carpeta
en vez de que elija un archivo, usando para ello el selector
El siguiente ejemplo abre un cuadro de diálogo guardar como Carpeta. Puedes hacerlo utilizando el método global
personalizado y muestra los contenidos del directorio SelectFolder:
Documentos en el área del navegador.

19
Dim f as FolderItem Dim dlg As New SelectFolderDialog
f = SelectFolder dlg.ActionButtonCaption = "Select"
If f <> Nil Then dlg.Title = "Title Property"
MsgBox(f.Name) dlg.PromptText = "Prompt Text"
End If dlg.InitialDirectory =
SpecialFolder.Documents

Si necesitas más control sobre este selector, puedes utilizar la Dim f As FolderItem
clase SelectFolderDialog. La clase tiene propiedades para f = dlg.ShowModal
modificar el: If f <> Nil Then
// Use the FolderItem here
• Texto del botón Acción
Else
• Texto para el botón Cancelar // User cancelled
End If
• Carpeta inicial

• Posición sobre la pantalla

• Texto general

• Título

• Nombre por omisión de la carpeta

Este ejemplo muestra un selector Folder personalizado:

20
Section 2

Archivos de Texto

Los archivos de texto pueden ser leídos por editores de texto Leer desde un archivo de Texto
(como SimpleText, NotePad, gedit, y BBEedit) y aplicaciones de Una vez tengas un FolderItem que represente un archivo de
tratamiento de textos (como Microsoft Word y Pages). Los texto existente que quieras abrir, procedes a abrir el archivo
archivos de texto pueden ser creados fácilmente, leer de ello o utilizando el método compartido Open de la clase
bien ser escritos por tus aplicaciones. Los archivos de texto son TextInputStream. Este método es una función que devuelve el
muy convenientes dado que también pueden ser leídos por otras “flujo” o “stream” que porta el texto desde el archivo de texto
muchas aplicaciones. hacia tu aplicación. Este fliujo se denomina un TextInputStream.
Esta es una clase especial de objeto diseñado específicamente
Tanto si vas a leer de un archivo de texto como a escribir un
para leer texto de archivos de texto. A continuación utiliza los
archivo de texto, debes de tener en primer lugar un FolderItem
métodos ReadAll o ReadLine del TextInputStream para obtener
que represente el archivo del que vas a leer o sobre el que vas a
el texto del archivo de texto. El TextInputStream mantiene el
escribir.
registro de la última posición en el archivo del que está leyendo.

Flujos de Texto El método TextInputStream.ReadAll devuelve todo el texto del


Los flujos de texto se utilizan para leer o escribir datos sobre los
archivo (mediante el TextInputStream) como una String. El
archivos de texto. Existen dos clases para flujos de texto:
método ReadLine devuelve la siguiente línea de texto (el texto
TextInputStream y TextOutputStream. Como puedes esperar,
tras el último caracter leído pero antes del próximo caracter de
utiliza TextInputStream para leer datos desde los archivos de
fin de línea). A medida que leas texto, puedes determinar si has
texto. Utiliza TextOutputStream para escribir datos sobre los
alcanzado el final del archivo comprobando la propiedad EOF (fin
archivos de texto.
de archivo del TextInputStream. Esta propiedad será True
cuando se haya alcanzado el final del archivo. Cuando hayas

21
terminado de leer de un archivo, llama al método Close del Este ejemplo lee las líneas de texto de un archivo almacenado en
TextInputStream para cerrar el flujo del archivo, permitiendo así la carpeta de Preferencias en la carpeta del Sistema en un
que el archivo esté disponible para ser abierto de nuevo. ListBox:

Este ejemplo permite que el usuario seleccione un archivo de


texto utilizando el cuadro de diálogo de Abrir archivo, mostrando Dim f As FolderItem
el texto en una TextArea. Asume que se han definido los tipos de Dim stream As TextInputStream
archivo válidos en un Conjunto de Tipos de Archivo denominado f =
TextTypes: SpecialFolder.Preferences.Child("My
Apps Prefs")
If f <> Nil And f.Exists Then
stream = TextInputStream.Open(f)
Dim f As FolderItem While Not stream.EOF
Dim stream As TextInputStream ListBox1.AddRow(stream.ReadLine)
f = GetOpenFolderItem(TextTypes.All) Wend
// All the file types in this set stream.Close
If f <> Nil Then End If
stream = TextInputStream.Open(f)
TextArea1.Text = stream.ReadAll()
stream.Close Tratar con las Codifi caciones
End If Si estás leyendo y escribiendo archivos de texto para tu
aplicación, este código funcionará. Sin embargo, si los archivo
proceden de otras aplicaciones o plataformas, en otros lenguajes
Nota: Dado que ReadAll lee todo el texto del archivo, la cadena resultante o bien una combinación de lenguajes, entonces necesitas indicar
será tan grande como el propio archivo. Recuerda esto dado que leer un
archivo de gran tamaño podría requerir una mayor cantidad de memoria que
la codificación del texto. Esto es así porque los códigos de
la disponible por el usuario para la aplicación. caracter superiores al ASCII 127 pueden ser distintos de los que
espera tu aplicación. Cuando leas texto, puedes definir la

22
propiedad Encoding del TextInputStream a la codificación del pasando la codificación como un parámetro opcional a Read o
archivo de texto. ReadAll.

Este es el primer ejemplo, corregido para indicar la codificación Escribir a un Archivo de Texto
de texto del flujo de texto entrante a Windows.ANSI. La cadena Una vez que tienes un FolderItem que representa un archivo de
que contiene el texto utilizará la codificación indicada hasta que texto que quieras abrir y sobre el que quieras escribir, puedes
se cambie específicamente a cualquier otra. El código asume que abrir el archivo usando el método compartido Append de la clase
existe un Conjunto de Tipos de Archivo definido en la aplicación, TextOutputStream. Si estás creando un archivo de texto nuevo o
denominado TextTypes: sobreescribiendo un archivo de texto existente, usa el método
compartido Create de la clase TextOutputStream. Estos métodos
son funciones que devuelven un “flujo” o “stream” y que porta el
Dim text As String
texto desde tu aplicación hacia el archivo de texto. El flujo se
Dim f As FolderItem
denomina un TextOutputStream. Este es un tipo especial de
Dim stream As TextInputStream
objeto diseñado específicamente para escribir texto sobre
f = GetOpenFolderItem(TextTypes.All)
archivos de texto. Utiliza a continuación el método WriteLine de
If f <> Nil Then
la clase TextOutputStream para escribir texto sobre el archivo.
stream = TextInputStream.Open(f)
stream.Encoding = El método WriteLine, por defecto, añade un retorno de carro al
Encodings.WindowsANSI final de cada línea. Esto está controlado por la propiedad
text = stream.ReadAll Delimiter del TextOutputStream, y que puede cambiarse por
stream.Close cualquier otro caracter.
End If
Cuando termines de escribir el texto al archivo, llama al método
Close del TextOutputStream para cerrar el flujo del archivo,
El objeto Encodings proporciona acceso a todas las haciendo que pase a estar nuevamente disponible para su
codificaciones. Utilízalo siempre que necesites definir una apertura. Esto solicita al usuario que elija un archivo de texto y
codificación. También puedes indicar la codificación del texto escribe a continuación el contenido de tres TextFields al archivo
de texto cerrando por último el flujo. Asume que existe un tipo de
23
archivo denominado “Text” en el Conjunto de Tipos de Archivo
denominado TExtTypes. Este es un de los tipos de archivo Dim t As TextOutputStream
comunes que se incluyen en el Editor de conjuntos de Tipos de Dim f As FolderItem
Archivo. f =
GetSaveFolderItem(FileTypes1.Text,
"CreateExample.txt")
Dim f As FolderItem
If f <> Nil Then
Dim fs As TextOutputStream
t = TextOutputStream.Create(f)
file =
t.WriteLine(NameField.Text)
GetOpenFolderItem(TextTypes.Text)
t.WriteLine(AddressField.Text)
If f <> Nil Then
t.WriteLine(PhoneField.Text)
fs = TextOutputStream.Append(f)
t.Close
fs.WriteLine(NameField.Text)
End If
fs.WriteLine(AddressField.Text)
fs.WriteLine(PhoneField.Text)
fileStream.Close Tratando con las Codifi caciones
End If
Tal y como ocurre en el caso de leer archivos de texto, al escribir
un archivo de texto necesitas indicar una codificación. Si la
aplicación que va a leer el archivo está esperando que el texto
Si quieres crear un nuevo archivo de texto, llama entonces a esté en una codificación concreta, deberías de convertir el texto a
TextOutputStream.Create. Este ejemplo pasa un nombre de dicha codificación antes de exportarlo.
archivo por defecto para el nuevo archivo de texto:
Antes de escribir una línea o bien el bloque entero de texto (con
el método Write) utiliza la función ConvertEncoding para convertir
la codificación del texto. He aquí un ejemplo revisado. Este
convierte el texto a la codificación MacRoman:

24
Dim file As FolderItem
Dim fileStream As TextOutputStream
file = GetSaveFolderItem(TextTypes.Text, "My
Info")
fileStream = TextOutputStream.Create(file)
fileStream.Write(ConvertEncoding(NameField.Text,
Encodings.MacRoman)
fileStream.Close

Acceso Aleatorio a Archivos de Texto


Los archivos de texto sólo pueden ser accedidos
secuencialmente. Esto significa que para leer parte del texto que
se encuentra en mitad del archivo, debes de leer todo el texto
que se encuentra antes de él. También significa que para escribir
algo de texto que se encuentra a mitad del archivo de texto,
debes de escribir todo el texto que se encuentra antes del texto a
insertar, escribir a continuación el texto que quieres insertar, y
escribir a continuación el texto que sigue al texto insertado. No
puedes leer de un archivo de texto y escribir sobre el mismo
archivo de texto al mismo tiempo. Si estas limitaciones suponen
un problema para tu proyecto, considera utilizar un archivo
binario en vez de ello. Los Archivos Binarios están recogidos en
la siguiente sección.

25
Section 3

Archivos Binarios

Los archivos binarios son archivos simples que almacenan entonces puede que no lo sepas. Algunos formatos son
valores en su formato binario, en vez de hacerlo como texto. Por públicos. Por ejemplo el formato PNG es público. Otros formatos
ejemplo, el número 30000 almacenado como texto requiere de no lo son. Muchos desarrolladores de software no publican los
cinco caracteres de texto (o bytes) para almacenarse en un formatos binarios usados por sus aplicaciones en la creación de
archivo de texto. En un archivo binario, este número puede los documentos.
escribirse como un entero corto (o simplemente un “short”). Un
short requiere sólo de 2 bytes. Flujos Binarios
Los datos leídos o escritos a un archivo binario viajan a través de
Los archivos binarios también presentan la ventaja añadida de un BinaryStream. Un BinaryStream es una clase que representa
que puedes leer y escribir sobre el archivo sin tener que cerrarlo el flujo de la información entre el FolderItem y el archivo que
entre ambas operaciones. Por ejemplo, puedes abrir un archivo representa. A diferencia de la clase TextInputStream (que sólo
binario, leer parte de los datos, escribir a continuación algunos puede utilizarse para leer de un archivo de texto) y la clase
datos y cerrarlo. También puedes leer y escribir en cualquier TextOutputStream (que sólo puede usarse para escribir datos a
parte del archivo sin tener que leer anteriormente todos los datos un archivo de texto, los BinaryStream pueden utilizarse tanto
precedentes sobre los deseados. para leer como para escribir datos. Incluso puedes indicar al
BinaryStream que sólo leerás datos desde el archivo de modo
La mayoría de las aplicaciones almacenan los datos en formato
que dicho archivo puedan continuar estando disponible para la
binario. EL formato es simplemente el orden de los datos sobre
escritura por parte de otras aplicaciones.
el archivo. Para leer un archivo binario, debes de conocer cuál es
el orden de la información en el archivo. Si tu propia aplicación Los BinaryStream pueden leer y escribir tipos de datos
es la que ha creado el archivo, entonces lo sabrás, pero si el específicos, como por ejemplo cadenas, enteros short, enteros
archivo ha sido creado por otra aplicación no creada por ti,

26
largos, moneda y bytes individuales. También pueden utilizarse
para leer y escribir datos binarios sin formato, en crudo. Dim f As FolderItem
Dim stream As BinaryStream
Leer desde un Archivo Binario f = GetOpenFolderItem(FileTypes1.Text)
Una vez que tienes un FolderItem que representa el archivo que If f <> Nil Then
quieres abrir, abres el archivo usando el método Open de la clase stream = BinaryStream.Open(f, False)
Do
BinaryStream. Este devuelve un BinaryStream. A continuación
TextArea1.AppendText(stream.Read(255))
utilizas los métodos de la clase BinaryStream para leer los datos
Loop Until stream.EOF
del stream. La clase BinaryStream incluye diferentes métodos
stream.Close
para leer cada uno de los tipos de datos soportados. End If

El BinaryStream mantiene un seguimineto de la última posición


del archivo desde el que lees en su propiedad Position. Sin
Cuando lees un BinaryStream, puede que necesites tener en
embargo, puedes cambiar el valor de dicha propiedad para
cuenta la codificación de los caracteres. Para ello, puedes pasar
mover la posición a cualquier ubicación en el archivo.
un parámetro opcional a los métodos Read y ReadPString que
Este ejemplo presenta el Selector de Arbrir Archivo, lee el archivo especifica la codificación. Utiliza el objeto Encodings para
compuesto por cadenas, y muestra dichas cadenas en una obtener cualquier codificación y pásala a Read o ReadPString.
TextArea. Advierte que dado que el código sólo lee datos y no Por ejemplo, la siguiente línea especifica la codificación ANSI:
escribe, se pasa el valor False en el método Open para indicar
que el archivo debería de abrirse en modo de “solo lectura”. De TextArea1.AppendText(stream.Read(255
igual modo, la lectura continúa en bucle hasta que la propiedad , Encodings.WindowsANSI))
EOF del flujo sea True. La propiedad EOF se ajusta
automáticamente como True una vez que se alcanza el final del
archivo. Para obtener más información, consulta la sección sobre
Codificaciones en el capítulo de Texto.

27
Escribir a un Archivo Binario
Una vez que tengas un FolderItem que represente el tipo de Dim f as FolderItem
archivo que quieras abrir y sobre el que quieras escribir, puedes Dim stream as BinaryStream
abrir el archivo usando el método Open de la clase BinaryStream. f = GetSaveFolderItem(FileTypes1.Text,
Si estás creando un nuevo archivo, utiliza el método Create de la "Untitled.txt")
clase BinaryStream. Este método devuelve un BinaryStrema. A If f <> Nil Then
continuación puedes utilizar el método apropiado para escribir stream = BinaryStream.Create(f, True)
datos sobre el flujo. La clase BinaryStream incluye métodos stream.Write(TextArea1.Text)
separados para cada uno de los tipos de datos soportados. stream.Close
End If
El BinaryStream mantiene un seguimiento de la última posición
en el archivo sobre la que se ha escrito en su propiedad Position.
Sin embargo, puedes cambiar el valor de esta propiedad para
mover la posición a cualquier ubicación del archivo.

Cuando hayas finalizado de escribir datos en el archivo, llama al


método Close del BinaryStream para cerrar el flujo del archivo,
permitiendo que esté disponible para que pueda abrirse de
nuevo.

Este ejemplo muestra el cuadro de diálogo Guardar Como y


escribe los contenidos del TextArea1 al archivo de texto:

28
Section 4

Archivos Web

Cuando trabajes con apps web, encontrarás con frecuencia la


necesidad de que un archivo pueda ser subido o descargado. Dim localFile As FolderItem
localFile = GetFolderItem("localfile.txt")
La clase WebFile se utiliza para crear un archivo especial que
puede ser procesado por el navegador web para su subida o // mTextFile is a property of the web page
descarga. // so that it does not go out of scope
// while the file is downloading.
Descargar Archivos mTextFile = WebFile.Open(localFile)
Para iniciar una descarga sobre un WebFile debes de utilizar el
método ShowURL para mostrar su URL: ShowURL(mTextFile.URL)

ShowURL(MyWebFile.URL) El anterior ejemplo termina cargando el archivo desde disco a la


memoria para cada sesión e inicia la descarga del archivo. Esto
puede subir con rapidez el consumo de memoria en el servidor
Recuerda que MyWebFile debe de encontrarse en el ámbito para
web.
que pueda completarse la descarga.
Una mejor aproximación consiste en almacenar una única
En general, el modo más sencillo de crear un WebFile es usando
referencia del WebFile en la clase App global, de modo que no
un FolderItem como punto de partida:
sea preciso crear múltiples copias.

29
Dim localFile As FolderItem Dim localFile As FolderItem
localFile = GetFolderItem("localfile.txt") localFile = GetFolderItem("localfile.txt")

// App.TextFile is a property of App // App.TextFile is a property of App


// so that it does not go out of scope // so that it does not go out of scope
// while the file is downloading and so that // while the file is downloading.
// it can be reused by multiple sessions. // The False parameter loads the file from
App.TextFile = WebFile.Open(localFile) // disk in 64K chunks instead of loading it all
// into memory at once.
ShowURL(App.TextFile.URL) mTextFile = WebFile.Open(localFile, False)

ShowURL(App.TextFile.URL)

La propiedad URL obtiene un URL que es específico para la


Session al tiempo que se utiliza la misma instancia del WebFile,
Pero un WebFile en memoria también puede resultar útil,
ahorrando así RAM en el servidor.
especialmente si estás creando un archivo para descarga directa.
Cualquiera de los anteriores ejemplos están utilizando el método
Puedes crear un WebFile en memoria y suministrarlo como datos
por defecto del evento Open para cargar todo el archivo en
utilizando la propiedad Data:
memoria. Para ahorrar aun más memoria en el servidor web,
puedes dejar que se lea el archivo directamente de disco (en
fragmentos de 64K) indicando False como segundo parámetro:

30
Este control permite que el usuario añada uno o más archivos
// mTextFile is a property of the web page para su carga. Cuando se inicia la subida (llamando al método
mTextFile = New WebFile Upload), todos los archivos se envían a la app web en el servidor
mTextFile.MimeType = "text/plain"
donde puedes procesarlos desde el manejador de evento
UploadCompleted.
// Ensure the browser downloads the file rather
// than trying to display it. Las subidas de archivos mayores de 256K se escriben
mTextFile.ForceDownload = True directamente sobre la carpeta temporal en el caso de que sea
mTextFile.FileName = "TextFile.txt"
escribible. El archivo se mantiene en memoria si la carpeta
temporal no es escribible o bien si el archivo es de 256K o más
mTextFile.Data = "Hello, world!"
pequeño.
// This causes the file to be downloaded
Para los archivos mantenidos en memoria, ten en consideración
ShowURL(mTextFile.URL)
la RAM máxima disponible en el servidor web.

Una vez que se ha subido el archivo, si la carpeta temporal es


El anterior ejemplo también muestra el uso del MimeType para
escribible, entonces los archivos en memoria también se copian a
indicar el tipo de datos contenidos en el WebFile y
disco y se llamada al manejador de evento UploadCompleted.
ForceDownload para asegurar que el navegador siempre
descargue el archivo. Algunos navegadores pueden intentar Si la carpeta temporal no es escribible entonces todos los
mostrar cierto tipo de archivos (como por ejemplo texto, PDF, archivos estará en memoria.
etc.)
En cualquier caso, debes de procesar los archivos en el
Subir Archivos manejador de evento UploadCompleted para guardarlos. Los
Para subir archivos al servidor usando una app web, puedes usar archivos de la carpeta temporal se borran y los que están en
el control WebFileUploader descrito en el Libro Guía del Usuario memoria se liberan al regresar del manejador de evento
2: Interfaz del Usuario. UploadCompleted.

31
El siguiente código procesa cada archivo subido (que es un
WebUploadedFile) en memoria y guarda sus datos a un archivo Dim saveFile As FolderItem
real en disco utilizando un FolderItem y un BinaryStream.
For Each file As WebUploadedFile In Files
saveFile = New FolderItem(file.Name)
Dim bs As BinaryOutputStream Try
Dim f As FolderItem file.Save(saveFile)
Catch e As IOException
For Each file As WebUploadedFile In Files // File Error, skip file
f = New FolderItem(file.Name) Continue
Try End Try
bs = BinaryStream.Create(f, True) Next
bs.Write(file.Data)
bs.Close
Catch e As IOException
// Error, skip file
End Try

Sin embargo, es más sencillo y utiliza menos memoria llamar


simplemente al método Save para guardar el archivo en una
ubicación permanente:

32
Section 5

XML

XML (Extensible Markup Language) es un formato de archivo de


texto legible por las personas, y que puede utilizarse para varios <?xml version="1.0" encoding="UTF-8"?>
<League>
propósitos. También resulta un gran formato de archivo para su
! <Team name="Seagulls">
uso en tus propias aplicaciones.
! ! <Player name="Bob" position="1B" />
! ! <Player name="Tom" position="2B" />
Puedes crear, abrir, modificar y gestionar los archivos XML
! </Team>
utilizando la clase XmlDocument y las relacionadas.
! <Team name="Pigeons">

XML es un formato de documento que utiliza etiquetas, similares <Player name="Bill" posi-
a las que puedes ver en el HTML. Estas etiquetas crean nodos tion="1B" />
! ! <Player name="Tim" position="2B" />
XML que contienen tus datos. Dichas etiquetas son sensibles al
! </Team>
uso de mayúsculas/minúsculas.
! <Team name="Crows">
! ! <Player name="Ben" position="1B" />
He aquí un ejemplo de un documento XML que describe tres
! ! <Player name="Ty" position="2B" />
equipos en una liga de fútbol ficticia:
! </Team>
</League>

Una etiqueta es cualquier cosa entre ángulos, como por ejemplo


<Team>. Dentro de una etiqueta puedes tener atributos, que es
lo que representa el “name” dentro de la etiqueta Team. De
modo que, en este ejemplo:

33
XMLDocument es la clase principal utilizada para trabajar con los
documentos XML. XMLDocument se utiliza para crear nuevos
documentos XML y cargar documentos XML.
<Team name="Seagulls">
Para crear documentos XML, creas una nueva instancia de
XMLDocument y añades los Nodos para las etiquetas que hayas
Team es una etiqueta y tag y name es un atributo.
definido.
La estructura XML es muy específica. Cada etiqueta debe de
tener una etiqueta de cierre, y que es el nombre de la etiqueta Crear un Documento XML
precedida por una barra. La etiqueta de cierre para <Team> es </ Para crear el XML mostrado al inicio de esta sección, creas en
Team>. Es posible tener una etiqueta de una sola línea que tenga primer lugar una instancia XMLDocument:
embebida su propia etiqueta de cierre. Puedes ver esto aquí:

Dim xml As New XMLDocument


<Player name="Bob" position="1B" />
Ahora puedes añadir el nodo superior (denominado root) al
Dado que esta etiqueta finaliza en “/>” se considera que se cierra documento XML:
a sí misma.
Dim root As XMLNode
Los nombres de etiqueta pueden ser cualquier cosa que quieras.
root =
Dado que se trata de tu formato de archivo, defines las etiquetas
xml.AppendChild(xml.CreateElement("League"))
y otros aspectos de su formato.

Existen varias clases disponibles para ayudarte a leer, procesar y


Y ahora puedes añadir el primer equipo al root:
crear archivos XML, incluyendo: XMLDocument, XMLNode,
XMLElement y XMLNodeList.

34
Dim team As XMLNode team = root.AppendChild(xml.CreateElement("Team"))
team.SetAttribute("name", "Pigeons")
team =
player = team.AppendChild(xml.CreateElement("Player"))
root.AppendChild(xml.CreateElement("Team"))
player.SetAttribute("name", "Bill")
player.SetAttribute("position", "1B")

El equipo tiene un atributo que contiene su nombre: player = team.AppendChild(xml.CreateElement("Player"))


player.SetAttribute("name", "Tim")
player.SetAttribute("position", "2B")

team.SetAttribute("name", "Seagulls")
team = root.AppendChild(xml.CreateElement("Team"))
team.SetAttribute("name", "Crows")

Ahora puedes añadir los jugadores del equipo: player = team.AppendChild(xml.CreateElement("Player"))


player.SetAttribute("name", "Ben")
player.SetAttribute("position", "1B")
Dim player As XMLNode
player = player = team.AppendChild(xml.CreateElement("Player"))
team.AppendChild(xml.CreateElement("Player")) player.SetAttribute("name", "Ty")
player.SetAttribute("position", "2B")
player.SetAttribute("name", "Bob")
player.SetAttribute("position", "1B")

Por último, añade el código para guardar el archivo:


player =
team.AppendChild(xml.CreateElement("Player"))
player.SetAttribute("name", "Tom") Dim f As FolderItem
player.SetAttribute("position", "2B")
f = GetSaveFolderItem("", "league.xml")
If f <> Nil Then
xml.SaveXml(f)
Ya has terminado con el primer equipo y sus jugadores. Repite el
End If
código para los siguientes dos equipos:

35
Esto te solicita que guardes el archivo (con el nombre por defecto
de league.xml). Una vez se ha guardado el archivo, puedes abrirlo Dim xml As New XMLDocument
(usando un editor de texto) o la mayoría de los navegadores web Try
para ver el XML. xml.LoadXML(f)
Catch e As XMLException
Cargar un Documento XML MsgBox("XML Error.")
Hablando de argar un archivo XML, he aquí como puedes cargar Return
los contenidos del archivo XML en un TextArea. End Try

En primer lugar, solicita un archivo XML:

Si el archivo seleccionado no es un archivo XML, entonces eleva


Dim f As FolderItem una XMLException. La excepción se recoge y se muestra un
f = GetOpenFolderItem("") mensaje de error.
If f = Nil Then Return
Ahora tienes un archivo XML válido que puedes procesar usando
los métodos y propiedades de las clases XMLDocument y
Ahora tienes un archivo válido, de modo que prueba a abrirlo XMLNode classes.
como archivo XML:
Este código tiene tres partes. La primera parte verifica que el
archivo XML tiene un nodo root llamado “League”. La segunda
parte es un bucle que procesa cada equipo. Y la tercera parte es
un bucle interno que procesa cada jugador de cada equipo. Los
datos XML se muestran en una TextArea.

36
archivo XML frente un archivo de texto plano es que los XML
hacen que resulte más sencillo actualizar el formato de archivo.

Por ejemplo, si quieres actualizar el formato para añadir un


atributo “entrenador” en la etiqueta Team, puedes hacerlo
Dim root As XmlNode
root = xml.FirstChild cuando se guarde el archivo y puedes modificar cualquier código
de carga para que sólo procese el atributo de “entrenador” en el
If root.Name = "League" Then
caso de que exista.
Dim team, player As XmlNode
team = root.FirstChild
En el código de guardar, añade un nuevo atributo detrás de cada
While team <> Nil nodo Team, como en el ejemplo:
XMLArea.AppendText(team.GetAttribute("name") +
EndOfLine)
player = team.FirstChild team.SetAttribute("coach", "Coach Mark")
While player <> Nil
XMLArea.AppendText("--->" +
player.GetAttribute("name") + " " +
player.GetAttribute("position") + EndOfLine) Con este cambio, crea un nuevo archivo XML y cárgalo usando el
player = player.NextSibling código de carga ampliado. El archivo XML se carga
Wend
correctamente, pero se ignora el nuevo atributo de entrenador.
team = team.NextSibling
Wend Has ampliado tu formato de archivo XML sin romper su
Else capacidad de ser cargado.
MsgBox("Not a League XML file.")
End If Por supuesto, querrás actualizar el código de carga de modo que
pueda mostrar el entrenado. Para ello sólo has de añadir otra
línea encargada de obtener el atributo de entrenado después de
Ampliar un Formato de Archivo XML procesar el atributo correspondiente al nombre del equipo:
XML es un gran formato de archivo cuyo uso puedes considerar
en vez de los archivos de texto planos. Una ventaja del uso de un

37
XMLArea.AppendText(team.GetAttribute("name") +
EndOfLine)
XMLArea.AppendText(team.GetAttribute("coach") +
EndOfLine)

38
Section 6

JSON

JSON (JavaScript Object Notation) es un formato ligero de


{
intercambio de datos. Resulta fácil de leer y de escribir para los "Seagulls":{
"players":{
humanos. Es fácil de parsear y generar para las máquinas. JSON "Bob":{
no es tan verboso como el XML y se utiliza con frecuencia para "position":"1B"
},
las comunicaciones de datos en Internet y la web dado a que los "Tom":{
"position":"2B"
archivos de datos son más pequeños. }
}
},
Puedes crear y modificar datos JSON usando la clase JSONItem "Pigeons":{
"players":{
class. Con la clase JSONItem puedes gestionar los datos del "Bill":{
mismo modo que haces con los Diccionarios y los Arrays. "position":"1B"
},
"Tim":{
He aquí un ejemplo de un documento JSON que describe tres "position":"2B"
}
equipos de una liga de fútbol fictícia: }
},
"Crows":{
"players":{
"Ben":{
"position":"1B"
},
"Ty":{
"position":"2B"
}
}
}
}

39
Este ejemplo JSON tiene una gran cantidad de espacio en blanco
para hacer que resulte más sencilla su lectura. Sin todos los team = New JSONItem
espacios en blanco, el JSON es mucho más pequeño: players = New JSONItem

player = New JSONItem


{"Seagulls":{"players":{"Bob":{"position
player.Value("position") = "1B"
":"1B"},"Tom":{"position":"2B"}}},"Pigeo
players.Value("Bob") = player
ns":{"players":{"Bill":{"position":"1B"}
,"Tim":{"position":"2B"}}},"Crows":{"pla
player = New JSONItem
yers":{"Ben":{"position":"1B"},"Ty":{"po
player.Value("position") = "2B"
sition":"2B"}}}}
players.Value("Tom") = player

team.Value("players") = players
Crear Datos JSON league.Value("Seagulls") = team
Para crear los datos JSON mostrados al inicio de esta sección
debes crear en primer lugar un objeto JSONItem que contenga
todo: Haz lo mismo para los otros dos equipos:

Dim league As New JSONItem


Dim team, players, player As JSONItem

Ahora puedes poblar la información de cada equipo. Así es como


completas el primer equipo:

40
team = New JSONItem team = New JSONItem
players = New JSONItem players = New JSONItem

player = New JSONItem player = New JSONItem


player.Value("position") = "1B" player.Value("position") = "1B"
players.Value("Bill") = player players.Value("Ben") = player

player = New JSONItem player = New JSONItem


player.Value("position") = "2B" player.Value("position") = "2B"
players.Value("Tim") = player players.Value("Ty") = player

team.Value("players") = players team.Value("players") = players


league.Value("Pigeons") = team league.Value("Crows") = team

Ahora ya se ha creado toda la información JSON. Puedes


convertirlo a una cadena para guardarlo (usando un
TextOutputStream) o para mostrarlo. Este código muestra los
datos JSON en una Text Area:

JSONArea.Text = league.ToString

Cargar Datos JSON


Los datos JSON también pueden ser cargados y parseados
usando la clase JSONItem.
41
Cuando tienes los datos JSON como una string, puedes
convertirlos a una clase JSONItem simplemente:

Dim jsonData As String = JSONArea.Text


Dim league As New JSONItem
league.Load(jsonData)

Con los datos en un JSONItem, ahora puedes parsearlos para


mostrarlo.

Puedes pasar en bucle por todos los equipos, mostrándolos y


mostrando a continuación los jugadores de cada uno de ellos.

42
Section 7

Conjuntos de Tipos de Archivo

Hay muchos tipos de archivos diferentes. El tipo de un archivo En vez de escribir code que trate directamente con todos estos
define el tipo único de datos almacenados en dicho archivo. Por ripos de archivo, códigos de creador u sufijos de archivo, Xojo te
ejemplo, un archivo de texto almacena texto, mientras que un abstrae a ti y a tu código de todo ello mediante los tipos de
archivo PNG almacena imágenes. Los archivos tienen archivo. Un tipo de archivo es un ítem almacenado con tu
extensiones de archivo (o sufijos) que identifican el tipo de proyecto y que representa un tipo de archivo concreto, un
archivo. Por ejemplo, un archivo de Texto tiene una extensión creador y una o más extensiones. Cada tipo de archivo tiene un
“txt”. Un archivo con el nombre “misNotas.txt” es reconocido nombre que se utiliza desde el código durante la apertura y la
como archivo de Texto. creación de archivos. Esto te permite trabajar con nombres de tu
elección y que te resulten fáciles de recordar en vez de utilizar
El tipo de archivo facilita que las aplicaciones sepan si pueden
códigos crípticos. También abstrae a tu código del sistema
trabajar con un tipo de archivo en particular. Por ejemplo,
operativo, haciendo que resulte más sencillo para ti crear
cualquier aplicación que pueda abrir archivos de texto espera
versiones de tu aplicación para otros sistemas operativos.
que el tipo de archivo de cualquier archivo de texto que deba de
abrir sea “TEXT”. Este tipo de archivo indica a la aplicación que El Editor de Conjuntos de Tipos de Archivos o la clase FileType
se trata de un archivo de texto estándar. Los archivos PNG se utilizan para crear Tipos de Archivos. El Editor de Conjuntos
tienen dicho nombre dado que “PNG” es el tipo de archivo de un de Tipos de Archivos está descrito en la siguiente sección y la
archivo PNG. Las aplicaciones también son archivos, pero todas clase FileType está descrita en la Referencia del Lenguaje. Los
las aplicaciones tienen un tipo de archivo “APPL” que indica al atributos que das a los tipos de archivo en el editor se mapean
Mac OS que este archivo es un ejecutable y no simplemente directamente con las propiedades de los objetos de la clase
datos. FileType.

43
El Edito de Conjuntos de Tipos de Archivo Cuando utilices constantes dinámicas, los nombres se localizarán
Utilizas el Editor de Conjuntos de Tipo de Archivo para crear los automáticamente en Mac OS X. En otras plataformas, el uso de
elementos que representan los diferentes tipos de archivos que FileTypeSet.MyFileType.Name devolverá el valor de la constante
deseas que tu aplicación pueda abrir o crear. Para añadir tipos de dinámica, lo que te permite registrar/actualizar tu tipo de archivo
archivo a tu proyecto, has de añadir en primer lugar un Conjunto con el sistema utilizando el nombre localizado.
de Tipo de Archivo a tu proyecto: selecciona Insertar ↠ Conjunto
• Nombre de Objeto: El nombre utilizado en el código de tu
de Tipo de Archivo. Se añadirá un elemento denominado aplicación para identificar el tipo de archivo. Esta es la cadena
“FileTypes1” a tu proyecto y se mostrará en el Navegador, que puedes pasar al método para indicarle que use dicho tipo
quedando seleccionado de forma automática para mostrar el de archivo.
Editor de Conjuntos de Tipos de Archivo.
• MacType y MacCreator: Los códigos Macintosh Type y Creator
La barra de herramientas del Conjunto de Tipos de Archivo tiene utilizados por el sistema operativo Macintosh originalmente
estos elementos: Añadir Tipo de Archivo, Añadir Tipo de Archivo para identificar a los archivos. Si vas a asignar iconos
Común y Eliminar Tipo de Archivo. Normalmente utilizarás el personalizados al tipo de archivo, asegúrate de que el código
botón Añadir Tipo de Archivo Común. Este te permite seleccionar de Creator concuerda con el código Creator proporcionado a tu
de un listado un tipo de archivo usado comunmente. aplicación. Estos ajustes ya no son relevantes en las actuales

Por el contrario, el botón Añadir Conjunto de Tipo de Archivo versiones del OS X.


Figura 1.2
crea una entrada de tipo de archivo en blanco en el editor, y te
• Extensiones: Las
permite definir el tipo de archivo. Es la mejor opción para definir extensiones de archivo
tipos de archivo personalizados que sean únicos a tu aplicación. usandas en OS X,

El cuerpo del editor contiene columnas para los siguientes Windows, y Linux para

atributos del tipo de archivo: identificar los tipos de


archivo. Puedes indicar
• Nombre Mostrado: El nombre mostrado a los usuarios en los más de una extensión
cuadros de diálogo de apertura de archivo. Puedes utilizar un por tipo de archivo.
literal de tipo string o bien una constante. Separa las múltiples
44
extensiones con el uso de punto y coma. Si necesitas añadir o eliminar tipos de archivos de imagen,
puedes modificar simplemente el Conjunto de Tipos de Archivo y
• UTI: El Identificador de Tipo Uniforme (Uniform Type Identifier).
esta línea de código se referirá automáticament a los nuevos
Usado en OS X para identificar los tipos de archivo. Fue
miembros del conjunto.
desarrollado como sustituto del OSType (códigos de tipo y
creador). Introducido en Mac OS X 10.6 Snow Leopard, fue Añadir un Tipo de Archivo Común
diseñado para eliminar los problemas con el sistema de los Para añadir un tipo de archivo común, haz esto:
códigos de File Type y Creator. Para obtener más información
sobre los UTI de Apple, consulta http://developer.apple.com/ 1. Haz clic en el botón Añadir Tipo de Archivo Común en el
macosx/uniformtypeidentifiers.html. Editor de Conjuntos de Tipo de Archivos para mostrar un
listado de los tipos de archivo comunes. Selecciona el que
• Icon: El documento de icono para el tipo de archivo. Cuando el
quieras.
usuario hace doble clic sobre dicho icono tu aplicación se
ejecutará y abrirá dicho archivo. En el lenguaje, un conjunto de 2. Haz clic en “Más...” en el listado para acceder a Tipos de
Tipo de Archivo tiene una propiedad de tipo string “All” que Archivo adicionales. Esto abre una área en la parte inferior del
devuelve todos los tipos de su conjunto. Puedes utilizar All en Editor de Conjuntos de Tipos de Archivo donde puedes
tu código cuando quieras referirte a todos los tipos de archivo seleccionar más tipos:
del conjunto.

Supongamos que creas un Conjunto de Tipo de archivo 3. Una vez que hayas elegido el Tipo de Archivo, este aparecerá
denominado ImageTypes en los que especificas todos los tipos en el Editor de Conjuntos de Tipos de Archivo. Puedes hacer
de imágenes válidos que tu aplicación puede abrir. Puedes clic sobre cualquiera de los valores para cambiarlos en caso
indicar toda la lista de tipos de imágenes con una línea como: necesario.

Añadir un Tipo de Archivo Personalizado


f = GetOpenFolderItem(ImageTypes.All)
La mayoría de las aplicaciones crean archivos y les asignan
iconos personalizados. Estos iconos tienen por lo general un
aspecto similar al del icono personalizado de la aplicación. Esto

45
hace que resulte más sencillo para el usuario reconocer qué Es mejor proporcionar iconos y máscaras para cada uno de los
archivos han sido creados con una aplicación en concreto. tamaños soportados. Actualmente sólo OS X soporta un tamaño
Cualquier icono personalizado que añadas sólo aparecerá si has de icono de 512x512.
asignado el código Creator a tu proyecto y creas una aplicación
Nota: Si generas una aplicación OS X en Windows o Linux, los iconos de
independiente. 512 x 512 y de 256 x 256 no se incluirán dado que Windows y Linux no
ofrecen el soporte JPEG2000 necesario.
Si estás definiendo un tipo de archivo propio o no ves el tipo de
Los iconos de documento se utilizan cuando tu aplicación guarda
archivo deseado en el listado de Tipos de Archivo Comunes,
los documentos en ese tipo de archivo. Al hacer doble clic sobre
puedes añadirlo manualmente.
el icono de documento se ejecutará la aplicación.
Para añadir un Tipo de Archivo Personalizado manualmente, haz
esto: Usar los Tipos de Archivo
Pasas uno o más tipos de archivo a los comandos para indicar
1. Haz clic en el botón Añadir Tipo de Archivo en el Editor de que sólo los tipos de archivo pasados son los apropiados. Por
Conjuntos de Tipos de Archivo . Esto añade una fila ejemplo, la siguiente instrucción en el manejador de evento Open
vacía al editor. de un control indica que puede aceptar archivos TEXT que hayan
sido arrastrados desde el Escritorio:
2. Introduce los valores para el Tipo de Archivo.
Para introducir más de una extensión de archivo, sepáralas Me.AcceptFileDrop("text")
con puntos y coma.

Añadir un Icono de Documento Esta instrucción muestra un cuadro de diálogo de abrir archivo
Puedes definir un icono de documento personalizado para cada que permite al usuario ver y abrir sólo las películas QuickTime:
tipo de archivo que la aplicación pueda abrir y guardar. Puedes
importar, pegar o arrastrar iconos en el editor. También puedes f = GetOpenFolderItem("video/quicktime")
arrastrar iconos de un tamaño a otro y ajustarán
automáticamente su escala.
La clase FileType tiene un operador de conversión de cadena
incorporado. Esto te permite referirte al tipo de archivo por su
Nombre de Objeto y devolverá la cadena apropiada.

46
Por ejemplo, esto se refiere al tipo de archivo con el ObjectName También puedes indicar más de un tipo de archivo aceptable
de “JPEG” en el Conjunto de Tipos de Archivo “ImageTypes”: utilizando sólo sus Nombres de Objeto. Separa los tipos de
archico mediante punto y coma. Por ejemplo, la siguiente línea
permite que el usuario vea y abra los archivos PNG, GIF y JPEG:
f = GetOpenFolderItem(ImageTypes.JPEG)

f = GetOpenFolderItem("image/png;image/
También puedes concatenar dos tipos de archivo usando el
jpeg;image/gif")
operador “+” para indicar dos tipos de archivo.

Por ejemplo, esto se refiere tanto a los tipos de archivo “JPEG”


como “PNG” en el conjunto ImageTypes:

f = GetOpenFolderItem(ImageTypes.JPEG +
ImageTypes.Png)

El modo más sencillo de indicar varios tipos de archivo es


poniendo todos los tipos de archivo a los que quieras referirte en
un Conjunto de Tipos de Archivo y utilizar a continuación el
método All de la clase Conjunto de Tipos de Archivo. Esto
devuelve automáticamente todos los tipos de archivo
concatenados. Por ejemplo, lo siguiente devuelve todos los tipos
de archivo en el Conjunto de Tipos de Archivo ImageTypes:

f = GetOpenFolderItem(ImageTypes.All)

47
Section 8

Volúmenes Virtuales

Un Volumen Virtual es un objeto de archivo especial que actúa CreateVirtualVolume devuelve Nil si el Volumen Virtual no se
como un contenedor para otros archivos. Estepermite tener un puede crear.
único archivo que está compuesto por múltiples archivos.
Una vez que tienes un Volumen Virtual, puedes acceder o añadir
Los Volúmenes Virtuales se manipulan mediante FolderItems y archivos a éste mediante el uso de FolderItems.
soportan la lectura y escritura de archivos de Texto y Binarios
Acceder a Datos en Volúmenes Virtuales
Los Volúmenes Virtuales pueden utilizarse para crear de una Ampliando el anterior ejemplo, puedes añadir un archivo de texto
forma sencilla formatos de archivo que deban de contener a al Volumen Virtual creando un FolderItem en él y escribiendo a
otros archivos. contiunuación texto sobre éste mediante un TextOutputStream:

Crear Volúmenes Virtuales


Creas un Volumen Virtual usando el método CreateVirtualVolume If vv <> Nil Then
de un FolderItem. Dim testFile As FolderItem
testFile = vv.Root.Child("Test.txt")
Dim output As TextOutputStream
Dim vFile As FolderItem output = TextOutputStream.Create(testFile)
vFile = GetFolderItem(“TestVolume.vv”) output.Write("Hello, world!")
Dim vv As VirtualVolume output.Close
End If
vv = vFile.CreateVirtualVolume

48
Section 1

Comparación de Texto

Todas las comparaciones de texto no diferencian por omisión StrComp utiliza tres parámetros (string1, string2 y modo) y
entre mayúsculas y minúscilas. Esto significa que el texto como devuelve un resultado entero que indica el resultado de la
“Hola”, “HOLA”, “hola” y “hOLA” son tratados del mismo modo comparación.
cuando los comparas utilizando los operadores de comparación
(=, <, >, <>, <=, >=). If StrComp("Hello”, "hello", 0) = 0 Then
// Not displayed
MsgBox("They match!") // Displayed
If "Hello" = "hello" Then End If
MsgBox("They match!") // Displayed
End If If StrComp("Hello”, "hello", 0) <> 0 Then
// Displayed
MsgBox("The do not match.")
If "Hello" <> "hello" Then
End If
// Not displayed
MsgBox("They do not match.")
End If El parámetro modo indica el tipo de comparación a realizar.
Cuando modo = 0, la comparación se realiza utilizando
estrictamente los bytes de la cadena. Esto significa que si la
Si necesitas realizar comparaciones de texto que sean sensibles
cadena tiene dos codificaciones diferentes, siempre serán
al uso de mayúsculas y minúsculas, utiliza entonces la función
diferentes. En la mayoría de los casos querrás utilizar mode = 0.
StrComp para evaluar los valores de texto.
Cuando mode = 1, las cadenas se comparan solo para la
diferencia de caja en el caso de que concuerden exactamente a

50
excepción de su caja. Por ejemplo “Hola” y “HOLA” deberían ser
cadenas diferentes dado que son idénticas excepto por su caja.
Sin embargo, no significa que el texto como “hola” y “Hoy” no se
comparen exactamente tal y como esperarías. En sentido
estricto, “hola” es mayor que “Hoy” dado que la “h” tiene un
código de caracter superior al de “H”. Pero con mode = 1, el
dicho detalle no se tiene en cuenta dado que las dos cadenas no
son la misma.

51
Section 2

Texto Seleccionado

El término “Texto Seleccionado” se refiere al texto que está Figure 2.1


seleccionado (o “destacado”) en los Text Fields y Text Areas que
tienen el foco. Los Text Field y las Text Area tienen tres
propiedades que pueden utilizarse para obtner y/o definir el texto
seleccionado. Los Text Field y las Text Areas tiene un método,
SelectAll, que realiza la misma funcioón que el elemento de
menú Edición ↠ Seleccionar Todo. Una llamada a SelectAll
seleccionar todo el texto del campo.

Nota: El texto seleccionado solo funciona con las aplicaciones de


escritorio.

Si necesitas ejecutar algún código cuando el usuario mueva el


punto de iserción o resalte algunos caracteres, sitúa tu código en
el manejador de evento SelChange del Text Field o de la Text
Area.

Se utilizan las siguientes propiedades para gestionar el texto


seleccionado en los Text Fields y las Text Areas.

52
Section 3

Fuentes

Tienes la capacidad de definir la fuente, tamaño de fuente y Si quieres que el texto se muestre o imprima utilizando la fuente
estilo de fuente en la mayoría de los objetos y controles de tu System del
aplicación. Las Text Areas soportan el uso de múltiples fuentes, usuario, utiliza el Figura 2.1
estilos y tamaños (referidos en conjunto como texto con estilo), y nombre
las List Boxes soportan múltiples estilos. Los controles de “System” como
escritorio que utilizan una única fuente tienen una propiedad la fuente en la
TextFont que puedes definir asignando el nombre de la fuente asignación.
que quieres utilizar para mostrar el texto en dicho control. Puedes introducirla como la propiedad
Adicionalmente, varios controles también tienen casillas de TextFont en el panel de Propiedades. Si Figura 2.2
verificación para indicar si la fuente debería ser Negrita, Cursiva también defines cero como el TextSize,
o Subrayada. entonces se utilizará el tamaño de fuente
que funcione mejor en la plataforma
Las Text Areas tienen una propiedad TextFont pero también
sobre la que se esté utilizando la
pueden mostrar múltiples fuentes. Para obtener más información
aplicación. Debido a las diferencias en la
sobre esto, dirígete a la siguiente sección sobre Texto con Estilo.
resolución en pantalla, es frecuente que
se requieran diferentes tamaños de
Fuentes System y SmallSystem
La fuente System es la utilizada por el software del sistema fuente en función del sistema operativo de la plataforma. Esta

como su fuente por defecto. También es la fuente utilizada en los característica te permite utilizar diferentes tamaños de fuente

menús. Esta fuente varía en función del sistema operativo y de sobre diferentes plataformas sin tener que crear diferentes

las preferencias del usuario. ventanas para cada plataforma. Utiliza el Inspector para definir el
TextFont a “System” y el TextSize a cero.

53
Si el software del sistema soporta tanto una fuente pequeña
como grande para System, también puedes indicar la fuente Function FontAvailable(FontName as
“SmallSystem” como la fuente en TextFont. Esta opción String) As Boolean
selecciona la fuente pequeña del sistema en el ordenador del Dim i, nFonts As Integer
usuario, en el caso de que exista alguna. Si no existe una fuente nFonts = FontCount-1
pequeña del sistema, se utiliza la fuente System.
For i = 0 to nFonts
En OS X están soportadas las fuentes System y Small System. La
If Font(i) = FontName Then
Figura 2.3 ilustra la diferencia entre las dos fuentes. El TextSize es
Return True
cero en ambos casos.
End If
Next
Fuentes Disponibles
Es probable que quieras utilizar otras fuentes diferentes a la
fuente del sistema. En este caso tendrás que determinar si está Return False
instalada una fuente concreta en el ordenador del usuario. End Function
Existen dos funciones globales que facilitan realizar dicha
comprobación: FontCount y Font.
Crear un Menú de Fuente Dinámicamente
La siguiente función devuelve True o False cuando se pasa el Supongamos que quieres crear un menú Fuentes que muestre
nombre de una fuente para informarte de si está instalada la todas las fuentes en el ordenador del usuario. No sabes por
fuente: adelantado cuáles son las fuentes instaladas, de modo que
precissa crear los menuitems dinámicamente durante el inicio.

Para ello, creas una instancia de la clase MenuItem y la instancias


para cada fuente. El evento Action de la instancia de la clase se
encarga de manejar la selección del menú. Para obtener más
detalles sobre esta técnica dirígete a la Guía de la Interfaz del

54
Usuario. El capítulo Escritorio tiene una sección Menú que
muestra como hacerlo.

Fuentes Cocoa
En las aplicaciones Cocoa de OS X no todas las fuentes
aparecen como Negrita, Cursiva o Subrayada incluso en el caso
de que estén seleccionadas dichas propiedades. Dado al modo
en el que Cocoa dibuja las fuentes, los estilos Negrita, Cursiva y
Subrayado solo se muestran para las fuentes que lo incluyan
como parte de su definición de fuente. Dichos ajustes no pueden
aplicarse a cualquiera de las fuentes tal y como sí es posible
sobre otras plataformas.

Si no estás viendo el ajuste de fuente que esperabas, asegúrate


de verificar que la fuente lo soporte, algo que puedes hacer
usando TextEdit.

55
Section 4

Texto con Estilo

El término texto con estilo se refiere al texto que puede tener


más de una fuente, tamaño de fuente y/o estilo de fuente. El Function Fonts(item As TextArea) As String
Dim fonts, theFont As String
control de escritorio Text Area soporta el texto con estilo. Para Dim i, Start, Length As Integer
que una Text Area soporte el texto con estilo debe de activarse If item.SelTextFont = "" Then
su propiedad MultiLine, así como la propiedad Styled (en ambos Start = item.SelStart
Length = item.SelLength
casos, puestas a ON). Ambas opciones están definidas así por For i = Start To Start + Length
defecto. Para imprimir texto con estilo, debes de utilizar la clase Field.SelStart = i
StyledTextPrinter, característica que se cubre en el capítulos Field.SelLength = 1
If InStr(fonts, Field.SelTextFont) = 0 Then
sobre Impresión e Informes. If fonts = "" Then
fonts = Field.SelTextFont
Determinar Fuente, Tamaño y Estilo de Texto Else
Las TextAreas tienen propiedades que facilitan determinar la fonts = fonts + ", " + Field.SelTextFont
End If
fuente, tamaño de fuente y el estilo del texto seleccionado.
End If
Puede utilizarse la propiedad SelTextFont para determinar la Next
fuente del texto seleccionado. Si el texto seleccionado tiene sólo Return fonts
Else
una fuente entonces la propiedad SelTextFont contiene el
Return Field.SelTextFont
nombre de dicha fuente. Si el texto seleccionado utiliza más de End If
una fuente, entonces la propiedad SelTextFont está vacía. End Function

Esta función devuelve los nombres de las fuentes para el texto


seleccionado de la TextArea pasada: Se utiliza la propiedad SelTextSize para determinar el tamaño del
texto seleccionado y funciona del mismo modo con la propiedad

56
SelTextFont. Si todos los caracteres del texto seleccionado tienen En este ejemplo, si el texto seleccionado de la TextArea es
el mismo tamaño de fuente, entonces la propiedad SelTextSize negrita, entonces se marca el item de menú Negrita:
contendrá dicho tamaño. En el caso de que se utilicen diferentes
tamaños, entonces la propiedad SelTextSize será 0. StyleBold.Checked = TextArea1.SelBold

También hay propiedades booleanas para determinar si todos los


caracteres en el texto seleccionado usan el mismo estilo de Si todos los caracteres de un texto seleccionado no están en
fuente. Dado que el texto puede tener aplicados varios estilos, negrita entonces TextArea1.SelBold devuelve False y que será el
estas propiedades determinan si todos los caracteres del texto valor asignado a la propiedad Checked del ítem de menú
seleccionado tienen un estilo de fuente concreto aplicado sobre StyleBold
ellos. Por ejemplo, si todos los caracteres en el texto
Defi nir la Fuente, Tamaño, y Estilo del Texto
seleccionado son negrita pero algunos también son cursiva, una
comprobación de negrita devolverá True. Por otra parte, una Las propiedades usadas para comprobar la fuente, tamaño de
comprobación una comprobación de cursiva devolverá False fuente y estilos de fuente del texto seleccionado también son las
dado que parte del texto seleccionado no incluye el estilo de utilizadas para definir dichos valores. Una TextArea puede
fuente cursiva. Si devuelve False, soportar múltiples fuentes, tamaños de fuente y estilos. Un
el texto seleccionado contiene más Figura 2.3 TextField puede soportar una fuente, un tamaño de fuente y el
de un estilo de fuente. Si quieres estilo plano. Un TextField también puede soportar los estilos
determinar qué estilo son los Negrita, Subrayado y Cursiva para todo el texto del TextField.
utilizados, puedes seleccionar
Por ejemplo, para definir la fuente del texto seleccionado a
mediante código cada caracter del
Helvetica, debes hacer lo siguiente:
texto seleccionado y comprobar
las propiedades de estilo. Esta es
una operación similar a la función TextArea1.SelTextFont = "Helvetica"
Font de ejemplo en la que se
determina qué fuentes son las utilizadas en el texto seleccionado.

57
Al definir fuentes recuerda que la fuente debe de estar instalada
Figura 2.4
en el ordenador del usuario o la asignación no tendrá efecto.
Puedes utilizar la función FontAvailable mencionada
anteriormente en este capítulo para determinar si está instalada
una fuente en concreto.

Puedes definir la propiedad TextSize de un control a cero para


que tu aplicaión utilice el tamaño de fuente que se vea mejor
sobre la plataforma en la que se esté ejecutando la aplicación.
Puedes definir el tamaño de fuente del texto seleccionado
utilizando la propiedad SelTextSize. Por ejemplo, el siguiente Las TextAreas también incorporan métodos para activar y
código define el tamaño de fuente de TextArea1 a 12 puntos: desactivar los estilos de las fuentes. “Conmutar” en este caso
significa aplicar el estilo si parte del texto no tiene aun aplicado
dicho estilo, o bien eliminar el estilo del texto seleccionado en el
TextArea1.SelTextSize = 12
que se encuentre aplicado. El siguiente código conmuta el estilo
negrita del texto seleccionado en TextArea1:
Para aplicar un estilo de fuente en particular sobre el texto
seleccionado, define la propiedad de estilo correspondiente a TextArea1.ToggleSelectionBold
True. Por ejemplo, el siguiente código aplica el estilo Negrita al
texto seleccionado en TextArea1:
Objetos de Texto con Estilo
Cuando estás trabajando con texto con estilo que se muestra en
TextArea1.SelBold = True
una TextArea puedes trabajar con las propiedades de las
TextAreas que definen y obtienen los atributos de texto tal y
como se ha descrito en la anterior sección para manejar el texto
con estilo. Sin embargo, también hay clases para abrir, guardar y
gestionar el texto con estilo independientemente de la TextArea o
58
Figura 2.5 Cada método utiliza parámetros para la posición de inicio y la
cantidad de texto sobre la que se aplica el atributo. Dichos
números están basados en cero. Una llamada a Bold sería así:

cualquier otro control. De hecho, el texto con estilo no tiene ni


por qué ser mostrado en absoluto. Este conjunto de técnicas
utilizan las propiedades y métodos de la clase StyledText. Su
propiedad Text contiene el texto con estilo que está gestionado
por el objeto StyledText.

NOTA: El objeto Style no está soportado en las aplicaciones web. Puedes Dim st As New StyledText
utilizar un objeto Style para crear hojas de estilo personalizadas para aplicar st.Text = "How now Brown Cow."
estilo sobre controles, incluyendo texto. Aunque la Hoja de Estilo es un
concepto relacionado, no es idéntico. Una Hoja de Estilo no puede aplicar st.Bold(0, 3) = True
estilo arbitrario como puede hacerse con el objeto Style explicado en esta
sección. Consulta la clase WebStyle de la Referencia del Lenguaje para
obtener más información sobre las hojas de estilo web.

59
Esto define la primera palabra del texto, “How” a negrita. Cada StyleRuns. La clase StyledText tiene seis métodos para gestionar
conjunto contiguo de caracteres que tenga el mismo grupo de los StyleRuns.
atributos de estilo compone un objeto StyleRun. En este ejemplo,
El método Texto de un objeto StyledText puede tener múltiples
párrafos. Un párrafo es el texto entre dos caracteres de fin de
línea. Un párrafo puede definirse tanto con la función EndOfLinbe
o el caracter de fin de linea de la plataforma sobre la que se esté
ejecutando la aplicación.

Un párrafo puede estar compuesto por múltiples StyleRuns. Sólo


tiene una propiedad de estilo propia, la alineación de párrafo
(Izquierda, Centrado o Derecha).

Aunque puedes trabajar con un objeto StyledText por completo


desde código — sin llegar a mostrarlo nunca — el control
TextArea está “vinculado” con la clase StyledText en el sendipo
de que puedes acceder a todos los métodos y propiedades de la
clase StyledText mediante la propiedad StyledText de una
TextArea.

Para trabajar con un objeto StyledText en una TextArea, debes de


activar las propiedades MultiLine y Styled de la TextArea. Puedes
hacer esta operación desde el Inspector.

Supongamos que el TextArea con estilo tiene el texto que quieres


los tres primeros caraacteres forman un objeto StyleRun. El texto manipular usando la clase StyledText. El siguiente código carga el
restante es el segundo StyleRun. En el lenguaje de un procesador texto en el objeto StyledText.
de textos, cada StyleRun es una instancia de un estilo de
caracter. La propiedad Text está compuesta de una serie de
60
Dim st As New StyledText Dim st, ln As Integer
Dim Text As String
st = TextArea1.StyledText
Text = "Here is my styled text." + EndOfLine + "Aren’t you
TextArea1.AppendText("This is the really impressed?"
appended text.") TextArea1.StyledText.Text = Text

st.Bold(0, 4) = True
// Assign Font and Size to entire text
TextArea1.StyledText.Font(0, Len(Text)) = "Arial"
TextArea1.StyledText.Size(0, Len(Text)) = 14
El objeto StyledText es en realidad un alias al texto de la
TextArea, no una copia estática. Esto significa que la tercera línea // Apply character highlights to 'my' in first paragraph
TextArea1.StyledText.Bold(8, 2) = True
de código cambia los contenidos de la TextArea y la última línea
TextArea1.StyledText.TextColor(8, 2) = &cFF0000
define los cuatro primeros caracteres de la TextArea a negrita.
// Get positions of second paragraph (0-based)
En el siguiente ejemplo, este código define la propiedad Text del st = TextArea1.StyledText.Paragraph(1).StartPos-1
objeto StyledText y lo muestra en la TextArea: ln = TextArea1.StyledText.Paragraph(1).Length

// Second paragraph in Bold


TextArea1.StyledText.Bold(st, ln) = True
TextArea1.StyledText.Text = "Here is
my styled text." + EndOfLine _ // Second paragraph Centered
+ "Aren’t you really impressed?" TextArea1.StyledText.ParagraphAlignment(1) =
Paragraph.AlignCenter

A partir de este punto puedes asignar propiedades de estilo al


texto. Los cambios reformatean los contenidos de la TextArea.
Este es un ejemplo simple que funciona con estos dos párrafos:

61
Este ejemplo funciona con el objeto StyledText “vinculado” a la
TextArea, pero también puedes trabajr con el texto con estilo
“offline”. Declaras un objeto StyledText en una instrucción Dim y
operas sobre él sin hacer referencia a ningún control. Cuando
estés listo para mostrarlo, lo asignas a la propiedad de una
TextArea.

También puedes hacerlo con una línea como:

Figura 2.6

Dim st As New StyledText


//do whatever you want right here;
when you’re done, just write...
TextArea1.StyledText = st

También puedes exportar el texto con estilo como una secuencia


de StyleRuns y leerlos posteriormente para reconstruir un objeto
StyledText mediante el uso del método AppendStyleRun.
Consulta las entradas sobre StyleRun y StyledText en la
Referencia del Lenguaje para obtener más información.
62
Section 5

Codifi caciones

Todos los ordenadores utilizan sistemas de codificación para por los problemas relacionados con las codificaciones, dado que
almacenar las cadenas de caracteres como series de bytes. La la codificación utilizada se almacena junto con el contenido de la
codificación más antigua y familiar es la codificación ASCII. Está cadena.
documentado en la Referencia del Lenguaje. Define los códigos
Nota: Las cadenas en las Estructuras no contienen la información de
de caracteres para los valores de 0 a 127. Dichos valores codificación.
incluyen sólo las mayúsculas y minúsculas del alfabeto inglés, Si estás creando aplicaciones que abren, crean o modifican los
los números, algunos símbolos y códigos de control invisibles archivos de texto creados por otras aplicaciones, entonces
utilizados en los primeros ordenadores. Puedes utilizar la función debes de comprender cómo funcionan las codificaciones de
Chr para obtener el caracter que se corresponde con un código texto y qué cambios has de realizar para que el código pueda
ASCII en particular. trabajar correctamente.

Se han ido implementando varias extensiones sobre el ASCII


Codificaciones de Texo: de ASCII a Unicode
capaces de manejar símbolos adicionales, caracteres
Como sabes, los ordenadores no entienden realmente de
acentuados, alfabetos no románicos, etc. En particular, la
caracteres. Estos almacenan cada caracter como un código
codificación Unicode está diseñada para gestionar cualquier
numérico. Por ejemplo, el Retorno es el caracter ASCII número
lenguaje y mezcla de lenguajes en la misma cadena. Tus
13. Cuando la industria de la computación estaba en su infancia,
aplicaciones pueden soportar dos formatos Unicode diferentes,
cada fabricante de ordenadores utilizaba su propio esquema de
UTF-8 y UTF-16. Todas las constantes, literales de cadena, etc.,
numeración. Un esquema de numeración se denomina a veces
se almacenan internamente utilizando la codificación UTF-8
conjunto de caracteres. Es una relación entre letras, números,
Si las cadenas con las que trabajan se crean, guardan y leen en símbolos y códigos invisibles (como el retorno de carro o avance
el dominio de tus aplicaciones, entonces no has de preocuparte de línea) y números. Con un conjunto de caracteres puede

63
intercambiarse la información entre los ordenadores creados por estaban relacionadas entre sí. Las aplicaciones multiplataforma
diferentes fabricantes. debían de crear algún modo de gestionar los textos que utilizasen
los caracteres comprendidos en el rango entre 128 y 255.
En 1963 la American Standars Association (que posteriormente
cambió su nombre a American National Standards Institute) El problema es incluso peor para los usuarios de lenguajes que
anunció el American Standard Code for Information Interchange no utilizan los caracteres del alfabeto Románico, como el
(ASCII), y que estaba basado en el conjunto de caracteres japonés, chino o hebréo. Dado que hay tantos caracters, los
disponible en cualquier máquina de escribir en inglés. juegos de caracteres dirigidos a soportar dichos idiomas
utilizaban dos bytes de datos por caracter (en vez de un único
Con el paso de los años, los ordenadores fueron cada vez más
byte por caracter, como ocurre en el ASCII).
populares fuera de los Estados Unidos, y el ASCII comenzó a
mostrar sus puntos débiles. El conjunto de caracteres ASCII Apple creó eventualmente varias codificaciones de texto para
define únicamente sólo 128 caracteres. Esto cubre lo que está facilitar la gestión de datos. MacRoman es una codificación de
disponible en una máquina de escribir en inglés, además de texto para los archivos que utilizan ASCII. MacJapanese es una
ciertos caracteres de “control” que pueden utilizarse en los codificación de texto para los archivos que almacenan sus datos
ordenadores para controlar la salida. No incluye caracteres en caracteres japoneses. Existen otras. Pero estas codificaciones
especiales utilizados normalmente en los libros impresos, como eran específicas del Mac. No facilitaban el intercambio de datos
las comillas curvas o el apóstrofe, los boliches o los guiones con otros sistemas operativos, y también resultaba problemática
largos — como este. De igual modo, muchos lenguajes (como el la mezcla de datos con diferentes codificaciones (como por
Español, Francés y Alemán) utilizan caracteres acentuados que ejemplo una frase en japonés en mitad de un texto en castellano).
no están definidos por la especificación ASCII.
En 1986 las personas de Xeros y Apple tenían diferentes
Cuando se presentaron los sistemas operativos Macintosh y problemas que requerían de una misma solución. Mucho antes, el
Windows, cada OS definió sus extensiones sobre el estándar concepto de una codificación de caracteres universal, capaz de
ASCII, aplicando nuevos códigos desde 128 a 255. Esto permitió contener todos los caracteres de todos los lenguajes, se reveló
que ambos sistemas operativos pudiesen trabajar con caracteres como una solución obvia. La codificación universal fue bautizada
acentuados y otros símbolos no contemplados en el estándar como “Unicode” por una de las personas que contribuyó a su
ASCII. Sin embargo, las extensiones de Macintosh y Windows no creación en Xerox. Unicode resuelve todos estos problemas.
64
Cualquier caracter que necesites de cualquier lenguaje está Cambiar tu Código para Gestionar
soportado y será el mismo caracter en cualquier ordenador que Codificaciones de Texto
soporte Unicode. Como extra, puedes combinar caracteres de Desafortunadamente, no existe un método perfectamente seguro
diferentes idiomas en un único documento, dado que todos ellos para determinar la codificación de un archivo. Debes de saber en
están definidos en Unicode. qué codificación está el archivo para usarlo. Por ejemplo, si
procede de un usuario anglohablante de Windows, es probable
El soporte de Unicode comenzó a aparecer en el Macintosh con
que esté en Windows ANSI.
el Sistema 7.6 y en Windows con Windows 95. Puedes traducir
los archivos entre otras codificaciones de texto y Unicode, pero Si la codificación de una cadena está definida, puedes utilizar la
Unicode aun resultaba la excepción y no la regla. No fue hasta el función Encoding para obtener su codificación de este modo:
Mac OS X y Windows 2000 cuando Unicode pasó a ser el
estándar.
theEncoding = Encoding(myString)
Los usuarios de ordenadores están ahora en una transición. Hay
algunos que utilizan sistemas antiguos donde el Unicode no es
un estándar. Todos los nuevos sistemas que están ejecutando donde la variabla myString contiene la cadena cuya codificación
Mac OS X, Windows o Linux utilizan Unicode como la queremos determinar y theEncoding es un objeto TextEncoding.
codificación estándar. Como resultado, es posible que debas de Si la codificación no está definida, la función Enconding devuelve
lidiar con archivos de texto con diferentes codificaciones durante Nil.
algún tiempo. Esto significa que probablemente debas de
modificar tu código para gestionar esto. En algún momento
Codificaciones de Texto y Archivos
Cuando se trabaja con datos de texto en archivos, es
futuro, es posible que resulte raro no asumir que todos los
particularmente importante gestionar correctamente las
archivos estén ya en Unicode, pero hasta entonces debes de
codificaciones. Consulta la sección Archivos de Texto del capítulo
realizar algunas modificaciones en tu código de modo que tus
Archivos para obtener más información sobre este particular.
aplicaciones funcionen correctamente cuando encuentre texto
con diferentes tipos de codificación.

65
Obtener Caracteres Individuales
Tal y como hemos indicado previamente, cuando necesitas
obtener un caracter ASCII concreto, debes de utilizar la función
Chr pasándole el código ASCII para el caracter deseado. Pero si
quieres un caracter no ASCII, debes de indicar también la
codificación. La función Chr es sólo para la codificación ASCII;
de modo que es posible que no obtengas el caracter esperado si
pasas un número superior a 127. En vez de ello debes de utilizar
el método Chr de la clase TextEncoding. Este requiere que
indiques tanto la codificación como el valor del caracter. Por
ejemplo, lo siguiente devuelve el símbolo ™ en la variable s:

Dim s As String
s = Encodings.MacRoman.Chr(170)

66
Section 6

Formatear Números, Fechas y Horas

Hay varios modos de controlar cómo se muestran y formatean La función Str funciona de una forma similar a Format, pero no
los números, fechas y horas. utiliza los ajustes del OS para formatear el número.

Números
Los números están almacenados sin formato. Afortunadamente result = Str(Number, FormatSpec)
hay dos funciones que pueden formatear un número: Format y
Str.
Cuando se utiliza Str para formatear un número siempre se utiliza
La función Format facilita el formateado de números. El número el decimal “.” como separador decimal, independientemente de
resultante se formatea para utilizar las opciones de formato lo indicado por los ajustes de formato del SO.
correspondientes a los ajustes del OS.
Utiliza la función Format para los números que deban de
Para utilizar esta función, pasa la especificación de formato y el mostrarse al usuario. Utiliza la función Str para los que deban
número que deseas formatear. La función Format devuelve una almacenarse (como en una base de datos o un archivo XML).
cadena que representa el número con el formato ya aplicado
FormatSpec
sobre él.
El FormatSpec es una cadena compuesta por uno o más
La sintaxis para la función Formato es: caracteres que controlan cómo ha de formatearse el número. La
especificación de formato “$###,##0.00” aplica el formato de
dólares y centavos utilizados en los Estados Unidos.
result = Format(Number, FormatSpec)
En Windows, el caracter utilizado como separador Decimal y de
Miles está especificado por el usuario en el Panel de Control

67
Ajustes Regionales. En OS X, estos caracteres se indican en el Por defecto, la cadena FormatSpec se aplica a todos los
panel Formatos de la preferencia del sistema Internacional. números. Si quieres indicar diferentes FormatSpecs para
números positivos, negativos y cero, sólo has de separar los
formatos con punto y coma dentro de FormatSpec. El orden en el
Figura 2.7
que proporcionas el FormatSpecs es: positivo, negativo, cero.

Figura 2.8

68
Fechas Las propiedades ShortDate, AbbreviatedDate, y LongDate de la
Las fechas son objetos y tienen propiedades que contienen la clase Date utilizan el formato que el usuario haya indicado en
fecha en varios formatos. Para obtener una fecha formateada en dichos ajustes, y puede que no coincidan con los mostrados en
un modo concreto, sólo has de acceder a la propiedad indicada. la tabla. Para obtener la fecha actual en cualquiera de estos
formatos, sólo has de crear e instanciar un objeto de fecha
Figura 2.9 accediendo a continuación a la propiedad correcta. Aquí, la fecha
actual está formateada como una fecha larga, asignada a una
variable:

Dim now As New Date


Dim theDate As String
theDate = now.LongDate

Los formatos de fecha se controlan en Figura 2.10 La propiedad TotalSeconds de un objeto Date es la
las Propiedades de Fecha del usuario propiedad “maestra” que contiene la fecha/tiempo
(Windows) o los ajustes del sistema para asociada con el objeto. La propiedad TotalSeconds
los Formatos de Fecha (OS X). En OS X, está definida como la cantidad de segundos desde
el diálogo es accesible desde el panel el 1 de enero de 1904.
Formatos en la preferencia del sistema
Los valores de otras propiedades se derivan de
“Idioma y Texto”. En Windows, las
TotalSeconds. Si cambias el valor de TotalSeconds,
Propiedades de Fecha son una pantalla
los valores de Year, Month, Day, Hour, Minute y
del panel de control Opciones
Seconds cambian para reflejar el segundo en el que
Regionales. Los usuarios pueden elegir
tiene lugar TotalSeconds.
el orden del día, mes, año y separadores.

69
De igual modo, si cambias cualquiera de estas propiedades, el usuario a través de la pantalla Hora en el panel de control Ajustes
valor de TotalSeconds cambia en consecuencia. Regionales en WIndows o el panel Formatos de Tiempo en
“Idioma y Texto” en OS X.
Figura 2.11

Horas
Los valores de hora se almacenan como parte de la fecha. Los
objetos Date tienen dos propiedades que almacenan los valores
de tiempo en dos formatos diferentes. La Figura 2.12 muestra las
dos propiedades y ejemplos de cómo se devuelve el tiempo.

Para obtener el tiempo actual en cualquiera de estos formatos,


crea e instancia un objeto Date y accede a la propiedad
adecuada. En este ejemplo, se asigna a una variable el tiempo
actual formateado como LongTime:

Dim now As New Date


Dim theTime As String
theTime = now.LongTime

Tal y como ocurre con los formatos de fecha, son varios los
aspectos de ShorTime y LongTime que están controlados por el
70
Section 7

Expresiones Regulares

Buscar mediante Figura 2.12 Asigna una instancia RegExOptions para definir los

Expresiones Regulares ajustes de búsqueda.


Tus aplicaciones pueden buscar y sustituir
ReplacementPattern
texto usando expresiones regulares
El patrón RegEx a utilizar en la sustitución del texto.
(denominadas con frecuencia RegEx), un
patrón que describe el texto específico a SearchPattern
buscar en una cadena. El patrón RegEx a usar durante la búsqueda.

Utilizas las propiedades de las clases SearchStartPosition


RegEx, RegExOptions y RegExMatch, La posición para iniciar la búsqueda.
para definir una expresión regular y para
Métodos
buscar o sustituir texto utilizando
expresiones regulares. Replace
Busca el texto usando SearchPattern y lo sustituye
Las Expresiones Regulares pueden ser un utilizando ReplacePattern, devolviendo la cadena
poco difíciles de pillar, pero suponen un resultante.
modo rápido y eficiente de parsear texto.
Search
RegEx Busca las cadenas proporcionadas usando el patrón
Clase: RegEx indicado mediante SearchPattern.
Propiedades
Usa la clase RegEx para procesar las expresiones
Options

71
regulares. La propiedad SearchPattern contiene la expresión a RegExOptions
usar y llamando a continuación al método Search (suministrando Clase: RegExOptions
el texto sobre el cual buscar). Esta clase se utiliza para indicar varias opciones de búsqueda
como la sensibilidad a mayúsculas/minúsculas, avidez, etc.
El método Search devuelve una RegExMatch que contiene
Dirígete a la Referencia del Lenguaje para más información.
información sobre lo encontrado. Si no se encuentra nada,
entonces se devuelve Nil.
Ejemplos
Puedes buscar de nuevo llamando múltiples veces a Search, sin Este ejemplo encuentra la primera palabra en el texto “Software
development made easy”, devolviendo “Software”:
necesidad de volver a proporcionar el texto.

RegExMatch Dim re As New RegEx


Clase: RegExMatch
Dim match As RegExMatch
Propiedades
SubExpressionCount re.SearchPattern = "\w*"
La cantidad de subexpresiones para la búsqueda. match = re.Search("Software development
made easy")
SubExpressionString
If match <> Nil Then
Un array de subexpresiones. El índice 0 devuelve la cadena
MsgBox(match.SubExpressionString(0))
encontrada al completo.
End If
Métodos
Replace
Consulta la Figura 2.14 relacioanda para obtener más ejemplos
Utilizado para sustituir el resultado encontrado.
sobre otros patrones de búsqueda sobre el mismo texto
Cuando llamas a RegEx.Search, se devuelve una instancia de “Software development made easy”.
esta clase en el caso de que se halle el patrón (se han
Recuerda que un resultado RegEx (RegExMatch) puede devolver
encontrado correspondencias).
una o más correspondencias. Debes de comprobar
72
RegExMatch.SubExpressionCount para ver cuantas
correspondencias se han encontrado. Dim re As New RegEx

Figura 2.13 re.SearchPattern = "Software develop-


ment"
re.ReplacementPattern = "Programming"
Dim result As String
result = re.Replace("Software develop-
ment made easy")

MsgBox(result)

La variable result contiene ahora “Programming made easy”.


Sustitución
Este ejemplo realiza una simple eliminación de etiquetas HTML
Las Expresiones Regulares también pueden utilizarse para
del código HTML original:
sustituir cadenas en el texto.

Este ejemplo simplemente sustituye “Software development” con


“Programming”:

73
Dim re As New RegEx
re.SearchPattern = "<[^<>]+>"
re.ReplacementPattern = ""

Dim html As String = "<p>Hello.</p>"


Dim plain As String = re.Replace(html)

While (StrComp(html, plain, 0) <> 0)


html = plain
plain = re.Replace()
Wend

MsgBox(plain)

74
Section 8

Portapapeles

La clase Clipboard se utiliza para obtener o añadir datos al


clipboard del sistema. Las propiedades y métodos te permiten Dim c As New Clipboard
determinar el tipo de datos disponibles en el Portapapeles, Dim clipText As String
obtener datos del Portapapeles y enviar datos al Portapapeles.
La clase Portapapeles soporta tres tipos de datos: texto, If c.TextAvailable Then
imágenes y datos binarios/en crudo. Los datos binarios se clipText = c.Text
representan mediante una cadena y están marcados con el tipo End If
que especifiques de modo que puedas saber qué representa c.Close
dicha información.

Obtener texto del Portapapeles Añadir Texto al Portapapeles


Dado que el Portapapeles puede contener texto, imágenes y Este ejemplo añade texto al portapapeles:
datos binarios, deberías de preguntarle sobre el tipo de datos
que contiene antes de utilizarlo. Para comprobar en búsqueda
Dim c As Clipboard
de texto, utiliza el método TextAvailable:
c.Text = "Hello!"
c.Close

Para ejemplos sobre el uso del Portapapeles con datos gráficos


y binarios (en crudo), consulta la sección del Portapapeles en el
capítulo Gráficos y Multimedia.

75
Section 9

Criptografía

Con las funciones de criptografía puedes encriptar tu texto para Ejemplos


propósitos de seguridad. Este ejemplo calcula el hash del texto suministrado usando
SHA256:
El módulo Crypto contiene estos métodos:

• Hash
Dim value As String
• HMAC value = Crypto.SHA256("DataToEncrypt")

• PBKDF2

Para cada uno de ellos puedes indicar los datos y el algoritmo a


utilizar mediante las enumeraciones Crypto.Algorithm:

• MD5

• SHA1

• SHA256

• SHA512

Adicionalmente, se encuentran las funciones MD5, SHA1,


SHA256, y SHA512 que son métodos de conveniencia para
Crypto.Hash.

76
Section 1

Color

Color es un tipo de dato. Consiste en tres valores que definen un También puedes asignar un valor de color directamente, sin usar
color. Un color puede ser indicado utilizando los modelos RGB, las funciones RGB, HSV o CMY. Utilizas el modelo de color RGB
HSV o CMY. Utilizas las tres propiedades relevantes de Color mediante el literal &c. El literal &c contiene un valor de color.
para definir un color. Por ejemplo, para usar el modelo RGB, Utiliza la siguiente sintaxis para indicar un color:
utiliza la función RGB y define los valores para las propiedades
Red, Green y Blue. Estas son valores Integer que comprenden
&cRRGGBB
desde 0 hasta 255. La función RGB devuelve un Color
correspondiente a los valores pasados para rojo, verde y azul.
Son varias las clases que tienen propieades de tipo Color. Por donde RR es el valor hexadecimal para el Rojo, GG es el valor
ejemplo la propiedad ForeColor de la clase Graphics es un Color. hexadecimal para el Verde y BB es el valor hexadecimal para el
Azul. Cada valor va de 00 a FF, en vez de ir de 0 a 255. Por
Si necesitas almacenar un color, puedes crear una propiedad o
ejemplo, lo siguientes es equivalente al anterior ejemplo:
variable del tipo Color y utilizar a continuación la función RGB,
HSV o CMY. En este ejemplo se crea una nueva variable de tipo
Color y los valores RGB que lo componen se asignan utilizando Dim c As Color
la función RGB: c = &cFFFFFF

Dim c As Color “FF” es el hexadecimal de 255. Hay un modo sencillo de obtener


c = RGB(255, 255, 255) los valores hexadecimales. Usando el área de declaración Añadir
Constante, puedes definir una constante de tipo color y utilizar el
Selector de Color incorporado para elegirlo visualmente. Cuando

78
seleccionas un color, el valor hexadecimal queda insertado en el Ejemplo de Transparencia
área Value del cuadro de diálogo. El siguiente ejemplo dibuja muestras de color en un Canvas, con
diferentes transparencias. El código está en el evento Paint:
En este ejemplo la propiedad ForeColor de un objeto Graphics se
define a azul, de modo que el texto se dibujará con dicho color:
Dim red As Color
For i As Integer = 0 To 4
Sub Paint(g as Graphics)
red = RGB(255, 0, 0, i * 50)
g.ForeColor = RGB(9, 13, 80)
g.ForeColor = red
g.DrawString("Hello World", 50, 50)
g.FillRect(0, i*20, 200, 20)
End Sub
g.ForeColor = &c000000
g.DrawString("Transparency = " +
Transparencia Str(i*50), 0, i*20+15)
La transparencia se indica utilizando un parámetro Integer Next
adicional que varía desde 0 (opaco) a 255 (totalmente
transparente). Si se omite se utiliza 0 por defecto (sin
transparencia), como en los ejemplos anteriores. Para el literal de Nota: La transparencia
color &c, el parámetro de transparencia también es un entero, requiere de GDIPlus activo en Figura 3.1
pero indicado en hexadecimal. Por tanto, la máxima las aplicaciones Windows y
transparencia es el valor “FF” (255). Por ejemplo, esto es válido: de Cocoa en las aplicaciones
OS X.

&cff0000ff Determinar los valores


RGB para un Color
RGB(255, 0, 0, 255) Si necesitas asignar un color
en tiempo de ejecución pero no estás seguro sobre los valores
RGB que debes de utilizar para obtener un color concreto, debes

79
de utilizar el Selector de Color. El siguiente código muestra el En los selectore sde color de OS X haces clic sobre un color y la
Selector de Color: muestra del color aparece en el área de muestras en la parte
superior de la pantalla. Cuando hagas clic sobre OK, aparecerán
los valores RGB.
Dim c As Color
Dim b As Boolean En la versión Windows el Selector de Color utiliza sólo un
b = SelectColor(c, "Choose a color") formato. Puedes seleccionar cualquiera de los colores
If b Then // user chose a color predefinidos o bien hacer clic sobre el botón Definir Colores
// do something with the color (c) Personalizados para mostrar el selector de color “avanzado”, en
End If el que se representan los colores como un contínuo permitiendo
indicar el color utilizando los modelos RGB o HSV. Selecciona un
color haciendo clic sobre un punto en el espectro de color o
Si el usuario cancela la acción en el Selector de Color, la variable introduce los valores en las áreas RGB o HSV. Haz clic en Añadir
booleana, b, será False; de lo contrario el color seleccionado será a Colores Personalizados para añadir el color personalizado a
devuelto en el objeto de color, c, y estará disponible para su una de las muestras de color en la parte izquierda del diálogo.
asignación en la propiedad de color de un objeto.
La Propiedad Pixel de los Objetos Gráfi cos
La propiedad Pixel del objeto Graphics te permite obtener o
Figura 3.2 definir el color del píxel indicado. Esta propiedad es un ejemlo de
una propiedad cuyo tipo de dato es Color. En este ejemplo, el
manejador de evento Paint está definiendo un pixel a negro si es
blanco y como blanco si es negro:

80
Sub Paint(g As Graphics)
If g.Pixel(10, 20) = RGB(0, 0, 0) Then
g.Pixel(10, 20) = RGB(255, 255, 255)
Else
g.Pixel(10, 20) = RGB(0, 0, 0)
End If
End Sub

Puedes ver que el código que comprueba el color de un píxel y el


que define el color de un píxel es básicamente el mismo.

81
Section 2

Imágenes

Tus aplicaciones pueden incluir imágenes ya creadas o bien ventana, pero no es así. El origen siempre está en la esquina
dibujar tus propias imágenes. En algunos casos puedes añadir superior izquierda del área. Para toda la pantalla se trata de la
las imágenes deseadas sin escribir código. Cuando precisas esquina superior izquierda de la pantalla. Para múltiples
escribir código tienes a tu disposición una amplia variedad de pantallas, se trata de la esquina superior izquierda de la pantalla
clases. que se encuentre más a la izquierda en la configuración. Para
una ventana o página web, el origen es la esquina superior
Para dibujar una imagen en tu aplicación utilizas el control
izquierda de la ventana o página web, y para un control se trata
Canvas (para las aplicaciones de escritorio) o el WebCanvas
de la esquina superior izquierda del control. El eje X es el eje
(para las aplicaciones web).
horizontal. Aumenta su valor moviéndose de izquierda a derecha.
Dado que el dibujado se realiza utilizando coordenadas para El eje Y es el eje vertical y aumenta su valor moviéndose de
indicar la ubicación de las cosas es importante comprender arriba hacia abajo.
cómo funcionan.
De modo que, un punto que esta en 10,20 (en una ventana, por
ejemplo), está 10 píxeles desde el costado izquierdo de la
Coordenadas de Dibujado
La mayoría de los métodos gráficos requieren que indiques la ventana y 20 píxeles desde la parte superior de la ventana. Si

ubicación dentro del control en el que deseas inicar el dibujado. estás trabajando con un control Canvas, el punto 10, 20 esta 10

Esta ubicación se indica utilizando el sistema de coordenadas. El pixeles desde el costado izquierdo del control Canvas y 20

sistema es una rejilla de líneas horizontales y verticales de un píxeles hacia abajo desde el costado superior del control.

píxel de grosor. Si nunca antes has realizado dibujo por


Mostrar imágenes
ordenador con un sistema de coordenadas, es posible que
Las ventanas pueden emplear varias formas para mostrar
esperes que el origen (0,0) se enceuntre en el centro de la
imágenes.

82
Usar toda la Ventana Usar parte de la Ventana o Página Web
En una ventana (Window), puedes utilizar la propiedad Backdrop Si quieres mostrar una imagen en una porción del área de usuario
o bien el evento Paint para dibujar una imagen directamente en el (ventana o página web), entonces puedes llevarlo a cabo
fondo de la ventana. utilizando el control específico de la plataforma, ya sea un
ImageWell o ImageView.
El modo más sencillo de hacerlo consiste en añadir una imagen
al proyecto y asignarla a la propiedad Backdrop en el Inspector. Ambos controles son simples y sólo soportan el mostrado de una
imagen. No soportan el dibujado y no puedes alterar el tamaño
También puedes asignar una imagen con código: de la imagen mostrada.

ImageWell tiene una propiedad Image sobre la que puedes


Self.Backdrop = PictureName asignar la imagen (Picture) a mostrar:

También puedes solicitar al usuario la imagen a mostrar: LogoImageWell.Image = PictureName

Dim f As FolderItem Por supuesto también puedes solicitar la imagen al usuario y


f = GetOpenFolderItem("") asignarla.
If f <> Nil Then
WebImageView tiene una propiedad Picture y una propiedad URL
Self.Backdrop = f.OpenAsPicture
que puede utilizarse para mostrar una imagen.
End If
La propiedad URL muestra la imagen del URL indicado:

Sólo deberías de considerar el uso del fondo para las imágenes


LogoImageView.URL =
que no cambien. Si necesitas cambiar la imagen mostrada,
entonces deberías de utilizar una de las siguientes técnicas.
"http://www.website.com/picture.jpg"

83
También puedes asignar una imagen: Esto crea una imagen con un tamaño de 100x100 píxeles y 32
bits de profundidad de color.

LogoImageView.Picture = PictureName Ahora que ya tienes un nuevo objeto de imagen, puedes utilizar
su Graphics para dibujar. Para dibujar una imagen existente que
se ha añadido al proyecto:
Para mejorar el rendimiento de tus aplicaciones web, deberías de
cachear tus imágenes localmente en el navegador. Para ello, solo
has de asignar la imagen a una propiedad de la página web (o de Dim p As New Picture(100, 100, 32)
uno de sus controles) antes de utilizarla en tu ImageView. Esto p.DrawPicture(PictureName, 0, 0)
causa que se envíe la imagen al navegador sólo una vez (cuando
se carga la página con la propiedad). Si la utilizas de nuevo, ya
A continuación puedes asignar el objeto de imagen a cualquier
estará cacheada en el navegador y disponible para su uso
cosa que acepte una imagen, como:
inmediato.

Crear Imágenes LogoImageWell.Image = p // Desktop


Además de añadir imágenes pre-existentes a tus proyectos, LogoImageView.Picture = p // Web
también puedes crear imágenes de forma dinámica.

Para ello, necesitar tener una instancia de la clase Graphics o de


Graphics y WebGraphics
la clase WebGraphics. Pero no puedes instanciar estas clases tú Todo el dibujado se realiza utilizando Graphics o bien
mismo. Un modo de obtener una instancia de Graphics es WebGraphics. Graphics puede utilizarse en todo tipo de
creando una nueva instancia de una Picture: aplicaiones, incluyendo las aplicaciones de escritorio y web.

Como se ha indicado anteriormente, siempre puedes acceder a


Dim p As New Picture(100, 100, 32)
Graphics creando un nuevo objeto Picture. Adicionalmente, en
las aplicaciones de escritorio, cualquier control con un evento
Paint proporciona una instancia del objeto Graphics (como

84
parámetro denominado g), y que puedes utilizar para el dibujado.
Esto se realiza más frecuentemente mediante el uso del control g.DrawPicture(PictureName, 0, 0,
Canvas. En las aplicaciones web, el control WebCanvas te PictureName.Width,
proporciona una instancia WebGraphics en su evento Paint, y PictureName.Height, 10, 10, 20, 20)
que puedes usar para el dibujado.

Nota: El código de ejemplo en las próximas páginas puede utilizarse en el Mover una Picture
evento Paint de un Canvas o de un WebCanvas.
Note: El desplazamiento de imágenes no puede realizarse con el
Estalar Imágenes WebCanvas.
Con frecuencia la imagen no tiene el tamaño adecuado para ser Una imagen dibujada en un Canvas con el método DrawPicture
mostrada en la aplicación. Puedes escalar la imagen a cualquier puede moverse llamando al método Scroll del Canvas. Este
tamaño mediante el uso del método DrawPicture en Graphics y utiliza tres parámetros: la imagen a mover, y las cantidades de
WebGraphics. Este ejemplo, en un evento Paint, reduce la escala desplazamiento en las direcciones vertical y horizontal.
de la imagen desde su tamaño original para que encaje en el
tamaño del Canvas: Para utilizar el método Scroll en un control Canvas, debes de
almacenar el último valor de desplazamiento para el eje en el que
se esté llevando a cabo el movimiento, de modo que puedas
g.DrawPicture(PictureName, 0, 0, utilizarlo para calcular la cantidad de desplazamiento. Esto puede
g.Width, g.Height, realizarse añadiendo propiedades a la ventana que contiene el
PictureName.Width, control Canvas o creando una nueva clase basada en el control
PictureName.Height) Canvas y que contenga las propiedades para contener los
últimos valores para la cantidad de desplazamiento en X e Y.

Copiar una porción de una Imagen El siguiente ejemplo mueve una imagen. La imagen ha sido
Algunas veces es posible que debas de extraer solo una parte de añadida al proyecto. Las propiedades XScroll y YScroll se han
la imagen. Puedes hacerlo fácilmente utilizando el método añadido a la ventana con el objeto de contener las cantidades de
DrawPicture y especificando las coordenadas de la porción de la imagen ya desplazadas.
imagen que deseas:
85
Un modo conveniente de mover una imagen es mediante las El evento Paint del Canvas dibuja la imagen:
cuatro teclas de cursor. Para ello, sitúa tu código en el manejador
de evento KeyDown de la ventana activa. Este evento recibe
g.DrawPicture(PictureName, XScroll, YScroll)
cada pulsación de tecla. Tu código puede comprobar si se ha
pulsado cualquiera de las teclas de cursor y realizar la acción
correspondiente. Por ejemplo, este código del evento KeyDown Dibujar Píxeles
de la ventana desplaza la imagen 8 píxeles cada vez:
Puedes obtener y definir el color de cada píxel con el método
Pixel. Has de pasar las coordenadas X e Y del píxel y asignarle un
Function KeyDown (Key As String) As color. Aquí se dibujan 5.000 píxeles en coordenadas aleatorias
Boolean sobre el Graphics de un Canvas:
Select Case Asc(Key)
Case 31 // up arrow Figura 3.3
Yscroll = YScroll - 8
Canvas1.Scroll(0, -8)
Case 29 // Right arrow
Xscroll = XScroll - 8
Canvas1.Scroll(-8, 0)
Case 30 // Down arrow
Yscroll = Yscroll + 8
Canvas1.Scroll(0, 8)
Case 28 // Left arrow Dim c As Color
For i As Integer = 1 To 5000
Xscroll = Xscroll + 8
c = RGB(Rnd*255, Rnd*255, Rnd*255)
Canvas1.Scroll(8, 0)
g.Pixel(Rnd*Me.Width, Rnd*Me.Height) = c
End Select
Next

86
Nota: Pixel no está disponible en WebGraphics, de modo que este ejemplo Dibujar Rectángulos
no funciona con el WebCanvas.
Los rectángulos se dibujan utilizando los métodos DrawRect,
Dibujar Líneas
FillRect, DrawRoundRect, y FillRoundRect.
Las lineas se dibujan
Figura 3.4 Has de proporcionar las coordenadas X e Y de la esquina
con el método
DrawLine. El color de la superior izquierda del rectángulo,
línea se almacena en la asi como el ancho y altura del
Figura 3.5
propiedad ForeColor. rectángulo. Utiliza la propiedad
Para usar el método ForeColor para indicar el color del
DrawLine has de pasar rectángulo.
las coordenadas de
Las versiones Draw dibujan un
inicio y de final.
rectángulo con solo el borde. Las
Aquí se utiliza el método DrawLine para dibujar una rejilla dentro versiones Fill rellenan el rectángulo
de un control Canvas o el fondo de una ventana. El tamaño de con el color indicado en ForeColor.
celda de la rejilla se define con el valor de la constante kBoxSize:
Los RoundRectangles son rectángulos con esquinas
redondeadas. Por tanto, DrawRoundRect y FillRoundRect
Dim i As Integer precisan dos parámetros adicionales: el ancho y la altura de la
Const kBoxSize = 10 curvatura de las esquinas.

For i = 0 To Me.Width Step kBoxSize Este ejemplo dibuja un rectángulo y lo rellena con el color rojo:
g.DrawLine(i, 0, i, Me.Height)
Next
g.DrawRect(0, 0, 150, 100)
g.ForeColor = &cFF0000 // Red
For i = 0 To Me.Height Step kBoxSize
g.FillRect(0, 0, Me.Width, Me.Height)
g.DrawLine(0, i, Me.Width, i)
Next

87
Dibujar Óvalos Figura 3.7 coordenada Y del primer punto del polígno.
Los óvalos se dibujan con los métodos Este ejemplo dibuja un triángulo:
DrawOval y FillOval. Proporcionas las
coordenadas X e Y de la esquina superior
izquierda del óvalo y el ancho y altura del Dim points() As Integer
óvalo. Utiliza la propiedad ForeColor para points = Array(0, 10, 5, 40, 40, 5, 60)
indicar el color del óvalo. g.DrawPolygon(points)

DrawOval dibuja solo el borde del óvalo


(utiliza PenWidth y PenHeight para indicar el
Dibujar Texto
grosor del borde). FillOval rellena el óval con el ForeColor.
El método DrawString se utiliza para Figura 3.8
Este ejemplo dibuja un óvalo: dibujar texto. Proporcionas las
coordenadas X e Y de la esquina inferior
izquierda del texto, y parámetros
g.DrawOval(0, 0, 50, 75) opcionales para indicar si el texto debe
de continuar en una nueva línea o bien
estrecharse para encajar en el área indicada.
Dibujar Polígonos Figura 3.6
Los polígonos se dibujan con los métodos Este ejemplo dibuja texto:
DrawPolygon y FillPolygon. Puedes
proporcionar un array de enteros que
contenga cada punto del polígono. Este es g.ForeColor = &cff0000
un array basado en 1 donde los elementos g.TextFont = "Helvetica"
impares contienen las coordenadas X y los g.TextSize = 16
elementos pares las coordenadas Y. Esto g.DrawString("Hello world", 10, 130)
significa que el elemento 1 contiene la
coordenada X del primer punto del
polígono y el elemento 2 contiene la

88
Dibujar Iconos Estándar en un parpadeo no deseado.
Figura 3.10
Diálogos
Puedes crear regiones en una área padre con el método Clip.
El método MsgBox y la clase
Pasa la esquina superior izquierda de la región hija y su ancho y
MessageDialog te permiten mostrar
alto. Devolverá un nuevo objeto Graphics que se corresponde
cuadros de diálogo con un icono de
con la región indicada dentro del área. Ahora podrás dibujar
diálogo estándar. Puede que necesites
dentro del área hija como harías con cualquier otro objeto
diseñar tu propio cuadro de diálogo
Graphics. La única diferencia es que el dibujado estará confinado
para usar los iconos. Usando estos
en el área hija. Las coordeandas de cada llamada serán relativas
métodos puedes dibujar los iconos
a la esquina superior izquierda del área hija.
estándar cuando los necesites:
DrawCautionIcon, DrawNoteIcon, Aquí se muestra cómo funciona. Se han definido dos regiones en
DrawStopIcon. la parte superior del Canvas mediante llamadas al método Clip.
Las subsiguientes llamadas al método DrawRect muestran donde
Nota: Estos métodos no están disponibles en
WebGraphics. están cada uno de los clips. Las llamadas al método DrawOval
dibuja las formas dentro de las áreas recortadas. La primera
Dibujar en regiones
llamada intenta dibujar fuera del área. Si estuvieses dibujando
Cuando dibujas una imagen compleja que implique varias sobre el objeto Graphics padre, el primer óvalo pisaría al
llamadas a los métodos de Graphics, querrás crear regiones que segundo, pero como el dibujado está delimitado, esto no ocurre.
no se superpongan
Figura 3.9 dentro de dicha área.
Entonces podrás
dibujar dentro de
cada área “hija”, con
la confianza de que
cada dibujado no
pisará
inadvertidamente
cualquier otoro
objeto provocando
89
Canvas puede utilizarse fácilmente para suplementar los
Dim clip1 As Graphics = g.Clip(0, 0, 150, 15) controles incluidos de serie.
Dim clip2 As Graphics = g.Clip(150, 0, 150, 15)
Pongamos por caso que quieres crear un control personalizado
// Draw the border of the Canvas in black sencillo como un rectángulo cullo color de relleno cambie de
g.ForeColor = &c000000 negro a gris cuando se haga clic sobre él.
g.DrawRect(0, 0, g.Width, g.Height)
Para ello, sigue estos pasos:
// Draw into the first area in red
1. Arrastra un Canvas sobre la ventana.
clip1.ForeColor = &cff0000
clip1.DrawRect(0, 0, clip1.Width, clip1.Height)
2. Añade una propiedad a la Ventana llamada “mFilled As
// Try to draw outside its clip
Boolean”.
clip1.DrawOval(0, 0, 200, 15)

3. Selecciona el Canvas y añade un manejador de evento


// Draw into the second area in blue
MouseDown.
clip2.ForeColor = &c0000ff
En dicho manejador de evento, conmutas mFilled e indicas al
//draw the border
clip2.DrawRect(0, 0, clip2.Width, clip2.Height)
Canvas que se redibuje.
clip2.DrawOval(0, 0, 150, 15)
mFilled = Not mFilled
Me.Invalidate
Nota: Clip no está disponible en WebGraphics.

Controles Personalizados
Los controles visibles (los controles que tienen una interfaz
gráfica con la que puede interactuar el usuario directamente,
como los PushButtons) son imágenes que tienen el código que
controla como han de dibujarse. Esto significa que el control

90
4. Por último, en el evento Paint, dibujas un recángulo negro si
mFilled = True o bien un rectángulo gris en caso contrario.

Dim fillColor As Color


If mFilled Then
fillColor = &c000000 // Black
Else
fillColor = &caaaaaa // Gray
End If
g.ForeColor = fillColor
g.FillRect(0, 0, Me.Width, Me.Height)

91
Section 3

Gráfi cos Vectoriales

Un gráfico vectorial (en oposición a un gráfico de mapa de bits) define un RoundRectShape:


está compuesto por completo de objetos primitivos — líneas,
rectángulos, texto, círculos y óvalos, etc — que retienen su Dim r As New RoundRectShape
identidad en el gráfico. Es posible variar su tamaño sin que se r.Width = 120
pixelen o bien “descomponerse” tal y como puede hacer un r.Height = 120
gráfico de mapa de bits. La clase Object2D es la clase base para r.Border = 100
todas las clases que crean objetos primitivos, y que incluyen: r.BorderColor = RGB(0,0,0) // black
ArcShape, CurveShape, FigureShape, OvalShape, PixMapShape, r.FillColor = RGB(255,102,102)
RectShape, RoundRectShape y StringShape. r.CornerHeight = 15
r.CornerWidth = 15
Cada una de estas clases te permiten indicar los bordes, colores
r.BorderWidth = 2.5
de línea y de relleno, rotación, escala y posición.

Nota: Los gráficos vectoriales sólo funcionan en las aplicaciones de


escritorio. El único problema con esto es que la forma no aparece en
ninguna parte. Sólo está “definida” — lista para usar. Debes de
Dibujar y Mostrar un Objeto Vectorial añadir un comando para dibujar el objeto vectorial en otro
Dibujas un único objeto vectorial simplemente instanciándolo e
objeto. Como has aprendido en la anterior sección, un Canvas
indicando sus propiedades. Por ejemplo, el siguiente código
es un buen lugar donde realizar dibujado.

Utiliza el método DrawObject de Graphics para dibujar un gráfico


Vectorial. Añade DrawObject al anterior código e inclúyelo en el
evento Paint de un control Canvas:

92
Combinar Objetos Vectoriales
Dim r As New RoundRectShape También puedes crear objetos gráficos vectoriales compuestos
r.Width = 120 que estén formados por varios objetos gráficos vectoriales
r.Height = 120 individuales. El objeto compuesto es un objeto Group2D; es un
r.Border = 100 grupo de objetos Object2D. Utiliza los métodos Append o Insert
r.BorderColor = RGB(0,0,0) // black de la clase Group2D para añadir objetos gráficos vectoriales
r.FillColor = RGB(255,102,102) individuales al objeto Group2D. Cuando hayas terminado, dibuja
r.CornerHeight = 15 el objeto utilizando una llamada al método DrawObject.
r.CornerWidth = 15
r.BorderWidth = 2.5 Para ilustrarlo, tomemos el anterior código y añadamos un
segundo objeto RoundRectShape desplazado del original en 20
g.DrawObject(r, 100, 100) píxeles. Utiliza Append para añadir los dos RoundRectShapes al
objeto Group2D y dibújalo en el Canvas:

Figura 3.12
Figura 3.11

93
Combinar Bitmap y Gráfi cos Vectoriales
Dim r As New RoundRectShape Puedes combinar tanto bitmap como
r.Width = 120 gráficos vectoriales carando en Figura 3.13
r.Height = 120 primer lugar el gráfico bitmap sobre
r.Border = 100 un objeto vectorial, usando para ello
r.BorderColor = RGB(0, 0, 0) // black la clase PixmapShape. Luego
r.FillColor = RGB(255, 102, 102) agrupas este con otros objetos
r.CornerHeight = 15 vectoriales para crear una única
r.CornerWidth = 15 imagen. Este ejemplo combina un
r.BorderWidth = 2.5 gráfico bitmap añadido al proyecto
con una StringShape:
Dim s As New RoundRectShape
s.Width = 120
s.Height = 120 Dim px As PixmapShape
s.Border = 100 px = New PixmapShape(PictureName)
s.BorderColor = RGB(0,0,0) // black
s.FillColor = RGB(255, 102, 102) Dim s as StringShape
s.CornerHeight = 15 s = New StringShape
s.CornerWidth = 15 s.Y = 70
s.BorderWidth = 2.5 s.Text = "Hello, world!"
s.X = r.X + 20 s.TextFont = "Helvetica"
s.Y = r.Y + 20 s.Bold = True
Dim d as New Group2D
Dim group As New Group2D d.Append(px)
group.Append(r) d.Append(s)
group.Append(s) g.DrawObject(d, 100, 10)

94
Guardar y Cargar Gráficos Vectoriales Para guardar esta imagen, añade un botón llamado Guardar en la
Los gráficos vectoriales pueden guardarse usando el método ventana y utiliza este código:
Save de la clase Picture. Los gráficos vectoriales se almacenan
como archivos PIC en OS X, y como archivos EMF en Windows. Dim file As FolderItem
If TargetMacOS Then
Nota: Guardar y Cargar gráficos vectoriales no está soportado en Linux. file = GetSaveFolderItem("", "vector.pict")
Dado que necesitas un objeto de imagen para llamar a guardar, Else
file = GetSaveFolderItem("", "vector.emf")
debes dubujar tus gráficos vectoriales sobre una imagen en
End If
primer lugar, y luego dibujar la imagen en el Canvas. En este
ejemplo, mSavePicture es una propiedad de tipo Picture en la If file <> Nil Then
mSavePicture.Save(file, Picture.SaveAsDefaultVector)
ventana:
End If

Dim r As New RoundRectShape


r.Width = 120
r.Height = 120
r.Border = 100
r.BorderColor = RGB(0,0,0) // black
r.FillColor = RGB(255,102,102)
r.CornerHeight = 15
r.CornerWidth = 15
r.BorderWidth = 2.5

mSavePicture.Graphics.DrawObject(r,
100, 100)
g.DrawPicture(p, 0, 0)

95
Para cargar una imagen vectorial, utiliza el método
OpenAsVectorPicture del FolderItem. Este código solicita al
usuario que seleccione una imagen vectorial, mostrándola a
continuación en un ImageWell:

Dim file As FolderItem


file = GetOpenFolderItem("")
If file <> Nil Then
Dim p As Picture
p = file.OpenAsVectorPicture
If p <> Nil Then
VectorWell.Image = p
End If
End If

96
Section 4

Películas y Sonido

Tus aplicaciones pueden mostrar y reproducir películas usando Este código solicita al usuario que elija una película y la
varios controles disponibles: MoviePlayer (para aplicaciones de reproduce en un MoviePlayer:
escritorio), WebYouTubeMovie y WebMoviePlayer (para
aplicaciones web).
Dim f As FolderItem
Reproducir Películas en Aplicaciones de f = GetOpenFolderItem("")

Escritorio
Para reproducir una película en tu aplicación de escritorio, sólo If f <> Nil Then
has de arrastrar un control MoviePlayer sobre la ventana. MoviePlayer1.Movie = f.OpenAsMovie
Entonces puedes ajustar la propiedad Movie a la película que MoviePlayer1.Play
quieras reproducir. Esto puede llevarse a cabo en el Inspector End If
para las películas que se hayan añadido al proyector o puede
hacerse en tiempo real asignando una Película a la propiedad
Movie. Reproducir Películas en Aplicaciones Web
Pueden utilizarse dos controles para reproducir películas en las
En OS X, MoviePlayer utiliza QuickTime para reproducir la aplicaciones web: WebYouTubeMovie y WebMoviePlayer.
película. En Windows puedes elegir entre QuickTime o Windows
Media Player para reproducir la película ajustando la propiedad WebYouTubeMovie
PlayerType. WebYouTubeMovie es un control sencillo que sólo puede
reproducir películas desde YouTube. Sólo has de indicar el URL
Nota: MoviePlayer no está soportado en Linux. para la película de YouTube en el Inspector del control
WebYouTubeMovie.

97
WebMoviePlayer Esto funciona con la mayoría de los archivos de audio como WAV,
WebMoviePlayer puede reproducir películas de una variedad de AIFF, MP3, AAC, etc.
fuentes. Si están disponibles utiliza las capacidades de vídeo También puedes cargar sonidos en tiempo de ejecución usando
HTML5 incluidas en el navegador. Si HTML5 no está soportada, el método FolderItem.OpenAsSound y la clase Sound:
prueba a utilizar Flash para reproducir el vídeo.

Para reproducir una película, asigna un URL a cualquiera de las Dim f As FolderItem
propiedades URL: DesktopURL, MobileCellularURL y
f = GetOpenFolderItem("")
MobileWIFIURL. WebMoviePlayer utilizará la propiedad
If f <> Nil Then
adecuada en función del dispositivo que se esté utilizando y la
Dim s As Sound = f.OpenAsSound
velocidad de conexión a Internet.
If s <> Nil Then
Hay varios métodos para controlar la película, incluyendo: s.Play
FastForward, FastForwardStop, FastRewind, FastRewindStop, End If
GoToBeginning, GoToEnding, Mute, Play y Reset. End If
Siempre debes de llamar a Reset después de cambiar el URL de
la película durante la ejecución de la aplicación.
La clase Sound tiene propiedades para ajustar el volumen y la
panorámica izquierda/derecha. También tiene métodos para
Reproducción de Sonidos y Audio
reproducir, hacer bucle, detener, clonar y comprobar si se está
Con las aplicaciones de escritorio tienes a tu disposición varias
reproduciendo el sonido.
opciones para reproducir sonidos.
Note Player
Clase Sound
La clase NotePlayer se utiliza para reproducir notas musicales. En
Los archivos de sonido añadidos al proyecto pueden
OS X utiliza QuickTime y en Windows las funciones MIDI
reproducirse indicando sus nombres y llamando al método Play:
incorporadas.

Nota: NotePlayer no está soportado en Linux.


SoundName.Play
Este ejemplo reproduce “do-re-mi-fa-sol-la-si-do”:

98
NotePlayer1.Instrument = 1
// Notes for Do Re Mi Fa So La Ti Do
// (C, D, E, F, G, A, B, C)
Dim DoReMi(7) As Integer
DoReMi = Array(60, 62, 64, 65, 67,
69, 71, 60)

For Each note As Integer In DoReMi


NotePlayer1.PlayNote(note, 100)
// Pause to let note play
App.SleepCurrentThread(500)
Next

MoviePlayer
También puedes utilizar MoviePlayer para reproducir sonidos.
Sólo has de abrir tu archivo de sonido como si se tratase de una
Película y asignarlo a la Propiedad Movie. El uso de MoviePlayer
para reproducir sonidos te permite utilizar el controlador de
Película para reproducir y detener el sonido.

Puedes hacer que el MoviePlayer sea invisible, en caso


necesario.

99
Section 5

Animación

Las aplicaciones Web pueden utilizar la clase WebAnimator para


mover, cambiar el tamaño, rotar, escalar y cambiar la opacidad Animator1.Move(Me, 100, 100, 0.5)
de los controles en una página web en tiempo de ejecución.

Para animar los controles, añades un control WebAnimator a tu Scale


página web. Esto crea una instancia que aparece en el Shelf. El método Scale escala un control a un porcentaje de su tamaño
Puedes utilizar este WebAnimator para animar cualquiera de los original.
controles de la página web.
Este ejemplo reduce una imagen al 50% de su tamaño original
Con WebAnimator puedes animar un control sólo una vez (como durante un periodo de tiempo de un segundo:
moverlo de una posición a otra) o bien puedes encolar una serie
de animaciones (como girar un control antes de moverlo a una
Animator1.Scale(ImageView1, 50, 50, 1)
nueva posición).

Nota: WebAnimator no está soportado en las aplicaciones desktop.


Resize
Acciones de Animator El métod Resize cambia el tamaño de un control a los píxeles
Move indicados. Este ejemplo cambia el tamaño de una imagen a
100x100 en un perioro de un segundo:
El método Move mueve el control indicado desde una ubicación
a otra. Este ejemplo en el evento Action de un botón lo mueve a
la coordenada 100, 100 durante un periodo de medio segundo: Animator1.Resize(ImageView1, 100, 100, 1)

100
RotateX
RotateX rota un control sobre el eje X la cantidad de grados Animator1.RotateZ(ImageView1, 180, 0.5)
indicada.

Este ejemplo da la vuelta a una imagen (180 grados) en medio SkewX, Skew Y
segundo: El método skew distorsiona un control sobre el eje indicado.

Este ejemplo distorsiona una imagen en 20 grados:


Animator1.RotateX(ImageView1, 180, 0.5)

Animator1.SkewX(ImageView1, 20, 0.5)


RotateY
RotateY rota un control sobre el eje Y la cantidad de grados
Opacity
indicada.
El método Opacity cambia la opacidad al porcentaje indicado
Este ejemplo crea una imagen espejo de una imagen en medio (donde 0 = completamente transparente).
segundo:
Este ejemplo define la opacidad de una imagen a la mitad, para
lograr un efecto de fundido:
Animator1.RotateY(ImageView1, 180, 0.5)

Animator1.Opacity(ImageView1, 50, 0.5)


RotateZ
RotateZ rota un control sobre el eje Z la cantidad de grados
indicada.

Este ejemplo gira una imagen boca abajo durante medio


segundo:

101
Section 6

Portapapeles

La clase Clipboard se utiliza para obtener o añadir datos al


Portapapeles del sistema. Las propiedades y métodos te Dim c As New Clipboard
permiten determinar el tipo de datos que está disponible en el Dim clipPic As Picture
Portapapeles, obtener datos del Portapapeles y enviar datos al If c.PictureAvailable Then
Portapapeles. La clase Portapapeles soporta tres tipos de datos: clipPic = c.Picture
texto, imágenes y datos binarios/en crudo. Los datos binarios End If
están representados en forma de cadena y marcados con el tipo c.Close
que hayas indicado, de modo que puedas saber qué representan
dichos datos binarios.
Añadir una Imagen al Portapapeles
Tratar con Imágenes en el Portapapeles Este ejemplo añade una imagen al portapapeles:
El portapapeles puede contener cualquier imagen que pueda
almacenarse en la clase Picture.
Dim c As New Clipboard
Obtener una Imagen del Portapapeles c.Picture = ImageWell.Image
Dado que el Portapapeles puede contener texto, imágenes y c.Close
datos binarios, debes de preguntar cuál es el tipo de datos que
contiene antes de utilizarlos. Para comprobar si es una imagen,
utiliza el método PictureAvailable:
Datos en Bruto en el Portapapeles
Los datos en bruto se refieren a cualquier dato que no sea texto
o una imagen. Esto te permite poner cualquier tipo de datos en

102
el portapapeles. Sólo tus aplicaciones podrán parsear dichos Este ejemplo almacena información RTF desde un Text Area con
datos, no obstante. estilos en el Portapapeles:

Obtener Datos en Bruto del Portapapeles


Dim c As New Clipboard
Utiliza el método RawData para obtener datos en bruto desde el
Portapapeles. Los datos en bruto están en formato de cadena y
c.AddRawData(TextArea1.StyledText.RTFDat
debes de proporcionar un indicador de tipo, utilizado para a, "RTF")
identificar el tipo de datos que se han de recuperar. c.Close

Este ejemplo obtiene datos RTF del Portapapeles y los almacena


en una Text Area:

Dim c As New Clipboard


If c.RawDataAvailable("RTF") Then
TextArea1.StyledText.RTFData =
c.RawData("RTF")
End If
c.Close

Añadir Datos en Bruto al Portapapeles


Utiliza el método AddRawData para añadir datos en bruto al
portapapeles indicando el tipo.

103
Section 1

Conceptos de Bases de Datos

Las técnicas descritas en el capítulo Archivos son adecuadas Podrías tener una tabla Equipo con las columnas: ID, Nombre,
para tratar con los datos de la aplicación, en la mayoría de los Entrenador, Ciudad. En la Figura 4.1 puedes ver su aspecto.
casos. Pero existen ocasiones en las que necesitas algo más
Note: La columna ID es requerida en la mayoría de las bases de datos y
rápido y más estructurado. Aquí es donde resultan útiles las asegura que cada fila de la tabla se pueda identificar de forma única.
bases de datos.
Relaciones
Tablas, Columnas y Datos Por lo general una base de datos está compuesta por varias
Una base de datos es una forma estructurada de organizar la tablas, y dichas tablas suelen estar relacionadas entre sí de
información. Esto se realiza mediante un concepto denominado algún modo.
“Tablas”. Una tabla es un contenedor para un conjunto común
de datos. Una tabla tiene una o más columnas que definen el Figura 4.2
tipo de datos que pueden contener. Los datos están
almacenados dentro de las tablas como filas. Cada fila es un
conjunto de datos único.

Figura 4.1

105
Por ejemplo, para llevar el seguimiento de los jugadores de cada No estás limitado al soporte incorporado para bases de datos.
equipo debes de tener otra tabla, llamada Jugador, que está También puedes utilizar muchas otras bases de datos, utilizando
relacionada con la tabla Equipo. para ello las librerías de terceros, plugins y componentes.

Observa que la tabla Jugador también tiene su propia columna Hay por lo general dos tipos de bases de datos: embebidas y de
ID. Pero además también tiene una columna TeamID. Esta servidor.
columna define la relación entre Equipo y Jugador. Indica de
Bases de datos Embebidas
forma clara a qué equipo se corresponde un jugador. Por
ejemplo, puedes ver que Bob y Tom tienen el TemaID = 1. En la Una base de datos embebida es una base de datos que está
misma tabla, puedes ver que el equipo con ID = 1 es Seagulls. De guardada junto con tu aplicación, generalmente como archivo o
una serie de archivos. No necesitas instalar ningún otro software
modo que Bob y Tom son del equipo Seagulls.
con el objeto de acceder a la base de datos, el software
Puedes buscar por los equipos de otros jugadores utilizando la necesario está embebido en tu aplicación.
misma técnica.
Las bases de datos embebidas por lo general solo permiten el
La colección de tus tablas se denomina el “esquema de la base acceso simultáneo de un único usuario cada vez.
de datos” o “diseño de base de datos”. SQLite es un ejemplo de base de datos embebida. Este tipo de
base de datos es utilizado con frecuencia por aplicaciones como
Motores de bases de datos los clientes de correo, navegadores web, el software de gestión
Un diseño de base de datos, como el descrito anteriormente se
fotográfica y cualquier otro que necesite gestionar grandes
crea utilizando un “motor de bases de datos”. Este es un cantidades de datos pero que no necesite compartirlos con
producto específico de bases de datos que se utiliza para otros. También puedes embeber una base de datos como SQLite
guardar el diseño de la base de datos. El framework tiene soporte con aplicaciones web que tengan un uso de ligero a medio.
de serie para dichos motores de bases de datos, y a los que nos
referimos generalmente como la base de datos: SQLite, Servidores de bases de datos
PostgreSQL, MySQL, Oracle y Microsoft SQL Server. Los servidores de bases de datos son bases de datos más
potentes que, con frecuencia, tienen características más
avanzadas, siendo la más significativa el acceso múltiple.

106
Algunos ejemplos de bases de datos de servidor son: Nota: Los comandos SQL se escriben generalmente en mayúsculas.
PostgreSQL, MySQL, Oracle and Microsoft SQL Server. SELECT
Una base de datos servidor está instalada por lo general sobre su El comando SELECT se utiliza para recuperar un dato concreto
propio servidor dedicado. Tu aplicación se comunica con este de una o más tablas. Usando el ejemplo Equipo, este comando
servidor. obtiene los nombres de todos los equipos:

Los servidores de bases de datos son utilizados por las


aplicaciones que precisan compartir datos entre múltiples SELECT Name FROM Equipo;
usuarios, como los sistemas de nóminas, facturación o
aplicaciones de tipo empresarial. Los servidores de bases de
INSERT
datos se utilizan por sitios web de gran tamaño como Facebook y
Twitter. El comando INSERT se utiliza para añadir filas a una tabla.
Utilizando el ejemplo Equipo, este comando añade el equipo
SQL (Structured Query Language) Seagulls:
Los motores de bases de datos comparten una estructura de
comandos similar, denominada SQL. Se corresponde con
INSERT INTO Equipo (Name, Coach,
Structured Query Language. Desafortunadamente, SQL no es
City)
idéntico entre las diversas bases de datos que puedas utilizar.
Esto puede hacer que sea un reto cambiar entre motores de
bases de datos (como SQLite y PostgreSQL, por ejemplo), dado
que el SQL utilizado para enviar comandos a la base de datos Observa que en este caso no se incluye la columna ID. Esto es
probablemente sea diferente. porque la mayoría de las bases de datos lo rellenan
automáticamente. Los detalles de funcionamiento de cada base
No obstante, son varios los comandos SQL comunes que de datos varía, en cualquier caso.
siempre están disponibles, incluso si la sintaxis específica cambia
ligeramente en función de la base de datos que estés utilizando.
UPDATE
Estos son: SELECT, INSERT, UPDATE, DELETE, COMMIT and
ROLLBACK.

107
El comando UPDATE se utiliza para modificar las tablas se pierdan los datos. Un fallo revertirá todo a su estado inicial,
existentes de una tabla. Este ejemplo cambia el nombre del una especie de Deshacer.
Coach para los Seagulls:
En segundo lugar, para las bases de datos que puedan tener
varios usuarios, los cambios realizados en una transacción no
UPDATE Equipo son normalmente visibles para otros usuarios hasta que la
SET Coach = ‘Ken’ transacción se marque como completada. Esto evita que la gente
WHERE Team = ‘Seagulls’; pueda ver datos antes de que estén listos.

El inicio de una transacción varía en función de las bases de


datos que estés usando. Algunas bases de datos inician una
DELETE
transacción automáticamente y otras requieren que se ejecute un
El comando DELETE elimina una fila de la tabla. Esta ejemplo
comando específico, como pueda ser:
elimina los Seagulls:

BEGIN TRANSACTION;
DELETE FROM Team WHERE Name = 'Seagulls';

En cualquier caso, para completar una transacción se utiliza el


COMMIT, ROLLBACK (Transacciones) comando COMMIT. Para cancelar (o deshacer) una transacción,
Los cambios a una base de datos se realizan en lo que se utilizas el comando ROLLBACK.
denomina una transacción. Este es un bloque de procesado en
los que o bien todos se completan con éxito o bien ninguno lo
hace.

Cuando estés utilizando una transacción, los cambios realizados


a una base de datos no se realizan de forma permanente hasta
un commit. Esto sirve dos propósitos. En primer lugar, asegura
que siempre se mantiene la integridad de los datos. Si ocurre un
error debido a algunos cambios en varias tablas, no querrás que

108
Section 2

Uso Sencillo de Bases de Datos

Para un uso sencillo de un base de datos, puedes añadir una Usar el Editor de Bases de Datos
base de datos ya existente a tus proyectos usando el comando El Editor de
Insertar > Base Bases de Datos
Figura 4.5
de datos. Figura 4.3 proporciona un
modo de editar
Puede elegir
el esquema de
entre añadir una
la base de datos
bases de datos
para las bases
existente en
de datos SQLite.
Microsoft SQL
Figura 4.4 En la columna situada más a la izquierda ves el nombre de las
Server, MySQL, ODBC, Oracle,
tablas en la base de datos. Cuando se selecciona una tabla, el
PostgreSQL o SQLite. Además puedes
área central muestra las columnas de la tabla.
optar por crear una nueva base de datos
SQLite. Puedes utilizar los botones de la barra de herramientas para
añadir y eliminar las tablas y las columnas.
Cuando se añada una base de datos,
aparecerá en el Navegador con su
Control DataControl
nombre. El control DataControl proporciona una forma fácil y potente de
crear una pantalla de entrada de datos que funciona con la tabla
En el caso de SQLite, al seleccionar la base de datos en el
Navegador se accederá al Editor de Bases de Datos. de una base de datos. El DataControl es un único objeto
compuesto por botones de navegación de registro (Primero,
Previo, Siguiente y Último), y un texto de título en el centro.

109
Con un DataControl puedes mostrar los datos de una base de Los controles que pueden mostrar datos de un DataControl son:
datos sin escribir una línea de código. Sólo has de indicar el TextField, ListBox, PopupMenu, ComboBox y Label.
nombre de la base de datos a usar, la tabla a la cual acceder y el
Para que un TextField muestre el nombre del Artista, indicas los
SQL a ejecutar. A continuación se indica al resto de los controles

Figura 4.8
Figura 4.6

de la ventana que utilicen el DataControl para mostrar la


información o realizar otras acciones.
valores para las propiedades DataSource y DataField en el
Por ejemplo, así es como se configura el DataControl para
Inspector. Estos ajustes son para un TextField denominado
acceder a la información del Artista en la base de datos
ArtistNameField:
ChinookSample:
Con un DataControl y TextField configurados como arriba, al
ejecutar el proyecto se muestra el primer Artista en el TextField.
Figura 4.7 Puedes utilizar los botones de navegación en el DataControl para
desplazarte por los nombres de Artista en la tabla.

En el caso de que quieras controlar la navegación manualmente,


puedes llamar los siguientes métodos del DataControl: MoveFirst,

110
MovePrevious, MoveNext, MoveLast. También puedes saltar a
una fila concreta utilizando MoveTo. ChinookDataControl.Update
ChinookDataControl.RunQuery
Además de mostrar los datos, el DataControl también puede
manipular los datos en la tabla. Puedes añadir nuevas filas,
actualizar las filas existentes y borrar filas. Añadir una nueva fila requiere de algunos pasos adicionales. En
primer lugar, necesitarás un botón para crear una nueva fila.
Para ello, has de llamar a los métodos apropiados de la clase
Luego podrás poner este código en su manejador de evento
DataControl: NewRecord, Insert, Update, Delete
Action:
Por lo general llamas a estos métodos desde botones que
realizan las acciones. Por ejemplo, para borrar la fila que se esté ArtistDBControl.NewRecord
mostrando, pon este código en un botón Borrar: ArtistNameField.Text = ""

ChinookDataControl.Delete
Este código crea una nueva fila y borra el TextField de modo que
ChinookDataControl.RunQuery
puedas escribir el nuevo nombre del artista.

Para guardarlo en la base de datos, necesitas otro botón, quizá


El método RunQuery vuelve a alimentar el DataControl de modo
llamado Guardar, con este código:
que no tenga las filas borradas. Generalmente llamas a RunQuery
después de haber realizado algo que haya cambiado los datos en
el DataControl. ArtistDBControl.Insert
ArtistDBControl.RunQuery
De igual modo, este código te permite editar el nombre mostrado
y actualizar la fila en la base de datos:

111
Section 3

SQLite

Acerca de SQLite Data del sistema operativo. En las aplicaciones web, la base de
SQLite es el motor de base de datos incluido de serie. Puedes datos está por lo general junto con la propia aplicación web.
acceder a él utilizando la clase SQLiteDatabase.
Utilizas la clase SQLiteDatabase para trabajar con bases de
SQLite es una base de datos embebida de código abierto y de datos SQLite. Este código crea una nueva base de datos SQLite
dominio público utilizada por todo tipo de software. Es ligera, en la carpeta Application Data:
rápida y fácil de usar. Funciona genial tanto en aplicaciones de
escritorio como web.
Dim dbFile As FolderItem
A diferencia de la mayoría de bases de datos, SQLite no tiene dbFile =
columnas estrictamente tipadas. Puedes poner datos de SpecialFolder.ApplicationData.Child(
cualquier tipo en cualquier columna, sin importar de cómo se "MyDatabase.sqlite")
haya definido dicha columna.
Dim db As New SQLiteDatabase
Puedes aprender más sobre SQLite visitando su sitio web: db.DatabaseFile = dbFile
If db.CreateDatabaseFile Then
www.SQLite.org
// Use the database
Crear Bases de datos End If
Las bases de datos SQLite son un único archivo que se
encuentran generalmente en el mismo ordenador sobre el que se
Si la base de datos ya existe, entonces CreateDatabaseFile
ejecuta la aplicación. En las aplicaciones de escritorio, la base
conectará con la base de datos existente.
de datos SQLite está normalmente en la carpeta Application

112
Crear una Tabla Claves Principales con Incremento
El siguiente comando SQL crea la tabla Equipo usada en los Automático
anteriores ejemplos: Con SQLite, si una tabla tiene una columna indicada como la
clave principal INTEGER, entonces dicha columna se incrementa
automáticamente cada vez que se añade una nueva fila. Esta
CREATE TABLE Team (ID INTEGER, Name
columna está mapeada internamente con la columna rowid que
TEXT, Coach TEXT, City TEXT, PRIMARY
está en todas las tablas SQLite.
KEY(ID));
Sólo porque SQLite tenga una columna rowid no debes de
confiar en ella como clave principal. Los valores de Rowid
Conectar a una Base de datos pueden cambiar inadvertidamente, y esto podría causar la
Si estás conectando a una base de datos que ya existe, puedes
corrupción entre las relaciones de la base de datos. Siempre has
llamar simplemente al método Connect:
de crear una clave principal independiente. Cuando haces un
INSERT de datos en una tabla con clave principal, se omite dicha
Dim dbFile As FolderItem clave principal en el SQL de INSERT:
dbFile =
SpecialFolder.ApplicationData.Child(
INSERT INTO Team (Name)
"MyDatabase.sqlite") VALUES ('Seagulls');

If dbFile.Exists Then
Dim db As New SQLiteDatabase Después de añadir una fila a la base de datos, puedes obtener el
db.DatabaseFile = dbFile valor de la última clave principal con el método LastRowID:
If db.Connect Then
// Use the database
Dim lastValue As Integer
End If
lastValue = db.LastRowID
End if

113
Encriptación
Las bases de datos SQLite pueden estar cifradas. Una base de Dim dbFile As FolderItem
datos cifrada no puede verse salvo que conozcas las clave de dbFile =
cifrado. SpecialFolder.ApplicationData.Child(
"MyDatabase.sqlite")
Cifrando una Base de datos
Para cifrar una nueva base de datos, indica un valor para la Dim db As New SQLiteDatabase
propiedad EncryptionKey antes de llamar a CreateDatabaseFile o db.DatabaseFile = dbFile
antes de llamar a Connect. If db.CreateDatabaseFile Then
// Encrypt the database
db.Encrypt("MySecretKey123!")
Dim dbFile As FolderItem
End If
dbFile =
SpecialFolder.ApplicationData.Child(
"MyDatabase.sqlite") También puedes usar el método Encrypt para cambiar la clave de
cifrado sobre cualquier base de datos cifrada.
Dim db As New SQLiteDatabase
db.DatabaseFile = dbFile Descrifrar una Base de datos
db.EncryptionKey = "MySecretKey123!" Para descifrar una base de datos cifrada, llama al método
If db.CreateDatabaseFile Then Decrypt después de haber conectado con la base de datos:
// Use the database
End If

Para cifrar una base de datos ya existente, llama al método


Encrypt, proporcionando la clave de cifrado como parámetro:

114
datos utilizando la clase DatabaseRecord, pero hay una limitación
Dim dbFile As FolderItem sobre la cantidad de memoria disponible.
dbFile =
Si necesitas almacenar grandes objetos en una base de datos,
SpecialFolder.ApplicationData.Child(
pero quieres poder leer los datos secuencialmente de la base de
"MyDatabase.sqlite")
datos, utiliza la clase SQLiteBlob en combinación con los
métodos CreateBlob y OpenBlob de la clase SQLiteDatabase:
Dim db As New SQLiteDatabase
db.DatabaseFile = dbFile
db.EncryptionKey = "MySecretKey123!" Dim blob As SQLiteBlob
If db.CreateDatabaseFile Then blob = db.OpenBlob("Team", "Logo", 1, True)
db.Decrypt // Decrypt the database If blob <> Nil Then
End If // Read BLOB
Dim data As String
While Not blob.EOF
Soporte para Múltiples Usuarios // Read 1000 bytes at a time
SQLite no es técnicamente una base de datos de usuarios
data = blob.Read(1000)
múltiples; pero activando una característica denominada Write-
Wend
Ahead Logging (WAL) puedes mejorar el rendimiento cuando son
múltiples usuarios los que acceden a la base de datos.
// Do something with the data
Esto es más útil para las aplicaciones web, dado que pueden End If
tener varios usuarios conectados a la aplicación web, cada uno
de ellos utilizando la base de datos.
Conectar otras Bases de datos SQLite
Objetos de gran tamaño Por lo general, cuando conectas a una base de datos SQLite
Los objetos de gran tamaño se denominan BLOBs (Binary Large estás conectando con un único archivo. Con SQLite es posible
Objects). Puedes añadir objetos de gran tamaño a la base de

115
conectar con múlltiples archivos de bases de datos SQLite. Esto
se logra “conectando” las bases de datos adicioanles.

El método AttachDatabase vincula el archivo de base de datos


indicado y te permite asignar un prefijo para usar con todas las
tablas de la base de datos vinculada.

El método DetachDatabase permite desvincular la base de datos


vinculada.

dbFile = GetFolderItem("ExtraDB.sqlite")
If db.AttachDatabase(dbFile, "extra") Then
Dim rs As RecordSet
rs = db.SQLSelect("SELECT * FROM
extra.Table")
// Process results...
End If

Determinar la Versión SQLite


Algunas veces puede resultar útil saber exáctamente qué versión
de SQLite se está usando en la aplicación. Puedes comprobarlo
usando la propiedad LibraryVersion.

MsgBox(db.LibraryVersion)

116
Section 4

PostgreSQL

Acerca de PostgreSQL Con esta información puedes conectar con la base de datos en
PostgreSQL es un servidor de bases de datos gratuito, potente, el servidor utilizando la clase PostgreSQLDatabase:
multiplataforma y de código abierto. Para usarlo debes de
asegurarte de que esté instalado el archivo plugin
Dim db As New PostgreSQLDatabase
PostgreSQLPlugin.rbx en la carpeta Plugins.
db.Host = "192.168.1.172"
Puedes aprender más sobre PostgreSQL en su sitio web: db.Port = 5432
db.DatabaseName = "BaseballLeague"
www.PostgreSQL.org db.UserName = "broberts"
db.Password = "streborb"
Conectar a PostgreSQL
If db.Connect Then
Para conectar a PostgreSQL debes de tener instalado un
// Use the database
servidor PostgreSQL en tu ordenador o en un servidor accesible.
Else
Debes de saber varias cosas sobre esta instalación, incluyendo:
// DB Connection error
• La dirección IP o nombre del Host MsgBox(db.ErrorMessage)
End If
• El puerto que se utilizará (por lo general, el 5432)

• El nombre de la base de datos en el servidor Conexiones Seguras


• El nombre de usuario para conectar con el servidor También puedes conectar a una base de datos PostgreSQL
usando SSL para obtener una conexión segura. Para ello, usa la
• La contraseña a utilizasr para conectar con el servidor

117
propiedad SSLMode para indicar el tipo de conexión segura a VARCHAR y que permite indicar un tamaño máximo para la
utilizar: cadena:

Dim db As New PostgreSQLDatabase CREATE TABLE Equipo (ID INTEGER NOT


db.Host = "192.168.1.172" NULL, Name VARCHAR(100), Coach
db.SSLMode = VARCHAR(100), City VARCHAR(100));
PostgreSQLDatabase.SSLRequire
db.Port = 5432
db.DatabaseName = "BaseballLeague" Claves Principales con Incremento
db.UserName = "broberts" Automático
db.Password = "streborb" PostreSQL no te permite crear una clave principal que se
If db.Connect Then autoincremente. Pero la funcionalidad equivalente está disponible
utilizando Sequences.
// Use the database
End If Una Sequence es un objeto de base de datos que gestiona
valores únicos para su uso como claves principales. Utiliza esta
secuencia cuando crees nuevas filas en la tabla.
Crear una Tabla
Este SQL crea la tabla Equipo utilizada en los anteriores Este SQL declara una secuencia para la tabla Equipo con valores
ejemplos: a partir de 1:

CREATE TABLE Equipo (ID INTEGER NOT CREATE SEQUENCE TeamSeq START 1;
NULL PRIMARY KEY, Name TEXT, Coach
TEXT, City TEXT); Utiliza la Sequence en las instrucciones SQL INSERT, como
sigue:
En vez del tipo de datos TEXT, que permite una cantidad ilimiada
de texto en una cadena, también puedes usar el tipo de dato
118
INSERT INTO Equipo (ID, Name) db.SQLExecute("BEGIN TRANSACTION")

VALUES (nextval('TeamSeq'), 'Seagulls');


// Create the Large Object and save its reference
Dim oid As Integer
oid = db.CreateLargeObject
Las funciones nextval, lastval y currval se utilizan para acceder al
siguiente valor en la secuencia, el último valor en la secuencia y // Open the newly created Large Object
Dim lo As PostgreSQLLargeObject
el valor actual de la secuencia, respectivamente.
lo = db.OpenLargeObject(oid)

Objetos de gran tamaño // Write the file to the Large Object


Los objetos de gran tamaño, o BLOBS, te permiten almacenar Dim bs As BinaryStream
bs.Open(inputFile)
datos no tradicionales en la base de datos, como archivos,
Dim data As String
imágenes y cualquier otra cosa que sea binaria. Los Objetos de While Not bs.EOF
gran tamaño se almacenan independientemente de las tablas y data = bs.Read(1000)
lo.Write(data)
son referenciados mediante su propio identificador único. Para
Wend
trabajar con BLOBs en PostgreSQL, has de usar la clase bs.Close
PostgreSQLLargeObject class. lo.Close
db.SQLExecute("END TRANSACTION")
Este ejemplo guarda un archivo en un LargeObject:

Nota: PostgreSQL requiere que todas las operaciones de objeto de gran


tamaño se realicen dentro de una transacción, tal y como se muestra en el
anterior ejemplo.

Notificaciones
Otra característica de PostgreSQL es el protocolo Listen y Notify.
Con Listen y Notify, puedes dejar que el Servidor PostgreSQL te
notifique de los eventos para los que estés escuchando.

119
Para escuchar las notificaciones has de llamar al método Listen
con el nombre de la notificación que quieres escuchar:

db.Listen("LogInNotification")

Para interrogar realmente al Servidor PostgreSQL si se ha


producido cualquiera de las notificaciones por las que se está
escuchando, ha de llamarse al método CheckForNotifications.
Por lo general harás esto dentro de un temporizador de modo
que tu aplicación compuebe las notificaciones de forma regular:

db.CheckForNotifications

Cuando lleguen las notificaciones reales, se llama


ReceivedNotificationEvent con el nombre de la notificación.

Envía notificaciones usando el método Notify así:

db.Notify("LogInNotification")

120
Section 5

MySQL

Acerca de MySQL Necesitarás saber varias cosas sobre esta instalación,


MySQL es un servidor de bases de datos potente, incluyendo:
multiplataforma y de código abierto.
• La dirección IP del Host o su nombre
Para usarlo, necesitas el archivo de plugin
• El Puerto que se utilice (generalmente, el 3306)
MySQLCommunityServerPlugin.rbx plugin en la carpeta de
Plugins. El plugin soporta la conexión con MySQL Community • El nombre de la base de datos en el servidor
Edition en Windows, OS X y Linux.
• el nombre de usuario para conectar con la base de datos
Puedes obtener más información sobre MySQL en su sitio web:
• La contraseña para conectar con el servidor
www.MySQL.com
Con esta información puedes conectar con la base de datos en
Licencia el servidor utilizando la clase MySQLCommunityServer:
La licencia de MySQL es más compleja que la de otros
servidores de bases de datos. Para obtener más información
sobre las opciones de licencia, dirígete a su sitio web en:

http://www.mysql.com/about/legal/licensing/index.html

Conectando a MySQL
Para conectar a MySQL debes de utilizar un servidor MySQL
instalado bien en tu ordenador o bien en un servidor accesible.

121
Dim db As New MySQLCommunityServer Dim db As New MySQLCommunityServer
db.Host = "192.168.1.172"
db.Host = "192.168.1.172"
db.Port = 3306
db.Port = 3306 db.DatabaseName = "BaseballLeague"
db.DatabaseName = "BaseballLeague" db.UserName = "broberts"
db.Password = "streborb"
db.UserName = "broberts"
db.SSLMode = True
db.Password = "streborb"
If db.Connect Then Dim keyFile As FolderItem
keyFile = GetFolderItem("MySQLKeyFile")
// Use the database
db.SSLKey = keyFile
Else
// Connection error Dim certFile As FolderItem
certFile = GetFolderItem("MySQLCertificateFile")
MsgBox(db.ErrorMessage)
db.SSLCertificate = certFile
End If
Dim authFile As FolderItem
authFile = GetFolderItem("MySQLAuthFileFile")
db.SSLAuthority = authFile
Conexión Segura
Dim authPath As FolderItem
También puedes conectar con una base de datos MySQL usando
authPath = GetFolderItem("SSLCACertFile")
SSL para obtener una conexión segura. Para ello, utiliza la db.SSLAuthorityDirectory = authPath
propiedad SSLMode (y opcionalment otras propiedades SSL)
Dim cipher As String
para indicar el tipo de conexión segura a utilizar:
cipher = "DHE-RSA-AES256-SHA"
db.SSLCipher = cipher

If db.Connect Then
// Use the database
End If

122
Crear una Tabla
Este SQL crea la tabla Equipo utilizada en anteriores ejemplos: INSERT INTO Team (Name)
VALUES ('Seagulls');

CREATE TABLE Equipo (ID INTEGER NOT


NULL AUTO_INCREMENT PRIMARY KEY, Después de añadir una fila a la base de datos, puedes obtener el
Name TEXT, Coach TEXT, City TEXT); valor de la última clave primaria llamando al método GetInsertID:

En lugar del tipo de dato TEXT, que permite una cantidad Dim lastValue As Integer
ilimitada de texto, también puedes usar el tipo de dato VARCHAR lastValue = db.GetInsertID
que permite indicar el tamaño máximo para la cadena:

CREATE TABLE Equipo (ID INTEGER NOT


NULL AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(100), Coach
VARCHAR(100), City VARCHAR(100));

Claves Primarias con Incremento Automático


Si una tabla tiene el atributo AUTO_INCREMENT asignado a una
clave primaria, entonces dicha columna se incrementará
automáticamente cuando se añada una nueva fila a la tabla.

Cuando se añadan datos con INSERT a una tabla con clave


primaria, has de omitir la clave primaria del SQL INSERT:

123
Section 6

Oracle

Acerca de las Bases de datos Oracle • El nombre de la base de datos en el servidor


La base de datos Oracle (por lo general simplemente Oracle) es
• El nombre del usuario para conectar con el servidor
un potente servidor de bases de datos utilizado por lo general en
compañías de gran tamaño. Funciona con Windows y Linux. • La contraseña a usar para conectar con el servidor
Están disponibles tanto versiones comerciales como gratuitas.
Con esta información puedes conectar con la base de datos en
Necesitas una copia del archivo de plugin OraclePlugin.rbx en la el servidor usando la clase OracleDatabase:
carpeta de Plugins. El plugin soporta la conexión a bases de
datos Oracle desde Windows, OS X y Linux.
Dim db As New OracleDatabase
Para obtener más información sobre las bases de datos Oracle, db.DatabaseName = "BaseballLeague"
dirígete a su sitio web en: db.UserName = "broberts"
db.Password = "streborb"
www.Oracle.com
If db.Connect Then
// Use the database
Conectar a Oracle
Para conectar a Oracle, has de tener un servidor Oracle instalado End If
en tu ordenador o bien en un servidor accesible. Cada ordenador
que se vaya a conectar a la Base de datos Oracle ha de tener
instalado el cliente Oracle OCS 9i (o posterior). Depurando la Conexión
Dado que Oracle puede resultar un tanto más compleja de
Tendrás que saber varias cosas sobre esta instalación, configurar en comparación con otras bases de datos, puede
incluyendo: resultar útil activar el modo de depuración. Define la propiedad

124
Debug a True antes de conectar para registrar los mensajes en la
consola: CREATE SEQUENCE TeamSeq START 1;

db.Debug = 1 Usa la secuencia en las instrucciones INSERT SQL así:

Crear una Tabla INSERT INTO Equipo (ID, Name)


VALUES (TeamSeq.nextval, 'Seagulls');
Este SQL crea la tabla Equipo usada en anteriores ejemplos:

CREATE TABLE Equipo (ID INTEGER NOT Las funciones nextval y currval se utilizan para acceder al
NULL PRIMARY KEY, Name siguiente valor en la secuencia y al valor actual de la secuencia,
VARCHAR2(100), Coach VARCHAR2(100), respectivamente.

City VARCHAR2(100)); Tabla Dual


En otras bases ded atos, la parte FROM de una instrucción
SELECT es opcional, permitiéndote escribir SQL como este para
Claves Primarias con Incremento Automático
obtener el siguiente valor de la secuencia:
Oracle no te permite crear una clave primaria con incremento
automático. La funcionalidad equivalente está disponible usando
Sequences (similares a como funciona PostgreSQL). SELECT TeamSeq.nextval;
Una Sequence es un objeto de base de datos que gestiona
valores únicos para su uso con las claves primarias. Utilizas las O SQL como este para realizar un cálculo:
secuencias al crear nuevas filas en una tabla.

Este SQL declara una secuencia para la tabla Equipo con valores SELECT 5*5;
que parten desde 1:

125
Oracle no soporta el FORM opcional, pero proporciona una tabla
“dummy”, denominada DUAL, que puedes utilizar para este
propósito:

SELECT TeamSeq.nextval FROM DUAL;


SELECT 5*5 FROM DUAL;

126
Section 7

Microsoft SQL Server

Acerca de Microsoft SQL Server • El nombre de la base de datos en el servidor


Microsoft SQL Server (MSSQL) es una base de datos potente
• El nombre de usuario para conectar al servidor
utilizada comunmente en grandes compañías donde se utilizan
las herramientas de Microsoft. Funciona con Windows y están • La contraseña para conectar al servidor
disponibles versiones gratuitas y comerciales.
Con esta información puedes conectar a la base de datos en el
Para utilizarla necesitas una copia del plugin servidor utilizando la clase MSSQLServer:
MSSQLServerPlugin.rbx en la carpeta Plugins. El plugin soporta
la conexión a Microsoft SQL desde Windows.
Dim db As New MSSQLServerDatabase
Para obtener más información sobre Microsoft SQL Server, db.Host = "192.168.1.172"
dirígete a su sitio web en: db.DatabaseName = "BaseballLeague"
db.UserName = "broberts"
www.microsoft.com/sqlserver
db.Password = "streborb"
If db.Connect Then
Conectar a Microsoft SQL Server
Para conectar a Microsoft SQL Server (MSSQL) has de tener un // Use the database
servidor MSSQL instalado en tu ordenador o bien sobre un End If
servidor accesible. Has de conocer los siguiente sobre dicha
instalación, incluyendo:
Crear una Tabla
• La dirección IP del host o su nombre Este SQL crea la tabla Team usada en los anteriores ejemplos:

127
Tras añadir una fila a la base de datos, puedes obtener el valor de
CREATE TABLE Team (ID INT NOT NULL la última clave primera accediendo a la columna @@IDENTITY
IDENTITY PRIMARY KEY, Name TEXT, mediante una instrucción SELECT:
Coach TEXT, City TEXT);

db.SQLExecute("SELECT @@IDENTITY")
En vez del tipo de dato TEXT, que permite una cantidad de texto
ilimitada, también puedes utilizar el tipo de datos VARCHAR con
el que se puede indicar el tamaño máximo para la cadena:

CREATE TABLE Team (ID INT NOT NULL


IDENTITY PRIMARY KEY, Name VAR-
CHAR(100), Coach VARCHAR(100), City
VARCHAR(100));

Claves Primarias con Auto-Incremento


Si una tabla tiene asignada el atributo IDENTITY a su clave
primaria, entonces dicha columna se auto-incrementará cada vez
que se añada una nueva fila a la tabla.

Cuando hagas un INSERT de datos en una tabla con clave


primaria, has de omitir la clave primaría en el SQL INSERT:

INSERT INTO Team (Name)


VALUES ('Seagulls');

128
Section 8

Otras Bases de datos

Además de las bases de datos que tienen soporte de serie, • MonkeyBread Plugins (http://www.monkeybreadsoftware.de)
descritas en las anteriores secciones, puedes conectar MonkeyBread ofrece plugins para conectar con bases de datos
prácticamente con cualquier otra base de datos mediante una mediante JDBC (Java Database Connectivity), un estandar de
serie de métodos. conexión a base de datos similar a ODBC. Adicionalmente,
tienen plugins para conectar directamente a una amplia
ODBC (descrito en la siguiente sección) te permite conectar con
variedad de bases de datos.
cualquier base de datos para la que tengas un controlador
ODBC. • Studio Stable Database (http://www.studiostable.com)
Studio Stable Database es un servidor ligero de bases de
Otras bases de datos que puedes utilizar son:
datos basado en SQLite.
• CubeSQL (http://www.sqlabs.com)
Servidor de base de datos multiplataforma basado en SQLite.

• Valentina Database (http://www.valentina-db.com)


Base de datos columnar multiplataforma (tanto embebida
como servidor).

• OpenBase SQL (http://www.openbase.com)


Base de datos multiplataforma originalmente creada para
OpenStep/NeXT.

129
Section 9

ODBC

Acerca de ODBC las credenciales necesarias para Figura 4.10


ODBC (Open Database Connectivity) es un controlador de bases conectar con la base de datos.
de datos disponible sobre Windows, OS X y Linux. Mediante el
Esto resulta en un DSN o Data
plugin ODBC puedes conectar con cualquier base de datos para
Source Name. Entonces puedes
la que tengas un controlador ODBC. Los controladores ODBC
utilizar este DSN para conectar a
están disponibles para prácticamente cualquier base de datos.
la base de datos en
Para usar ODBC, necesitas una copia del archivo combinación con la clase
ODBCPlugin.rbx en la carpeta Plugins. ODBCDatabase de dos formas.
Puedes indicar que tu aplicación
Conectar a una Base de datos con ODBC solicite al usuario que elija un
El modo en el que conectes con DNS utilizando el selector del sistema operativo o bien puedes
una base de datos ODBC Figura 4.9 proporcionar el nombre del DSN manualmente.
depende de la base de datos
que estés utilizando. Lo primero Este código proporciona una propiedad DataSource en blanco,

que has de hacer es configurar solicitando el DSN al usuario:

el controlador ODBC usando la


herramienta de configuración
ODBC adecuada para tu
sistema operativo. En esta
herramienta instalas el
controlador ODBC e introduces

130
Por último, si conoces el formato preciso utilizado por el
Dim db As New ODBCDatabase controlador ODBC, puedes crear un DSN manualmente:
db.DataSource = ""
If db.Connect Then Dim db As New ODBCDatabase
db.DataSource = "DSN=TestDSN;UID=broberts;PWD=streborb"
// Use the database
Else If db.Connect Then
// Connection error // Use the database
Else
// or the user selected
// Connection error
// Cancel from the ODBC browser End If
End If

Fuentes para Controladores ODBC


Como alternativa puedes proporcionar el nombre DSN junto con Antes de que puedas conectar con cualquier base de datos
las credenciales. Este ejemplo utiliza un TestDSN ya existente mediante ODBC, necesitarás obtener un controlador ODBC.
para conectar con la base de datos: Varios fabricantes de bases de datos ofrecen de forma gratuita
dichos controladores OBDC. Otras fuentes son las siguientes:

Dim db As New ODBCDatabase • Actual Technologies (http://www.actualtech.com)


db.DataSource = "TestDSN"
db.UserName = "broberts" • OpenLink (http://www.openlinksw.com)
db.Password = "streborb"
• DataDirect (http://www.datadirect.com)
If db.Connect Then
// Use the database
Else
// Connection error
End If

131
Section 10

Operaciones con Bases de Datos

Una vez has conectado a una base de datos (consulta las Este ejemplo obtiene el nombre de todos los equipos y los añade
anteriores secciones para la base de datos que estés utilizando), al ListBox:
el proceso de realizar operaciones comunes con la base de
datos es prácticamente siempre idéntico, con independencia del
Dim rs As RecordSet
motor de base de datos utilizado. Por lo general necesitas rs = db.SQLSelect("SELECT * FROM Team")
realizar estas acciones: If rs <> Nil Then
While Not rs.EOF
• Recuperar datos ListBox1.AddRow(rs.Field("Name").StringValue)
rs.MoveNext
Wend
• Añadir datos
rs.Close
Else
• Cambiar datos
// Check if there was an error
If db.Error Then
• Eliminar datos MsgBox(db.ErrorMessage)
End If
Recuperar Datos de la Base de datos End If

RecordSet
La clase RecordSet es la utilizada para recuperar datos de la El método SQLSelect devuelve un RecordSet. Deberías de
base de datos. Se utiliza el comando SQL SELECT para obtener comprobar siempre si el RecordSet es Nil antes de intentar
datos desde una o más tablas, utilizando a continuación el utilizarlo. Un RecordSet podría ser Nil debido a un error de la
RecordSet para iterar por los resultados. base de datos o bien por algo tan simple como un error
tipográfico en la sentencia SELECT. Si es Nil, debes comprobar

132
el error generado por la base de datos, algo que debe realizarse hacerlo con las aplicaciones Web. Debido a una técnica de
en la cláusula Else. hacking denominada “Inyección SQL”, querrás asegurarte de
usar las Prepared SQL Statments para asegurarte de que tu SQL
El bucle While itera por las filas del RecordSet hasta que se llega sea más seguro.
al final (EOF significa End Of File, fin de archivo).
Prepared SQL Statements y Enlace de Base de datos
El método Field se utiliza para obtener el valor de una columna en
Cada clase de base de datos tiene su propia clase
particular para la fila en curso del RecordSet, en este caso la PreparedSQLStatment. Un Prepared SQL Statment pasa el SQL y
columna Name. sus argumentos de forma separada a la base de datos, quien
posteriormente los combina para crear la instrucción SQL sin que
Es muy importante llamar a rs.MoveNext, acción que mueve la
se vea afectada por una inyección SQL.
actual fila del RecordSet a la siguiente. Si olvidas hacerlo
causaría un “bucle infinito” dado que el RecordSet permanecería Este es un ejemplo de como puedes obtener los nombres de
en la misma fila, añadiéndose una y otra vez al ListBox (o hasta todos los jugadores del equipo Seagulls sin usar las Prepared
que el equipo se quede sin memoria). SQL Statments:

El método Field se utiliza para obtener los valores de la columna


Dim sql As String
en base al nombre de la columna. También puedes utilizar el
sql = "SELECT * FROM Player WHERE Team = 'Seagulls'"
método IdxField para obtener los valores de la columna
basándote en la posición de la columna en la instrucción SELECT Dim rs As RecordSet
rs = db.SQLSelect(sql)
(basado en 1).

SQL
Dado que utilizas SQLSelect para obtener RecordSets, tu SQL Con una Prepared SQL Statement, sólo has de suministrar una
consiste principalmente de instrucciones SELECT. reserva para el parámetro ‘Seagulls’ (generalmente una “?”,
aunque varía en función de la base de datos que estés
Puedes limitarte a suministrar el SQL directamente como una utilizando). El valor para el parámetro se suministra
cadena (tal y como se ha hecho en el ejemplo anterior), y que posteriormente:
funciona bien para las aplicaciones de escritorio. Pero no querrás

133
Dim sql As String Dim sql As String
sql = "SELECT * FROM Player WHERE Team = ? SELECT * sql = "SELECT * FROM Player WHERE Team = ? SELECT *
FROM Player WHERE Team = ?" FROM Player WHERE Team = ?"

Dim ps As SQLitePreparedStatement Dim ps As SQLitePreparedStatement


ps = db.Prepare(sql) ps = db.Prepare(sql)
// Identify the type of the first parameter // Identify the type of the first parameter
ps.BindType(0, SQLitePreparedStatement.SQLITE_TEXT) ps.BindType(0, SQLitePreparedStatement.SQLITE_TEXT)
// Bind the first parameter to a value
ps.Bind(0, "Seagulls") Dim rs As RecordSet
rs = ps.SQLSelect("Seagulls")
Dim rs As RecordSet
rs = ps.SQLSelect

Las clases específicas de SQLPreparedStatement para cada


base de datos son:
Como puedes ver este código es un poco más largo debido a las
líneas adicionales encargadas de indicar el tipo de cada • SQLitePreparedStatement
parámetro (los tipos están disponibles como constantes para la
• PostgreSQLPreparedStatement
clase de instrucciones preparadas) y para vincular el valor de
cada parámetro. Pero todo esto merece la pena dado que es • MySQLPreparedStatement
mucho más seguro en comparación a utilizar SQL plano.
• OracleSQLPreparedStatement
Puedes simplificarlo de algún modo proporcionando los valores
como parte de la llamada SQLSelect, de este modo: • MSSQLServerPreparedStatement

• ODBCPreparedStatement

Añadir datos a la Base de datos


Puedes aladir nuevos datos a tu base de datos utilizando dos
métodos diferentes. Puedes utilizar la clase DatabaseRecord para

134
añadir nuevas filas a la base de datos. O puedes utilizar Por lo general, la sintaxis INSERT tiene este aspecto:
directamente la instrucción SQL INSERT.

DatabaseRecord INSERT INTO table (column1, column2)

La clase DatabaseRecord se utiliza para crear nuevas filas en una


VALUES (value1, value2);
tabla específica. Usa los diversos métodos “Column” de la clase
para asignar valores a las columnas. Luego, llama al método Puedes crear este comando INSERT utilizando la concatenación
InsertRecord para insertar el registro en la tabla indicada: de cadenas:

Dim row As New DatabaseRecord Dim tableName = "Team"


row.Column("Name") = "Seagulls" Dim teamName = "Seagulls"
Dim coachName = "Mike"
row.Column("Coach") = "Mike"
Dim cityName = "Albany"
row.Column("City") = "Albandy" Dim insertSQL As String
insertSQL = "INSERT INTO " + tableName + "(" + _
"Name, Coach, City) VALUES ('" + _
db.InsertRecord("Team", row)
teamName + "', '" + coachName + "'," + cityName + _
If db.Error Then ")"
MsgBox(db.ErrorMessage)
End If
Pero realmente deberías de utilizar un PreparedSQLStatement
para obtener mejores resultados y un código más simple:
Este método funciona sobre cualquier base de datos y no
requiere de cambios en la aplicación si decides utilizar un motor
de base de datos diferente.

SQL
También puedes crear manualmente el SQL para la instrucción de
inserción pero has de utilizar el INSERT correcto para la base de
datos que estés utilizando.
135
Dim tableName = "Team" Dim rs As RecordSet
Dim teamName = "Seagulls" rs = db.SQLSelect("SELECT * FROM Team")
Dim coachName = "Mike" If rs <> Nil Then
Dim cityName = "Albany" While Not rs.EOF
Dim insertSQL As String rs.Edit
insertSQL = "INSERT INTO " + tableName + "(" + _ If Not db.Error Then
"Name, Coach, City) VALUES (?, ?, ?)" rs.Field("Name").StringValue = "Billy"
Dim ps As SQLitePreparedStatement rs.Update
ps = db.Prepare(insertSQL) Else
ps.SQLExecute(teamName, coachName, cityName) MsgBox(db.ErrorMessage)
End If

rs.MoveNext
Cambiar Datos ya Existentes Wend
rs.Close
Editar y Actualizar un RecordSet Else
// Check if there was an error
A medida que avanzas por un RecordSet puedes optar por editar
If db.Error Then
la fila actual y guardar de nuevo los cambios en la base de datos. MsgBox(db.ErrorMessage)
Puedes hacer esta operación llamando al método Edit del End If
RecordSet. Esto hace que la fila actual sea editable de modo que End If
puedes cambiar cualquiera de los valores de sus columnas.

Después de que cambies los valores, puedes actualizar la base SQL


de datos con estos cambios:
Por supuesto, también puedes usar SQL directo para ello. Como
con cualquier otro SQL, has de crear el comando UPDATE para
que se corresponda con la sintaxis requerida por tu base de
datos. Por lo general tiene este aspecto:

136
Usas SQLExecute para borrar datos:
UPDATE table
SET column1 = value1
Dim deleteSql As String
WHERE column2 = value2; deleteSQL = "DELETE FROM Team WHERE Name = 'Seagulls'"
db.SQLExecute(deleteSQL)
If db.Error Then

Borrar Datos MsgBox(db.ErrorMessage)


End If
Borrar con un RecordSet
Puedes borrar la fila actual de un RecordSet llamando al método
DeleteRecord: Gestión de Errores
Como has podido advertir en varios de los ejemplos, cuando se
trabaja con bases de datos es esencial realizar una gestión
rs.DeleteRecord adecuada de los errores.

Sin la gestión de errores no tendrías forma de saber si los


SQL comandos ejecutados contra la base de datos se han realizado
El SQL para borrar datos es relativamente simple: correctamente, causando así la posible pérdida de información o
cuelgues en la aplicación.

DELETE FROM table WHERE column = value Siempre has de comprobar la propiedad Error tras cada comando
de base de datos. Si la propiedad Error está a True, entonces la
propiedad ErrorMessage contiene una descripción del error, y
que debes de mostrar o bien escribir en un log.

Obtener Información Sobre la Base de datos


En ocasiones puede resultar útil obtener información sobre la
base de datos, como por ejemplo las tablas que incluye y las
columnas e indexación de las tablas. La clase Database tiene tres

137
métodos para devolver esta información: TableSchema,
FieldSchema y IndexSchema. Dim columns As RecordSet
columns = db.FieldSchema("Team")
If columns <> Nil Then
TableSchema
While Not columns.EOF
El método TableSchema de la clase database devuelve un ListBox1.AddRow(columns.IdxField(1).StringValue,
RecordSet con una columna que contiene los nombres de todas columns.IdxField(2).StringValue,
columns.IdxField(3).StringValue,
las tablas en la base de datos.
IdxField(4).StringValue, IdxField(5).StringValue)
columns.MoveNext
Este ejemplo añade el nombre de las tablas a un ListBox:
Wend
columns.Close
End If
Dim tables As RecordSet
tables = db.TableSchema
If tables <> Nil Then
While Not tables.EOF Nota: FieldType es un Integer que describe el tipo de dato para la columna.
ListBox1.AddRow(tables.IdxField(1).StringValue) La Referencia del Lenguaje tiene un listado de todos los valores de tipo
tables.MoveNext disponibles.
Wend IndexSchema
tables.Close
End If IndexSchema devuelve el nombre de los índices de una tabla. El
RecordSet tiene una columna, el nombre del índice.

FieldSchema
De forma similar, un FieldSchema devuelve un RecordSet con
información de todas las columnas (campos) de una tabla
concreta. Los resultados pueden variar dependiendo de la base
de datos, pero generalmente están disponibles estas columnas:
ColumnName, FieldType, IsPrimary, NotNull y Length.

Este ejemplo muestra en un ListBox información de cada


columna de la tabla Team:

138
Section 1

Impresión

En las aplicaciones de escritorio la impresión es muy similar a utilizarlos de nuevo sin preguntar nuevamente al usuario.
dibujar en la pantalla. Pero en vez de dibujar en un objeto
Graphics de un control Canvas, dibujas en un objeto Graphics
Dim ps As New PrinterSetup
creado específicamente para la impresión. Hay métodos para
ps.SetupString = mPrinterSettings
crear nuevas páginas y obtener los ajustes de la impresora.

En las aplicaciones web, la impresión es más limitada. Por lo If ps.PageSetupDialog Then


general lo que mejor funciona es crear simplemente un HTML, mPrinterSettings = ps.SetupString
mostrarlo en un control HTML Viewer e imprimir esto. End If

Desktop Printing
Nota: mPrinterSettings es una propiedad String Figura 5.2
Ajustes de Impresora en la Window que contiene este código.
Antes de imprimir querrás Dado que mPrinterSettings es una String,
darle al usuario la opción de Figura 5.1 puedes guardarla fuera de la aplicación
elegir la impresora a usar. Esto (una base de datos o un archivo) y usarla
se realiza con la clase la próxima vez que el usuario quiera
PrinterSetup, y que muestra el imprimir.
diálogo de Configuración de
La clase PrinterSetup tiene propiedades
Página del sistema operativo.
para ajustes de impresión como la
Esta clase devuelve los
orientación, tamaño de página y
ajustes para que puedas
resolución. La Referencia del Lenguaje tiene más detalles.

140
Nota: PrinterSetup no está soportado en las aplicaciones Linux. página a la impresora para su impresión. Este ejemplo imprime
Imprimir Texto y Gráfi cos “Hello” en la página 1 y “World” en la página 2:

Para imprimir texto y


gráficos sólo has de Dim page As Graphics
Figura 5.3
dibujar sobre el objeto page = OpenPrinterDialog
Graphics de la impresora. If page <> Nil Then
Para obtener estos
page.DrawString("Hello", 50, 50)
objetos Graphics, puedes
page.NextPage
llamar a los métodos
page.DrawString("World", 50, 50)
globales
OpenPrinterDialog u page.NextPage
OpenPrinter. La única End If
diferencia entre ellos es
que uno muestra el Diálogo de
Figura 5.4 También puedes utilizar cualquier ajuste de impresión que se
Impresión y el otro no. Dado que
haya especificado previamente mediante la clase PrinterSetup.
el sistema de impresión de OS X
tiene la capacidad automática de
imprimir a PDF, puedes utilizar
este método para generar
archivos PDF con sencillez.

Dado que estás dibujando sobre


un objeto Graphics, puedes
utilizar todos los comandos que
has aprendido en el capítulo de Gráficos y Multimedia.

Adicionalmente también puedes utilizar el método específico de


la impresora, NextPage, para controlar cuando se envía una

141
Dim ps As New PrinterSetup Dim page As Graphics
ps.SetupString = mPrinterSettings page = OpenPrinter
If page <> Nil Then
If ps.PageSetupDialog Then page.DrawString("Hello", 50, 50)
mPrinterSettings = ps.SetupString page.NextPage
End If page.DrawString("World", 50, 50)
page.NextPage
Dim page As Graphics End If
// Use Printer Settings
page = OpenPrinterDialog(ps)
Imprimir Texto con Estilo
If page <> Nil Then
Dado que las TextAreas son capaces de mostrar texto con estilo
page.DrawString("Hello", 50, 50)
y múltiples tamaños de fuentes, probablemente querrás retener el
page.NextPage
texto con estilo cuando imprimas. La clase StyledTextPrinter se
page.DrawString("World", 50, 50)
utiliza para este propósito, utilizando el método DrawBlock.
page.NextPage
End If Para imprimir texto con estilo, has de crear en primer lugar un
objeto StyledTextPrinter y llamar a continuación al método
StyledTextPrinter de la TextArea (indicando los gráficos a usar y el
Cada vez que llames a NextPage, el objeto gráfico (en este caso
ancho del texto) para obtener una instancia de la
la página) se borrará para que puedas comenzar de inmediato el
StyledTextPrinter que se pueda utilizar para la impresión.
dibujado de la nueva página.

Para imprimir sin que se muestre el diálogo de impresión solo has Con esta instancia, has de llamar a continuación al método
de llamar al método OpenPrinter: DrawBlock para dibujar el texto con estilo en la página (indicando
las coordenadas iniciales y la altura).

Este ejemplo imprime texto con estilo en una TextArea:

142
Const ppi = 72
Dim page As Graphics Dim page As Graphics
page = OpenPrinterDialog
Dim stp As StyledTextPrinter
If page <> Nil Then Dim colWidth, spaceBetweenColumns, pageHeight As Integer
Dim styledText As StyledTextPrinter
Dim columnToPrint As Integer

// 72 pixels per inch * 7.5 inches // 18 pixels is 1/4 inch (72/4)


Dim textWidth As Integer = 72 * 7.5
spaceBetweenColumns = ppi\4

styledText = TextArea1.StyledTextPrinter(page, width)


// 7.5 inches minus 1/4 inch for space
//divided by 2 (72*7.5-18)/2
// 72 pixels per inch * 9 inches
colWidth = (ppi*7.5 - spaceBetweenColumns) \ 2
Dim blockHeight As Integer = 72 * 9
styledText.DrawBlock(0, 0, blockHeight)
// 10 inches * 72 pixels per inch
End If
pageHeight = ppi * 10

page = OpenPrinterDialog()
Nota: Para soportar la impresión con estilo, la Text Area debe de tener If page <> Nil Then
activas sus propiedades Multiline y Styled. stp = TextArea1.StyledTextPrinter(page, ppi*7.5)
stp.Width = colWidth
Si el texto a imprimir es más largo de lo que cabe en el bloque
columnToPrint = 1
especificado, entonces has de iterar por el texto hasta que se Do Until stp.EOF
haya impreso por completo. Esto se hace comprobando la stp.DrawBlock((colWidth+SpaceBetweenColumns)*
(columnToPrint-1), 0, pageHeight)
propiedad EOF de la clase StyledTextPrinter después de cada
If columnToPrint = 2 Then //printing last column
llamada a DrawBlock. If Not stp.EOF Then // more text to print
page.NextPage
Este ejemplo imprime los contenidos de una Text Area en dos columnToPrint = 1
columnas con un espaciado de un cuarto de pulgada entre las End If
Else // more columns to print on this page
columnas: columnToPrint = columnToPrint + 1
End If
Loop
End If

143
Impresión Web
La impresión Web es considerablememente más sencilla que la
impresión de escritorio, debido a que es mucho más restringida.
Tu aplicación web funciona en un navegador web, de modo que
estás limitado a lo que puede imprimir el navegador.

Los navegadores web suelen hacer un buen trabajo imprimiendo


HTML, de modo que para generar algo que puedas imprimir has
de renderizarlo en primer lugar como HTML (ya sea como una
string o en un archivo) y utilizar a continuación un control HTML
Viewer para mostrar el HTML.

Una vez que tengas lo que quieres mostrado en un HTML Viewer,


puedes tener un botón que llame al método Print del HTML
Viewer de modo que indique al navegador que imprima sus
contenidos:

HTMLViewer1.Print

También puedes llamar al método Print en un HTML Viewer de


una aplicación de escritorio.

144
Section 2

Editor de Diseño de Informes

Puedes crear informes para las aplicaciones de escritorio usando De un modo muy parecido al Editor de Diseño de Ventana,
el Editor de Diseño de Informes. Para crear un informe, usa el arrastras controles sobre el Editor de Diseño de Informes para
botón o el menú y selecciona Informe. Esto añade un informe al diseñar los informes. Los Informes usan un modo de diseño a
proyecto y lo muestra en el Editor de Diseño de Informes. “bandas” que incluye múltiples franjas sobre las que puede
mostrarse la información. Por defecto verás tres bandas:
Figura 5.5 PageHeader, Body y PageFooter.

Lo que se incluya en la franja PageHeader parecerá en la parte


superior de cada página, incluyendo la primera página. De igual
modo, lo que se ponga en la franja PageFooter aparecerá en la
parte inferior de cada página, incluyendo la primera página.

La franja Body se repite para cada línea de datos que esté en el


informe. Por ejemplo, si tienes un informe que esté mostrando un
listado de Equipos, entonces obtendrás una franja distinta de
Body para cada equipo.

Para mostrar dicha información, un información tiene que


disponer de un conjunto de datos, algo que veremos en la
próxima sección de este capítulo.
Puedes indicar las unidades para la regla en Pulgadas,
Milímetros o Píxeles y especificar el ancho de la página de
informe usando el Inspector.

145
Controles del Editor de Diseño de Informes Figura 5.6
Hay una variedad de controles que puedes utilizar en los
informes, incluyendo: Field, Label, Picture, Line, Oval, Rectangle,
RoundRectangle, Date y Page Number.

Cada control tiene dos eventos que pueden utilizarse para


cualquier procesado: AfterPrinting y BeforePrinting.

Field
Se utiliza un Field en un informe para mostrar datos. Los fields se Date
mapean contra la fuente de datos que utilices con el informe,
Usado para mostrar la fecha actual en la que se genera el
indicando un valor en la propiedad DataField. Generalmente,
informe.
utilizas los Field en la banda Body, pero funcionan en cualquier
banda. Page Number

Label Muestra el número de página


Figura 5.7
en cada página del informe.
Como una Label en una Window o Web Page, una Label en un
informe muestra texto. Las Label se utilizan para cosas como los Si quieres mostrar los Equipos
títulos del informe y los encabezados de columna. de los ejemplos utilizados
previamente para XML, JSON
Picture
y las bases de datos puedes
Se utiliza una Picture para mostrar una imagen en el informe. La hacer un diseño similar al de
imagen puede indicarse cuando estés diseñando el informe o la Figura 5.6.
bien puede hacerse en tiempo de ejecución utilizando la
información en la fuente de datos. Grupos
Se usan los grupos para
Line, Oval, Rectangle, RoundRectangle
mostrar junta la información relacionada. Por ejemplo, en los
Usado para dibujar figuras en el informe. ejemplos Team y Player, hay dos fuentes de datos: equipos y

146
jugadores. Puedes utilizar los Grupos en un informe para mostrar Traer al Frente, Arriba, Hacia atrás, Atrás
los equipos con cada jugador del equipo mostrado bajo el Cambia el orden de capa de los controles en el diseño del
nombre del equipo, y quizá con un ligero indentado. informe.

Para añdir un grupo al informe, haz clic en el botón “Añadir nuevo Rellenar Ancho, Altura
Grupo” en la barra de herramientas. Esto crea dos nuevas Rellena el tamaño del control para que se corresponda con el de
bandas, denominadas GroupHeader1 y GroupFooter1, donde su contenedor.
puedes incluir información adicional. La información en Group
Header aparece solo una vez por cada grupo. La información en Alinear Izquierda, Derecha, Arriba, Abajo
el Group Footer también aparecerá solo una vez por cada grupo, Alinea los controles seleccionados del informe entre sí.
pero esta se muestra tras la banda Detail.
Distribuir Horizontalmente, Verticalmente
Barra de Herramientas de Diseño de Distribuye los controles seleccionados del informe de forma
Informes equidistante entre sí.
La Barra de Herramientas del Editor de Diseño de Informes
contiene botones que te permiten modificar el diseño del informe.
Estos son:

Añadir Nuevo Grupo


Añade un nuevo Grupo al diseño de informe. Esto añade tanto un
Group Header como un Group Footer al diseño.

Añadir nueva Cabecera/ Pie de página


Usado para añadir una cabecera/pie de página al informe. Un
informe solo puede tener un único par de bandas Cabecera/Pie
de página. Dado que es posible borrar estas bandas, utiliza este
ítem para volver a añadirlas.

147
Section 3

Mostrar Datos e Imprimir Informes

Una vez has diseñado tu informe, has de proporcionar los datos


a mostrar. Esto es lo que se denomina fuente de datos. Las // teamRecordSet comes from a database
bases de datos son las utilizadas por lo general como fuente de // SELECT statement
datos para los informes, pero también puedes usar cualquier Dim ps As New PrinterSetup
dato que quieras usando la interfaz Reports.DataSet.
Dim rpt As New TeamReport
Usar una Base de datos como Fuente de If rpt.Run(teamRecordSet, ps) Then
datos If rpt.Document <> Nil Then
Es realmente simple utilizar una base de datos como fuente de // Save the document for
datos. Sólo has de obtener los datos que quieras en un // the Canvas to display
RecordSet. Luego sólo has de pasar el RecordSet al informe mReportDocument = rpt.Document
para que lo use como fuente de datos. End If
End If

Usar Texto como Fuente de Datos


Para usar cualquier otra cosa como fuente de datos para el
informe, como un archivo de texto, has de crear una clase
personalizada que implemente la interface Reports.DataSet. Esta
interface especifica estos métodos: EOF As Boolean,

148
Field(Integer) As Variant, Field(String) As Variant, NextRecord As array, pero antes de implementarlo, es necesaria otra propiedad
Boolean, Run, Type(String) As Integer. que se encargue de registrar la posición actual del array:

En estos métodos debes de obtener la información concreta que


necesites de tu fuente de datos actual (quizá un texto o archivo mArrayPosition As Integer
XML) y devolver el resultado o llevar a cabo la acción esperada.

Como un ejemplo sencillo, puedes poner los nombres de los Ahora puedes añadir el código para mover la posición del array:
equipos en un array de cadenas y crear a continuación una clase
para trabajar con este array. Debes de proporcionar el código
mArrayPosition = mArrayPosition + 1
para cada método de la interface.

Para comenzar, crear una clase y llámala TeamDataSet. Añádele


la interface Reports.DataSet. Ahora añade el array que contiene EOF es el siguiente. EOF debe devolver True cuando no hay más
los nombres de equipo como una propiedad de esta clase: datos en el array y False cuando aun hay datos.

mTeams() As String If mArrayPosition > mTeams.Ubound Then


Return True
Else
Crea un método Constructor para la clase e inicializa el array: Return False
End If

mTeams = Array("Seagulls", "Pigeons",


"Crows") Este código verifica si la posición actual del array estará más allá
de los límites del array.

Ahora puedas comenzar a implementar los métodos de la Avancemos, el método Type se utiliza para devolver el tipo del
interface. Comienza con NextRecord dado que es el más sencillo. nombre de campo indicado. El array sólo tiene un campo, el
El método NextRecord se utiliza para mover la posición actual del
149
nombre del equipo, de modo que este método debe de
comprobar el nombre de campo de “TeamName” y devolver su If name = "TeamName" Then
tipo, que es String. El tipo se indica mediante el mismo valor Return mTeams(mArrayPosition)
entero que especifica los tipos cuando se trabaja con bases de Else
datos. Para String, el valor es 5. Return ""
End If

If fieldName = "TeamName" Then


Return 5 Para Field(Integer) As Variant, utiliza el anterior código:
Else
Return 0
If idx = 0 Then
End If
Return Field("TeamName")
Else
Los dos métodos Field se utilizan para obtener el valor del campo Return ""
indicado en la actual posición del array. Un método Field utiliza End If
una posición de campo entera y el otro utiliza el nombre del
campo. Aquí sólo hay un campo, de modo que simplifica las
cosas. Por último has de implementar el método Run. Este es el método
llamado para iniciar el proceso de obtener los datos. También es
Para Field(String) As Variant: sencillo dado que todo lo que necesitas hacer para este ejemplo
es inicializar la posición del array:

mArrayPosition = 0

Con esta clase lista, ahora puedes usarla con el método


Report.Run para poblar el informe con tus datos:
150
Para mostrar el informe, el Documento para el informe se guarda
Dim teamData As New TeamDataSet en una propiedad de la ventana: mReportDocument As
Dim ps As New PrinterSetup Reports.RBReportDocument.
Dim rpt As New TeamReport
La clase RBReportDocument tiene una propiedad que te indica
If rpt.Run(teamData, ps) Then
cuántas páginas hay en el informe (PageCount) y los métodos
If rpt.Document <> Nil Then
usados para mostrar, imprimir y guardar el informe (Page, Print,
// Save the document for
Save).
// the Canvas to display
mReportDocument = rpt.Document Para mostrar el informe obtienes una página cada vez usando el
End If método Page (y que devuelve un Picture con la página del
End If informe). Luego puedes un Canvas para mostrar la imagen.

Por ejemplo, este código en el evento Paint de un Canvas


Mostrar el Informe muestra la página 1 de un informe:
El Editor de Diseño de Informes te permite diseñar tus informes
pero no hay modo de previsualizarlos sin ejecutar la aplicación.
If mReportDocument <> Nil Then
Los informes se generan como imágenes, de modo que puedes
g.DrawPicture(mReportDocument.Page(1), 0, 0)
mostrarlos ejecutando el informe con la fuente de datos y End If
mostrando el informe generado en un Canvas. Por supuesto,
dado que el informe es un Picture, puedes hacer todo lo que
podrías hacer con una Picture, como guardarlo directamente a un Este código siempre muestra sólo la página 1 de un informe.
FolderItem o a una base de datos.
Para realizar un previo real de un informe, por lo general
Los anteriores ejemplos te muestran como ejecutar el informe y mostrarás una página cada vez y tendrás botones Anterior/
tiene un comentario que el documento es guardado en un Siguiente para moverte por entre las páginas. Los botones
Canvas para mostrarlo. Anterior/Siguiente pueden modificar la propiedad

151
mCurrentDisplayPage que usas aquí en el event Paint en vez del
“1” para el número de la página: // teamRecordSet comes from a database SELECT statement
Dim ps As New PrinterSetup

If mReportDocument <> Nil Then Dim rpt As New TeamReport


If ps.PageSetupDialog Then
g.DrawPicture(mReportDocument.Page(mCurrentDisplayPage), Dim g As Graphics
0, 0) g = OpenPrinterDialog(ps)
End If If g <> Nil Then
If rpt.Run(teamRecordSet, ps) Then
If rpt.Document <> Nil Then
// Print the report
Imprimir el Informe rpt.Document.Print(g)
Imprimir un informe funciona de forma similar a la impresión End If
general. Usas PrinterSetup y OpenPrinter (u OpenPrinterDialog) End If
End If
para obtener un objeto Graphics. Entonces llamas al método
End If
Print del Documento de Informe para imprimirlo:

Guardar el Informe
Para guardar tu informe tienes un par de opciones, si estás
usando OS X puedes dejar que el usuario saque provecho de su
capacidad para imprimir directamente como PDF a un archivo. El
usuario simplemente usa el diálogo de impresión del OS X para
elegir la opción de guardar el informe como archivo PDF en vez
de enviarlo a la impresora.

Tu otra opción es guardar las Pictures a disco en vez de


dibujarlas sobre la pantalla.

152
Dim teamData As New TeamDataSet
Dim ps As New PrinterSetup
Dim rpt As New TeamReport
If rpt.Run(teamData, ps) Then
If rpt.Document <> Nil Then
Dim saveFolder As FolderItem
saveFolder = SelectFolder
If saveFolder <> Nil Then
Dim saveFile As FolderItem
For i As Integer = 1 To rpt.Document.PageCount
saveFile = saveFolder.Child("TeamReport" +
Str(i) + ".png")
rpt.Document.Page(i).Save(saveFile,
Picture.SaveAsPNG)
Next
End If
End If
End If

153
Section 1

Comunicación con Dispositivos Serie

Un dispositivo serie es un dispositivo que se comunica enviando añadido el controlador Serial a tu ventana entonces puedes
y/o recibiendo datos en serie. Esto significa que o bien está cambiar estas propiedades usando el Inspector.
enviando o bien recibiendo datos en uno u otro momento. No
Los detalles sobre cómo configurar estas propiedades depende
puede enviar y recibir al mismo tiempo. El dispositivo serie más
completamente del dispositivo que estés usando. Debes
común es un módem. Algunas impresoras son dispositivos serie.
consultar la documentación de tu dispositivo para determinar los
Las comunicaciones serie se realizan con la clase Serial. Para
ajustes que soporta.
comunicarte con un dispositivo serie configuras una instancia de
la clase Serial, abres el puerto serie para crear la conexión, lees
Abrir el Puerto
y/o escribes datos hacia y/o desde el dispositivo conectado a Una vez que hayas configurado el controlador Serial, puedes
uno de tus puertos serie y, finalmente, cierras el puerto serie abrir el puerto serie para iniciar la comunicación el dispositivo
cuando hayas finalizado para desconectarte del dispositivo serie. serie. Esto se realiza llamando al método Open de la clase. Este
método devuelve True si se logra abrir la conexión y False de lo
Configuración
contrario. Por ejemplo, supongamos que tienes un controlador
El primer paso es añadir la clase Serial a tu proyecto. El modo
Serial cuyo nombre es “SerialDevice”. Puedes abrir el puerto
más sencillo para ello es arrastrando un controlador Serial desde
serie con el siguiente código:
la Librería sobre la Ventana. También puedes arrastrar el
controlador Serial al Navegador para crear una subclase que
puedas instaciar en tu código.

Antes de que puedas comenzar a comunicarte con un


dispositivo serie, necesitas indicar al controlador Serial qué
puerto usar, la velocidad de comunicación y otros ajustes. Si has

155
En el manejador de evento DataAvailable puedes usar los
If SerialDevice.Open Then métodos Read o ReadAll para obtener parte o la totalidad de los
MsgBox("Opened serial port.") datos del buffer. Dicha información se devuelve como String.
Else Utiliza el método Read cuando quieras obtener una cantidad
MsgBox("Could not open serial port.") concreta de bytes (caracteres) del buffer. Si quieres obtener todos
End if los datos del buffer, utiliza el método ReadAll. En ambnos casos
la información devuelta desde el buffer se elimina de dicha
memoria para hacer espacio para los nuevos datos entrantes. Si
Una vez que hayas abierto con éxito el puerto serie, dejará de necesitas examinar los datos del buffer sin eliminarlos, debes de
estar disponible para el resto de aplicaciones (y otros usar el método LookAhead.
controladores Serial también), hasta que se cierre.
Este ejemplo en el evento DataAvailable añade los datos
Para cerrar el puerto Serial, llama al método Close: entrantes a una TextArea:

SerialDevice.Close TextArea1.AppendText(Me.ReadAll)

Leyendo Datos Puedes limpiar todos los datos del buffer aun no leídos llmando
Cuando el dispositivo serie envía datos hacia el controlador Serial al método Flush.
al que está conectado, la información enviada se guarda en un
lugar de la memoria del ordenador denominado buffer. El buffer Tanto los métodos Read como ReadAll pueden utilizar un
es simplemente un sitio en el que almacenar los datos enviados parámetro opcional que te permite indicar la codificación. Utiliza
por el dispositivo serie debido a que este tipo de equipos no el objeto Encodings para obtener la codificación deseada y
disponen de grandes cantidades de memoria. Cuando la pasarla como parámetro. Por ejemplo, el anterior código ha sido
información llega al buffer, se llama el manejador de evento modificado para indicar que el texto entrante utiliza la
DataAvailable del controlador Serial. codificación ANSI, un estándar en Windows:

156
Si por el contrario prefieres esperar a que se haya completado el
TextArea1.AppendText(Me.ReadAll(Encodings.WindowsANSI)) envío de todos los datos antes de continuar con la siguiente línea
de código, llama al método XmitWait antes de llamar a Write.

Puede que necesites indicar la codificación cuando el texto


proceda de otra plataforma o en otro lenguaje. Para obtener más SerialDevice.XmitWait
información sobre la codificación de texto, consulta la sección SerialDevice.Write(TextArea1.Text)
Codificaciones en el Capítulo 2.

Escribir Datos Cambiar la Configuración Serie


Puedes enviar datos a un dispositivo serie en cualquier momento Hay ocasiones en las que necesitas cambiar las propiedades del
siempre que el puerto serie esté abierto. Para enviar datos se usa comportamiento para el controlador Serie mientras que el puerto
el método Write. Los datos a enviar deben de ser una cadena. está abierto. Si bien puedes cambiar dichas propiedades, los
cambios no tendrán efecto hasta que cierres el puerto y vuelvas a
El método Write se realiza de forma asíncrona. Esto significa que
abrirlo. Si necesitas que se actualicen las propiedades de
tu aplicación no espera a que finalice el método Write antes de
inmediato, llama al método Poll.
que continúe con la siguiente línea de código. Este
comportamiento permite que tu aplicación continúe Esta acción actualiza todas las propiedades de inmediato y llama
respondiendo. de inmediato al manejador de evento DataAvailable en el caso de
que se encuentren datos esperando en el buffer.
Este ejemplo envía el texto de una TextArea hacia un dispositivo
Serie:
SerialDevice.Poll

SerialDevice.Write(TextArea1.Text)
Cerrar el Puerto
Una vez que has terminado de comunicar con un dispositivo
serie, debes de cerrar el puerto serie para finalizar la sesión de

157
comunicaciones y permitir que el puerto esté disponible para • Un protocolo de bajo nivel orientado a paquetes, de modo que
otros controladores Serie u otras aplicaciones. el SO pueda saber qué controlador cargar y hablar con el
dispositivo USB para identificarlo.
Para cerrar el puerto Serie, llama al método Close usando el
mismo controlador Serial que abrió el puerto: • Una API de fabricante; es decir, un dispositivo HID como un
ratón, teclado o dispositivo de almacenamiento.

SerialDevice.Close Sólo porque algo utilice un cable USB no significa que puedas
hablar con ello.

Comunicar con dispositivos USB y FireWire Algunos tipos de dispositivos son tan comunes que los
De un modo general, los dispositivos USB y FireWire utilizan una fabricantes de SO incluyen controladores para ellos. Esto es lo
interfaz completamente diferente que con frecuencia requieren de que ocurre con los dispositivos HID (como los ratones y
controladores para comunicarse con ellos. teclados), y los dispositivos de almacenamientos como los discos
duros. Si es uno de estos, entonces funcionará sin que debas de
Dicho esto, algunos de estos dispositivos tienen un chip en su
hacer nada más.
interior que los hace aparecer y comportarse como si fueran
dispositivos serie. Este chip se denomina con frecuencia FTDI. Prácticamente cualquier otra cosa requerirá de un controlador
por parte del fabricante. Si quieres controlar dicho tipo de
Si tu dispositivo no es reconocido como dispositivo serie,
dispositivo, necesitarás obtener una librería compartida del
entonces debes investigar mediante el plugin MonkeyBread
fabricante o bien escribir tu propia librería. Intenta contactar con
Software, en el que se incluye soporte USB para tipos de
el fabricante para ver si tiene una librería. Si el fabricante tiene
dispositivos específicos.
una librería para comunicaciones USB, puedes comunicarte con
la librería usando instrucciones Declare.
Información sobre USB
USB engloba varias cosas:
Comunicar con Módems
Los módems tienen un conjunto de comandos que puedes
• Una espacificación de cable de interconexión (el aspecto que
utilizar para indicar al módem que realice cosas como marcar un
han de tener los conectores del cable)
número concreto. La mayoría de estos comandos son los
158
mismos para cada módem. Tu módem probablemente incluya
una guía que liste dichos comandos. Consulta dicha guía para
obtener más información.

Usando Serial con Aplicaciones Web


Las aplicaciones Web pueden usar la clase serie para comunicar
con los dispositivos serie que estén conectados al servidor web.
La clase Serial no puede utilizarse para comunicar con los
dispositivos serie conectados al ordenador del usuario.

159
Section 2

Comunicación Internet

Comprender los Protocolos descripciones completas de dichos protocolos de Internet se


Cualquier tipo de comunicación requiere que todas las partes denominan RFCs (Request For Comments). El modo más
involucradas se pongan de acuerdo sobre un método y lenguaje sencillo para encontrar información de un RFC es buscar por
de comunicación. Por ejemplo, si quieres comunicar con un “RFC”. Esto te proporcionará un listado con los enlaces a varios
amigo puedes hablar con él cara a cara, llamarle por teléfono o sitios web que explican todos los protocols de Internet
enviarle un mensaje de correo. Ambos podréis comunicaros
Si estás escribiendo una aplicación que se comunique con otras
usando el mismo lenguaje o no seréis capaces de comunicaros
aplicaciones escritas por ti, entonces has de definir tu propio
en absoluto. Las comunicaciones vía Internet funcionan del
protocolo. Tu protocolo será simplemente un conjunto de
mismo modo. El lenguaje utilizado se denomina protocolo. Un
comandos que hayas definido para permitir que las aplicaciones
protocolo es simplemente un modo organizado de enviar y/o
entienda qué es lo que quiere o espera la otra parte.
recibir información.

Si estás escribiendo una aplicación que se comunicará con otra Limitaciones sobre los Sockets en
aplicación mediante TCP/IP (Transfer Control Protocol/Internet Aplicaciones Web
No puedes usar un socket de red de ningún tipo en una página
Protocol) por ejemplo, tendrás que comprender el protocolo que
web dado que el socket está funcionando realmente en un
espera utilizar la otra aplicación para que pueda existir una
servidor y puede no tener modo de enviar sus datos de nuevo
comunicación entre ambos. Por ejemplo, en Internet, el
hacia el navegador del cliente para actualizar la interfaz de
protocolo para la world wide web se denomina HTTP (HyperText
usuario. Sin embargo los sockets que sean una propiedad de tu
Transfer Protocol), el protocolo para enviar email se denomina
clase App funcionarán correctamente.
SMTP (Simple Mail Transfer Protocol), y el protocolo para recibir
email se denomina POP3 (Post Office Protocol 3). Las

160
Section 3

Comunicaciones TCP/IP

En algunas ocasiones las aplicaciones han de comunicarse con a una aplicación la capacidad de centrarse en información
otras aplicaciones en l amisma red. Esto puede realizarse concreta en vez de recibir todos los datos transmitidos a tu
mediante el uso de la clase TCPSocket. TCPSocket puede enviar ordenador a través de TCP/IP. Esto te permite navegar por la
y recibir datos usando el protocolo de Internet TCP/IP. web y enviar mensajes de correo electrónico al mismo tiempo,
dado que la web usa un puerto y el email otro. El puerto se
TCPSocket es una subclase de la clase SocketCore, que es la
representa mediante un número y haci miles de puertos
clase base para varias clases de red.
disponibles. Algunos ya han sido designados para funciones
TCPSocket puede utilizarse para comunicarse con otros concretas como la navegación web (80), el email (25, 110, 143),
ordenadores en la misma red. Cuando te conectas a Internet, FTP (20, 21), etc. Si estás diseñando una aplicación que necesite
eres parte de la red Internet. Esto te permite comunicarte con comunicarse con otra, tendrás que encontrar qué puerto está
otros ordenadores de Internet mediante TCP/IP. utilizando la otra aplicación.

Un TCPSocket tiene una propiedad Port que puede asignarse en


Configuración
Para empezar con la comunicación TCP/IP has de añadir una tiempo de diseño o bien en tiempo de ejecución, pero ha de

clase TCPSocket a tu proyecto. El modo más sencillo de hacerlo asignarse el valor antes de conectar con otro ordenador. Si

es arrastrando un control TCPSocket desde la librería hasta la tienes previsto iniciar la comunicación, debes de configurar

ventana o el Navegador. también la propiedad Address con la dirección (IP o nombre que
se pueda resolver) correspondiente al ordenador al que desees
Antes de que puedas conectar otro ordenador usando un conectarte.
TCPSocket, has de configurar el puerto. El puerto es para TCP/
Nota: OS X y Linux tienen restricciones de serie sobre los números de
IP lo mismo que son los canales en televisión o la asignación de puertos. Los puertos por debajo de 1024 no pueden ser asignados por un
frecuencias en las emisoras de radio. Los puertos proporcionan usuario que no esté usando privilegios “root”. OS X está configurado de

161
modo que un usuario no puede obtener usuarios root desde la interfaz interrupción de la conexión, resultando en un error de pérdida de
gráfica. La mayoría de los usuarios funcionan con privilegios de Admin — no
rro — de modo que deberás de utilizar puertos por encima de 1024 para las la conexión (102), o bien un error de fuera de estado (106).
comunicaciones TCP/IP con OS X o Linux, dado que el TCPSocket no
puede acceder a los números por debajo de 1024. Esto no es un bug, sino Cuando se establece la conexión, se llama al manejador de
una característica de seguridad incorporada en los sistemas operativos.
evento Connected. Si no se establece una conexión, ocurre un
Nota: Un control TCPSocket sólo puede estar conectado a una aplicación
al mismo tiempo. Si necesitas mantener múltiples conexiones error y se llama al manejador de evento Error.
simultáneamente, debes de usar el control ServerSocket, diseñado para tal
propósito. Consulta la sección Crear un Servidor en este capítulo. Una vez se ha establecido una conexión, tu aplicación puede
empezar a enviar y recibir datos con la aplicación situada en el
Conectar con otro Ordenador
Una vez que hayas asignado un puerto y una dirección IP, puedes otro extremo de la conexión.

conectar a una aplicación en el ordenador con dicha dirección IP,


Escuchar por una Conexión desde otro
siempre y cuando la aplicación esté escuchando por conexiones
Ordenador
TCP/IP en el puerto indicado. Para iniciar una conexión, has de
Tu aplicación también puede escuchar por una petición de
llamar al método Connect. Recuerda que no se establece una
conexión por parte de otra aplicación. Para ello, utiliza el método
conexión inmediatamente tras llamar a Connect. Debes de
Listen de TCPSocket:
esperar a que la aplicación a la que estás intentando conectar
acepte dicha conexión. Adicionalmente, podría haber una
latencia general o bien otros problemas que requieren de un TCPConnection.Listen
tiempo para resolverse.

Una vez que pongas a TCPSocket en modo listen, este espera de


TCPConnection.Connect forma asíncrona para una petición de conexión. Tu aplicación
continúa respondiendo mientras que el socket está esperando, lo
que significa que el código continuará ejecutándose.
Hay dos modos de determinar si estás conectado. Puedes o bien
esperar hasta que recibas un evento Connected desde tu Cuando se solicita una conexión, se llama al manejador de
TCPSocket o testear el valor devuelto del método IsConnected. evento Connected, lo que te permite saber que tienes una
Si no quieres esperar por dicha información, podrías causar la conexión.

162
Leer Datos operativo o está en otro lenguaje, puede que debas de indicar la
Cuando la aplicación en el otro extermo de la conexión envía codificación utilizada. Para más información sobre la codificación
datos al TCPSocket al que está conectado, se llama al manejador de texto, consulta la sección Codificaciones en el Capítulo 2.
de eventos DataAvailable. Los datos enviados se reciben en una
Tanto los métodos Read como ReadAll de la clase TCPSocket
área de memoria del ordenador denominada buffer. El buffer es
pueden usar un parámetro opcional que te permite especificar la
simplemente un lugar para almacenar los datos enviados por la
codificación. Utiliza el objeto Encodings para obtener la
otra aplicación.
codificación deseada y pasarla como parámetro. Por ejemplo, el
En el manejador de evento DataAvailable, puedes usar los código anterior se ha modificado para indicar que el texto
métodos Read o ReadAll del TCPSocket para obtener parte o entrante utiliza codificación ANSI, un estándar en Windows:
todos los datos del buffer (devueltos como String). Si quieres
obtener todos los daots del buffer, utiliza el método ReadAll. En TextField1.AppendText(Me.ReadAll(Encodings.WindowsANSI))
ambos casos, la información devuelta desde el buffer se elimina
de dicho espacio para hacer sitio a otros datos entrantes. Si
necesitas examinar los datos del buffer sin eliminarlos del mismo, Escribir Datos
utiliza el método LookAhead. Puedes enviar datos a la aplicación a la que estás conectado en
cualquier momento. Envías datos usando el método Write. Los
Este ejemplo en el manejador de evento DataAvailable añade los
datos que quieras enviar deben de ser una cadena. En este
datos entrantes a un TextField:
ejemplo, se envia el texto de un TextField mediante un control
TCPSocket:
TextField1.AppendText(Me.ReadAll)

TCPConnection.Write(TextField1.Text)
Cuando estás leyendo texto desde una fuente externa, es posible
que debas de indicar la codificación de texto. La codificación de
Si necesitas enviar el texto a una aplicación que esté esperando
texto es el esquema que mapea cada letra del texto a un código
una codificación de texto específica, entonces deberías convertir
numérico. Si el texto procede de otra aplicación, sistema
el texto a dicha codificación antes de enviarlo. Utiliza la función
163
ConvertEncoding para ello. Sus parámetros son el texto a Lenguaje para obtener un listado completo con los números de
convertir y la codificación de texto a usar. Por ejemplo, la error.
siguiente línea envía el texto de un TextField usando la
Los errores son simplemente modos de alertar a tu aplicación de
codificación MacRoman:
las condiciones que puede no haber anticipado o que pueda
anticipar.
TCPConnection.Write(ConvertEncoding(TextField1.Text,
Encodings.MacRoman))
Cerrar la Conexión
Cuando hayas finalizado la comunicación y quieras
desconectarte de la otra aplicación, puedes hacerlo cerrando la
Una vez que se han enviado todos los datos se llama al
conexión. La conexión se cierra llamando al método Close de
manejador de evento SendComplete. A medida que se envían los
TCPSocket. Supongamos que tienes un Socket llamado
datos, se llama al manejador de evento SendProgress, de modo
“TCPConnection” y que ha establecido una conexión. Para cerrar
que puedas saber cuántos datos se han enviado hasta ese
la conexión puedes usar el siguiente código:
punto. Nunca asumas cuánta información se ha enviado entre
llamadas al evento SendProgress. Es normal que fluctúe.
TCPConnection.Close
Nota: Si vas a enviar fragmentos pequeños de datos por la red
(especialmente en una red pequeña), es posible que tenga más sentido usar
la clase UDPSocket en vez de TCPSocket.

Gestión de Errores Otra Información del Socket


Pueden producirse errores durante el intento de conexión o al Destruir un Socket
enviar o recibir datos. Los errores no siempre son lo que parecen. Cuando llamas a los métodos Connect o Listen, se incrementa la
Por ejemplo, cuando el otro ordeandor cierra la conexión se cuenta de referencia del socket.
genera un error. Cuando ocurre un error se ejecuta el evento
Error. Los errores están representados por números. La Esto significa que el socket no tiene por qué ser propiedad de
nada (como una Ventana) para que continúe funcionando. Esto es
propiedad LastErrorCode contiene el número del último error que
útil en ciertas circunstancias. Por ejemplo, supongamos que
ha ocurrido. Consulta la clase SocketCore en la Referencia del
escribes tu propia sublacse socket que implementa todos los

164
eventos del socket, denominada MySocket. En el evento de
acción para un Button utilizas el siguiente código:

Dim s As New TCPSocket


s.Port = 7000
s.Address = "127.0.0.1"
s.Listen

Incluso una vez que se haya completado este evento, el socket


permanecerá activo escuchando por nuevas conexiones. De
hecho, el socket permanece activo y vivo hasta que lo cierres
específicamente, ya sea cerrando la conexión local o
remotamente.

Por supuesto, el socket también se cerrará cuando salgas de la


aplicación.

Seguridad
La clase SSLSocket se utiliza para realizar comunicaciones
seguras mediante TCP/IP.

Funciona de forma similar a la clase TCPSocket, pero tiene


propiedades adicionales para el tipo de conexión, gestionar la
ubicación de los certificados y para comprobar si se ha realizado
una conexión segura.

165
Section 4

Comunicaciones HTTP (web)

HTTP (HyperText Transfer Protocol) es el protocolo utilizado por utilizarse. El modo más simple es descargando los datos a una
los navegadores web. Para comunicarte usando el protocolo cadena de forma síncrona. Este código descarga los contenidos
HTTP has de emplear la clase HTTPSocket y la clase de un sitio web y configura el timeout a 0, de modo que espera
HTPPSecureSocket. Estas clases incluyen los métodos y indefinidamente hasta que se recibe la página.
propiedades que te permiten hacer todo tipo de comunicaciones
HTTP, como recuperar una URL o enviar un formulario. Dim pageData As String
pageData =
HTTPSocket es una subclase de TCPSocket y
HTTPSocket1.Get("http://www.wikipedia.org", 0)
HTTPSecureSocket es una sublase de TCPSecureSocket.

HTTPSocket También puedes descargar a un FolderItem y tienes la opción de


Algunas de las cosas más habituales que haces con un
realizar la descarga asíncrona. Cuando descargas de forma
HTTPSocket es obtener los contenidos de una página web, así
asíncrona, el código no se detiene en la línea Get. Por el
como enviar formularios a páginas web. contrario, tu código continúa funcionando y la aplicación sigue
respondiendo. Cuando se completa la descarga (y se han
Para usar el HTTPSocket, puedes arrastrar un TCPSocket a tu
recibido los datos), se llaman a los eventos del HTTPSocket.
ventana y cambiar su Super a HTTPSocket. O puedes añadir una
subclase a tu proyecto. Esta sintaxis obtiene la misma página web de forma asíncrona:

Obtener Contenidos de Páginas Web


HTTPSocket1.Get("http://www.wikipedia.org")
El método Get se utiliza para obtener los contenidos de una
// Code continues
página web. Hay una variedad de modos en los que puede

166
Cuando se completa la descarga se llama al manejador de evento SOAP
PageReceived. Hay otros eventos que también son llamados a SOAP (Simple Object Access Protocol) es un protocolo de
medida que se descarga la página como Connected, comunicaciones para el intercambio de datos entre aplicaciones
ReceiveProgress, HeadersReceived y DownloadComplete.
mediante HTTP. Son referidos generalmente como “servicios
Enviar un Formulario web”.

Puedes enviar datos a una página web. Para ello, creas un Usa las clases SOAPMethod y SOAPResult para comunicarte
Dictionary (diccionario) con los datos del formulario, asignas con los servicios web SOAP.
estos datos al HTTPSocket y haces un Post a la página web.

Este es el ejemplo que define la información para los fomularios


REST
REST (Representational State Transfer) es otra forma de
con dos campos, FirstName y LastName:
proporcionar servicios web. No es un protocolo, sino más una
API (Application Programming Interface) basada en HTTP que
Dim formData As New Dictionary
permite el intercambio de información entre aplicaciones.
form.Value("FirstName") = "Bob"
form.Value("LastName") = "Roberts"

Dim result As String


HTTPSocket1.SetFormData(form)
result = HTTPSocket1.Post("http://www.wikipedia.org", 0)

Como con Get, Post tiene una variedad de modos en los que
puede usarse. Puedes hacer que los resultados vayan a un
FolderItem y también puedes procesarlo de forma asíncrona.

Seguridad
El HTTPSecureSocket se utiliza para comunicaciones HTTP
seguras. Funciona del mismo modo que la clase HTTPSocket
pero usa SSL (Secure Socket Layer) para proporcionar seguridad.
167
Section 5

Email

Puedes enviar y recibir email usando los protocolos SMTP La clase EmailMessage tiene más propiedades y métodos para
(Standard Mail Transfer Protocol) y POP3 (Post Office Protocol). configurar el exto de mensaje HTML, cabeceras y receptores
CC.
Enviar Email
Para enviar email, utiliza la clase SMTPSocket. Para enviar email También puedes adjuntar vínculos al email usando la clase
seguro, usa la clase SMTPSecureSocket. EmailAttachment class.

Antes de que envíes un email, has de crear el mensaje de email. Nota: Has de proporcionar tu propio servidor SMTP para enviar realmente
el email. Puedes elegir entre utilizar el servidor de correo de tu PSI, el de
Para ello usa la clase EmailMessage. una tercera parte como Gmail. La mayoría de los servidores SMTP
requerirán que proporciones un nombre de usuario y contraseña.
Este ejemplo crea un mensaje de email sencillo y lo envia
mediante SMTPSocket:
Recibir Email
Para recibir email, usas la clase POP3Socket. Para recibir email
de un servidor seguro, usa la clase POP3SecureSocket.
Dim email As New EmailMessage
email.FromAddress = "paul@xojo.com" El POP3Socket se conecta a tu servidor POP3 y solicita los
email.Subject = "Hello, world!"
mensajes de correo. A medida que se reciben los mensajes, se
email.BodyPlainText = "I love Xojo!"
email.AddRecipient("custserv@xojo.com") llama al evento MessageReceived con el email como un
EmailMessage.
SMTPSocket1.Address = "mail.myserver.com"
SMTPSocket1.Port = 25 POP3Socket tiene varios eventos, propiedades y métodos que te
SMTPSocket1.Messages.Append(email)
permiten recibir email POP3.
SMTPSocket1.SendMail

168
Nota: Varios servicios de email usan IMAP en vez de POP. Si necesitas
IMAP, echa un vistazo al Plugin MonkeyBread Software.

Seguridad
Para enviar y recibir email de forma segura, usa las clases
SMTPSecureSocket y POP3SecureSocket. Funcionan del mismo
modo que las clases descritas anteriormente, pero usan SSL
(Secure Socket Layer) para establecer una comunicación segura.

169
Section 6

Comunicaciones UDP

El User Datagram Protocol, o UDP, es la base para el tráfico de dirección remota del equipo al que quieres enviar el paquete, el
alta velocidad en redes altamente distribuidas. Es un protocolo puerto al que debe de enviarse, y los datos que quieras enviar al
de no conexión que tiene una muy baja sobrecarga pero que no equipo remoto.
es tan seguro como TCP.
Modos UDPSocket
Al igual que el control TCPSocket, el control UDPSocket está Los sockets UDP pueden operar en varios modos que son muy
derivado de la clase SocketCore. similares, pero que tienen usos muy diferentes. El modo que más
recuerda a la comunicación TCP se denomina “unicasting”. Esto
Para usar un UDPSocket, debe de estar vinculado a un puerto
ocurre cuando la dirección IP indicada al enviar datos es la de un
específico del equipo. Una vez vinculado, el UDPSocket ya está
único equipo. Un ejemplo sería enviar datos a www.xojo.com, o
listo para su uso. Comienza a aceptar de inmediato cualquier
bien alguna otra dirección de red. Es un Datagrama que tiene un
dato que vea en el puerto al que está vinculado. Esto te permite
único receptor.
enviar datos, así como a configurar opciones del socket UDP.
Este ejemplo envía datos a un único equipo:
DataGrams
Para diferencia el equipo que te está enviando datos, un
UDPSocket utiliza una estructura de datos conocida como
Datagrama. Un Datagram es una clase incluida de serie con dos
propiedades, Address (que se corresponde con la dirección IP
del equipo remoto que te ha enviado los datos), y Data (los datos
propiamente dichos). Cuando has de enviar datos, debes de
indicar la información para el Datagrama. Esta información es la

170
Dim data As New Datagram Dim data As String
data.Address = "www.xojo.com" data = "Hello, World!"
data.Data = "Hello, World!"
data.Port = 9500 // 10.0.1.25 is the machine
// broadcasting the message
UDPSocket1.Write(data) UDPSocket1.Write("10.0.1.25", data)

El segundo modo de operación se denomina “broadcasting”. El tercer modo de operación para los UDPSockets es
Como implica el propio nombre, se trata de una escritura emitida. “multicasting”. Es una combinación de unicasting y de
Es similar a gritar con un megáfono. Todo el mundo recibe el broadcasting que es muy potente y práctica. El Multicasting es
mensaj, tanto si quieren como si no. Si el equipo está muy similar a una sala de chat: entras en la sala y puedes
escuchando en el puerto indicado, entonces recibirá los datos en mantener conversaciones con cualquiera que esté en ella.
dicho puerto. Este tipo de envío es muy intenso en cuanto a usos Cuando quieres entrar en la sala llamas a JoinMulticastGroup, y
de recursos de red. Como puedes imaginar, el broadcasting solo has de indicar el grupo al que quieres unirte. El parámetro de
genera una gran cantidad de tráfico de red. La buena noticia es grupo es un tipo de dirección IP especial, denominada IP de
que cuando emites tus datos éstos no abandonan tu subred. Clase D. Esta comprende desde 224.0.0.0 a 239.255.255.255.
Piensa en la dirección IP como el nombre de la sala. Si quieres
Básicamente, una emisión no dejará tu red local para viajar por el empezar a conversar con otras dos personas, los tres debéis de
mundo. Cuando quieras emitir datos en vez de enviarlos a una llamar a JoinMulticastGroup con la misma dirección IP de Clase
dirección IP o un equipo remoto, has de indicar la dirección de D especificada para el grupo. Cuando quieras dejar la sala, sólo
broadcast para tu equipo. Esta dirección cambia de equipo a has de llamar a LeaveMulticastGroup y, nuevamente, indicar la
equipo, de modo que hay una propiedad en la clase UDPSocket dirección del grupo que deseas abandonar. Puedes unirte a
que te indica la dirección de broadcast correcta. tantos grupos multicast como desees, no estás limitado a sólo
uno de forma simultánea. Cuando quieras enviar datos al grupo
Este ejemplo emite un mensaje a todos los equipos de tu red
local:
171
de multicast, sólo has de indicar la dirección IP del grupo, Toda la
gente que se haya unido al mismo grupo recibirá el mensaje.

Este ejemplo envía datos a los miembros del grupo multicast:

Dim data As String


data = "Hello, World!"

If UDPSocket1.JoinMulticastGroup("224.0.0.0") Then
UDPSocket1.Write("224.0.0.0", data)
End If

172
Section 7

Crear un Servidor

Gestionar Múltiples Conexiones Para iniciar el proceso de escucha del ServerSocket, configura el
Si necesitas comunicarte con más de una aplicación mediante el puerto a escuchar asignando un valor a la propiedad Port y llama
mismo puerto, es difícil hacerlo con el TCPSocket dado que al método Listen del ServerSocket
cada TCPSocket puede gestionar sólo una conexión cada vez.
To initiate the ServerSocket’s listening process, set the port to
Para usar el TCPSocket deberías de implementar un sistema
listen to by assigning a value to the Port property and call the
capaz de gestionar múltiples TCPSockets, a medida que se
ServerSocket’s Listen method.
reciben nuevas conexiones.
Note: En OS X y Linux, el intento de vincular un puerto inferior a 1024
Para gestionar esta situación, debes de utilizar un control causará un evento SocketCore.Error con un error 105 salvo que tu
aplicación esté ejecutándose con permisos root. Esta es una característica
ServerSocket. Un ServerSocket es un socket permanente que
de seguridad en los sistemas operativos basados en Unix. No es un bug,
escucha en un único puerto por múltiples conexiones. Cuando sino una característica de seguridad que evita la posible aparición de
problemas debido a que un socket esté escuchando en puertos
se realiza un intento de conexión en dicho puerto, el privilegiados.
ServerSocket deriva la conexión a otro socket y continúa
El control ServerSocket gestiona automáticamente un pool de
escuchando sobre el mismo puerto. Sin el ServerSocket es difícil
TCPSockets disponibles para su uso. No has de crear
implementar esta funcionalidad debido a la latencia entre la
TCPSockets de forma explícita, en vez de ello defines el tamaño
conexión entrante, su gestión, la creación de un nuevo puerto de
de este pool usando las propiedades MinimumSocketsAvailable
escucha y la reiniciación del proceso de escucha. Si tienes dos
y MaximumSocketsConnected después de establacer el socket
conexiones entrantes prácticamente al mismo tiempo, una de las
de escucha. Si cambias la propiedad
dos se perdería debido a que no habría disponible un socket a la
MaximumSocketsConnected, no matará ninguna conexión en
escucha sobre dicho puerto.
curso. Simplemente no permitirá más conexiones hasta que
alguna de las existentes se haya liberado. Si cambias la

173
propiedad MinimumSocketsAvailable, puede disparar el evento socket. Si se destruye el ServerSocket antes de que use uno de
AddSocket del ServerSocket para ajustar el bufer interno. los sockets reservados, entonces también se destruirán los
sockets no utilizados. Hasta entonces, el ServerSocket es el
Cuando llamas al método Listen del ServerSocket, este llena en
padre del TCPSocket, y por tanto permanecerá el TCPSocket. Si
primer lugar su pool interno de sockets disponibles. Lo hace
un socket devuelto en el evento AddSocket ha sido asignado a
llamando al evento AddSocket. Se llamará a este evento hasta
una conexión y se destruye el ServerSocket, el socket seguirá
que tenga suficientes sockets en el pool interno de sockets
conectado y continuará funcionando.
disponibles. Añade la cantidad indicada en la propiedad
MinimumSocketsAvailable, más diez sockets extra. (Ten en
cuenta que si devuelves Nil desde este evente, causará un
NilObjectException.) El ServerSocket no está listo para gestionar
las conexiones hasta que se haya completado este proceso. Las
conexiones que entren mientras que el servidor está poblando su
pool serán desatendidas. Para determinar si el ServerSocket está
listo para aceptar conexiones entrantes, comprueba su
propiedad IsListening.

Un ServerSocket solo puede devolver un TCPSocket (o una


subclase de TCPSocket) en su evento AddSocket.

Nota: El ServerSocket solo funciona con TCPSockets. Dado que UDP es un


protocolo sin conexión, no tiene sentido que un ServerSocket se encargue
de los UDPSockets.

Cuenta de Referencias
La cuenta de referencias no se incremente cuando devuelves un
socket con el método AddSocket. En lugar de ello, el socket se
puebla internamente y su cuenta de referencia se incrementa
cuando sel servidor gestiona una nueva conexión para dicho

174
Section 8

Comunicación entre Procesos

Puedes utilizar la comunicación entre procesos como un modo aplicación principal. Estas aplicaciones de consola pueden
de que puedan comunicarse entre sí dos aplicaciones del mismo comunicarse con la aplicación principal usando un IPCSocket.
ordenador.

IPCSocket
Para ello, utilizas la clase IPCSocket, que funciona de forma
similar a la clase TCPSocket. En vez de inficar una dirección IP y
un puerto, indicas una ruta a un “archivo socket”. Ambas
aplicaciones deben de utilizar la misma ruta. El archivo es usado
para la comunicación.

Nota: El archivo socket no se elimina después de cerrar la conexión. Debes


de eliminar dicho archvo en tu código o poner el archivo en la carpeta
temporal del sistema, donde el OS pueda eliminarlo en caso necesario.

Alguno ejemplos prácticos de un IPCSocket son:

• Comunicaicón entre dos aplicaciones. Por ejemplo cuando


ejecutas tu aplicación en el depurador, Xojo y tu aplicación se
comunican entre sí usando un IPCSocket.

• Para crear nuevas tareas. Si realizas cálculos extremadamente


complejos, probablemente querrás dejarlos ejecutándose en
sus propias aplicaciones de consola que se ejecutan desde la

175
Section 1

Temporizadores

Un Timer es una clase que ejecuta código a intervalos regulares (ModeOff). El siguiente código en un temporizador incrementará
o de forma periódica. Suponen un gran modo de que tu un contador mostrado en una Label:
aplicación pueda realizar un proceso mientras que de otro modo
estaría desocupada, lo que a veces puede ser un buen modo de
Dim value As Integer
dar la ilusión de concurrencia.
value = Val(CounterLabel.Text)
Los temporizadores se utilizan por lo general en combinación value = value + 1
con los threads para proporcionar actualiaciones a la IU (interfaz CounterLabel.Text = Str(value)
del usuario) durante procesos de segundo plano.

Asegúrate de que el temporizador se configura para ejecutarse


Usar un Timer
En las aplicaciones de escritorio, usa la clase Timer para crear un en modo repetido (Mode = ModeMultiple, o 2) y que el Period

temporizador. Las aplicaciones Web utilizan la clase WebTimer. está configurado a un valor como 1000, de modo que se

ambas funcionan de forma similar. actualice cada segundo.

El código que quieres ejecutar de forma periódica se encuentra Recuerda que no se llamará al temporizador si tu aplicación está

en el evento Action. Especifias el periodo usando la propiedad ocupada haciendo otra cosa. De modo que si bien el anterior

Period. Esta indica al temporizador con cuánta frecuencia se temporizador está configurado para dispararse cada segundo,

llama al evento Action. puede que se haga con menos frecuencia si tu aplicación está
realizando algún proceso significativo.
La propiedad de modo se utiliza en combinación con esta para
indicar si el temporizador se ejecuta repetidamente Si necesitas un control más preciso de tu procesado, querrás

(ModeMultiple), sólo una vez (ModeSingle) o bien se desactiva usar los hilos.

177
Section 2

Hilos

Los hilos suponen un modo de ejecutar código en segundo desde el hilo se considera parte del hilo y también funcionará en
plano. Son particularmente útiles para tareas que requieren de segundo plano.
un procesado largo y que, de otro modo, harían parecer que tu
Para iniciar un hilo has de llamar a su método Run, lo que
aplicación estuviese “congelada”, dado a que no ocurriría
ejecutará el código situado en el manejador de evento Run.
ninguna actualización en la interfaz de usuario.
Prioridad de Hilo
Los hilos son cooperativos, lo que significa dos cosas:
Por defecto un hilo tiene una prioridad de 5. Esta es la misma
• Son relativamente fáciles de usar prioridad que el hilo de la aplicación principal, de mdoo que si
dejas tu hilo en 5 éste tendrá asignada la misma cantidad de
• Sólo funcionan en un núcleo de la CPU
tiempo que el hilo principal.
Nota: Los hilos o hebras pre-emptivas, que es la capacidad de ejecutar el
código en múltiples núcleos de la CPU, son muy complejos y no están Por ejemplo, pongamos por caso que hay 100 “unidades” de
soportados por Xojo. Si necesitas ejecutar procesos en múltiples núcleos, tiempo de hilo disponible. Si tanto el hilo principal como tu hilo
debes considerar el uso de varias aplicaciones de consola en combinación
con los IPCSockets para la comunicación.
tienen una prioridad de 5, entonces la unidad de tiempo dividido
se calculará tal que así:
Crear un Hilo
Para crear un hilo, has de añadir en primer lugar un objeto Prioridad total = 5 (principal) + 5 (tu hilo) = 10
Thread a tu proyecto. Puedes hacerlo arrastrando un Thread Unidades de Tiempo = (5/10) * 100 = 50
desde la librería sobre la ventana, página web o navegador.
Esto significa que el hilo principal se ejecutará 50 veces y tu hilo
El código que quieras ejecutar en el hilo es el situado en el 50 veces.
manejador de evento Run. Cualquier cosa a la que accedas

178
Pero, ¿y si quieres que tu hilo se ejecute con más frecuencia Comunicar con la Interfaz de Usuario
porque está haciendo un proceso pesado? En este caso querrás Debido a las restricciones del sistema operativo, los hilos no
aumentar su prioridad. Si cambias la prioridad de tu hilo a 15, pueden acceder directamente o manipular ningún elemento de la
entonces la unidad de tiempo se calcula del mismo modo:
interfaz de usuario. Si tienes un hilo que necesite actualizar la
Prioridad Total = 5 (principal) + 15 (tu hilo) = 20 interfaz de usuario de algún modo, como actualizar la Barra de
Progreso, debes de utilizar un Timer como intermediario.
Unidades de Tiempo (tu hilo) = (15/20) * 100 = 75
En vez de dejar que el hilo actualice directamente una Barra de
Unidades de Tiempo (hilo principal) = (5/20) * 100 = 25
Progreso, ten un Timer que obtenga a intervalos regulares el valor
Esto significa ue tu hilo obtendrá 75 de las 100 unidades de de progreso desde el hilo, y que sea el encargado del actualizar
tiempo y que el hilo principal obtendrá sólo 25. De modo que tu la Barra de Progreso.
hilo se ejecutará 3 veces más rápido que el hilo principal.
Este es un ejemplo del manejador de evento Run de un hilo que
Control de Hilo se ha añadido a una ventana. El hilo simplemente itera por un
Los hilos pueden dormirse, suspenderse, reactivarse y matarse. array (con una pausa en la mitad). La posición del array se
Cuando duermes un hilo indicas la cantidad de tiempo específica almacena como una propiedad de la ventana (ArrayPostion) al
(en milisegundos) que ha de permanecer inactivo. Se despertará igual que el máximo valor del array (ArraySize):
automáticamente cuando haya transcurrido dicha cantidad de
tiempo. Si suspendes un hulo, este permanecerá suspendido Dim arrayValues() As Integer
hasta que lo reactives de forma específica. Por último, puedes arrayValues = Array(1, 2, 3, 4, 5, 6, 7, 9, 10)
matar un hilo para finalizarlo. ArraySize = arrayValues.Ubound
For i As Integer = 0 To ArraySize
ArrayPosition = i
Cada una de estas acciones cambia el estado del hilo. Puedes
App.SleepCurrentThread(1000) // Pause for 1 second
comprobar el estado de un hilo en cualquier momento usando la Next
propiedad State. Un hilo puede estar Running (0), Waiting (1),
Suspended (2), Sleeping (3) o NotRunning (4).
Un Timer independiente puede comprobar el valor
correspondiente a ArrayPosition y ArraySize y utilizarlo para
179
actualizar la Barra de Progreso. Este código está en el manejador A medida que funciona el hilo, este actualiza la propiedad de la
de evento Action de un Timer en la ventana: ventana. El hilo actualiza de forma periódica la Barra de Progreso
en la ventana con el valor de la propiedad.

If CountThread.State = Thread.NotRunning Este proceso en dos pasos evita que el hilo actualice
Then directamente la interfaz de usuario.
Me.Enabled = False
MsgBox("Finished!") Usar la clase Task
Else En Xojo encontrarás un ejemplo de una clase Task que puede
ThreadProgress.Value = ArrayPosition utilizarse para hacer esto también (Desktop/
UpdatingUIFromThread/UIThreadingWithTask). Task es una
ThreadProgress.Maximum = ArraySize
subclase de Thread que tiene un manejador de evento UpdateUI
End If
y un método. Utilízalo en vez de un Thread cuando quieras que tu
hilo pueda actualizar la interfaz de usuario. En el evento Run de la
Task puedes llamar al método UpdateUI cuando quieras
En el manejador de evento Action de un botón de la ventana, este
actualizar cualquier IU. Entonces, en el manejador de evento de
es el código encargado de iniciar el hilo y el temporizador:
UpdateUI puedes incluir el código que acceda directamente a la
IU.
If CountThread.State = Thread.NotRunning
Cuando llamas a UpdateUI pasas una lista de valores (usando
Then
una serie de Pairs — pares) o bien usando un diccionario de
CountThread.Run valores.
CountTimer.Period = 500
CountTimer.Mode = Timer.ModeMultiple Independientemente, en el manejador de evento UpdateUI,
CountTimer.Enabled = True obtienes un diccionario de los valores. Puedes comprobar los
valores del diccionario para determinar qué actualizar en la IU.
End If

180
Section 1

Acerca de la Depuración

Qué es Depurar Bugs Lógicos


Depurar significa eliminar errores, tanto lógicos como Estos son los fallos en la lógica de tu programación. Sabrás que
sintácticos, del código de tu aplicación. Los errores en código de has encontrado uno de estos cuando el código compile pero no
programación se denominan “bugs”. Probablemente te produzca los resultados esperados. El depurador puede ayudar
preguntes por qué se llama “bugs” a los errores. Bien, allá en a encontrar estos tipos de fallos dejándote observar cómo se
1940 la Marina de los Estados Unidos tenía ordenadores que ejecuta tu código línea a línea.
ocupaban todo un almacén. En aquél tiempo, los ordenadores
Bugs Sintácticos
utilizaban tubos de vacío y la luz de los tubos atraían a los
Estos son los bugs donde has escrito mal el nombre de una
insectos. Estos insectos accedían al interior del ordenador y
palabra clave, clase, propiedad, variable o método. También
cortocircuitaban los tubos. Los técnicos tenían que ir y eliminar
puede que hayas intentado utilizar dos valores juntos que no
los bichos (bugs) para lograr que el ordenador volviese a
puedan ir juntos. Por ejemplo, si intentas asignar un valor String
funcionar. Dado que este era un proyecto del fobierno, todo a una variable o propiedad de tipo Integer, obtendrás un error
quedaba registrado, de modo que ponían “quitar bichos del Type Mismatch porque se trata de tipos diferentes.
ordenador” en el registro. Suficiente como lección de historia.
Analizar el Proyecto
Depurar es parte de la programación. Es la parte de la
Dado que no es posible compilar el proyecto si contiene errores
programación que menos disfrutan los programadores. de sintaxis, tienes la opción de analizar el proyecto como paso
Afortunadamente, el depurador facilita el hallazgo de estos bugs preliminar. Selecciona Proyecto ↠ Analizar Proyecto o Proyecto
y su eliminación como, bien, bichos.
↠ Analizar Item, donde Item es el item actual en la ventana del
IDE. Analizar Proyecto comprueba la existencia de problemas
pero no compila el proyecto. Algunos problemas que puede

182
identificar son los errores de sintaxis, las variables locales y Los botones Tipo y Ubicación en el panel de Problemas te
parámetros no utilizados, y los problemas de conversión de tipos. permiten cambiar el modo de ver la información. El botón Tipo
agrupa los problemas por su tipo. De modo que todas las
Figura 8.1 “variables locales no utilizadas” aparecerán agrupadas.

El botón Ubicación agrupa los problemas por el objeto en el que


tienen lugar. Así que todos los problemas de Window1
aparecerán reunidos.

Analizar Item
El comando Analizar Item, disponible desde el menú Proyecto o
en la barra del Editor
de Código, funciona Figura 8.2
Los errores se indican mediante el signo de Stop y evitan la
posibilidad de compilar o ejecutar tu proyecto. Las advertencias como Analizar
se indican mediante el triángulo amarillo de advertencia, y no Proyecto, pero sólo
evitan que puedas compilar o ejecutar tu proyecto. analiza el código del
Editor de Código.
Si se encuentran errores o advertencias estos se listan en el
panel de Errores en la parte inferior del Espacio de trabajo. Filtrar tipos de
Expande cada fila mostrada para ver los problemas individuales.
Avisos
Puedes controlar los
Puedes hacer clic sobre cada problema para ver el editor
tipos de
correspondiente (generalmente el Editor de Código), con el
advertencias
problema resaltado.
mostrados por el
Deberías corregir cualquier error reportado. Y deberías de evaluar panel Errores. Selecciona Advertencias en el menú Proyecto par
las advertencias para ver si crees que deban de resolverse. No ver un cuadro de diálogo que lista todos los tipos de errores y
todas las advertencias han de ser resueltas. advertencias mostrados por el panel. Cuando analices, sólo se
reportarán las advertencias seleccionadas. Puedes deseleccionar
cualquier advertencia de la que no quieras tener conocimiento.
183
184
Section 2

Usar el Depurador

Cuando ejecutas tu proyecto desde Xojo, estás usando el mostrará el código fuente donde ha tenido lugar el error con la
depurador. Si no hay errores tras compilar y crear tu aplicación, línea que lo ha provocado. A partir de aquí podrás ver los valores
éste se ejecuta y el Espacio de trabajo cambia a la pestaña de de las variables y revisar la pila de llamadas (ver más adelante).
depuración. Esto se denomina ejecutar el proyecto en “modo de
Punto de parada
depuración”. Cuando estés utilizando tu aplicación no puedes
ver el depurador, pero hay varias modos para activarlo. Puedes definir puntos de parada en tu código. Un punto de
parada es un indicador que dice al depurador que se active
Activar el Depurador cuando se alcance la línea de código.

Activación Manual Es posible que quieras definir un punto de parada al inicio de un


Puedes activar manualmente el depurador haciendo clic en el método para revisar el código a medida que se ejecute.
botón de Pausa en la barra de herramientas del Depurador
cuando estés ejecutando tu aplicación. Si tu código se está Para definir un punto de parada,
haz clic en los “guiones” que Figura 8.3
ejecutando, entonces se mostrará el método en funcionamiento
y se destacará la siguiente línea de código a ejecutar. Si tu aparecen en el margen del Editor

aplicación está desocupada, entonces saltará en el Bucle de de Código. Cada guión indica una

Eventos donde podrás ver los objetos globales y sus variables línea de código sobre la que

(como App, Runtime y los módulos). puedes definir un punto de


parada. También puedes activar
Error en la Aplicación un punto de parada usando el
Si tu aplicación eleva una excepción no gestionada en modo de menú Proyecto ↠ Punto de
depuración, entonces el depurador pasará a estar activo. Se parada ↠ Activar (⌘ +\ en OS X, Ctrl-\ en Windows y Linux). El

185
mismo comando desactiva un punto de parada previamente El comando #If indica al compilador que sólo incluya este código
activado en la misma línea. Si quieres desactivar todos los puntos cuando cree una versión de depuración. Se elimina cuando
de parada del proyecto, utiliza el menú Proyecto ↠ Punto de generas una compilación normal. El comando Break es llamado

parada ↠ Borrar Todo. cuando i alcanza 75 y activa el depurador.

Para ver todos los puntos de parada en tu proyecto, usa La Pantalla de Depuración
Proyecto ↠ Punto de parada ↠ Mostrar Todo para ver todos los Una vez que estás en el depurador puedes controlarlo utilizando

puntos de parada en el panel Buscar.


Figura 8.4
Punto de parada Condicional
Hay ocasiones en las que quieres detenerte en un punto de
parada, pero sólo cuando tiene lugar una condición concreta. Por
ejemplo, podrías estar en un bucle largo y querer detenerte sólo
en el elemento 75th.

Puedes definir un punto de parada condicional en tu código


fuente usando una combinación de la instrucción condicional If y
el comando Break. Este ejemplo se detendrá en el punto de
parada cuando el contador de bucle alcance 75:

For i As Integer = 1 To 100 los comandos de la barra de herramientas: Pausar/Continuar,


#If DebugBuild Then Detener, Paso, Dentro, Fuera y Editar Código.
If i = 75 Then Break
• Pausar/Continuar: Utiliza Pausa para detener una aplicación
#Endif
en funcionamiento y activar el depurador. Si estás en el
Next
depurador, el botón Continuar indica a la aplicación que prosiga
su funcionamiento donde lo dejó. También puedes hacer clic en
el botón Ejecutar de la barra principal, seleccionar Proyecto
186
Project ↠ Continuar desde el menú o bien usar el atajo ⌘ +R • Fuera: Si estás es un método, haz clic en Fuera para ejecutar el
(Ctrl-R en Windows y Linux). resto del código del método y detenerte en el retorno del
método.
• Parar: El botón Parar detiene de inmediato la ejecución de la Además de hacer clic en el botón, puedes usar el comando de
aplicación. La aplicación sale de inmediato y no se ejecuta
menú Proyect ↠ Paso ↠ Dentro o el atajo Shift+⌘ +T (Shift-
ningún código adicional. También puedes usar el atajo Shift+⌘
Ctrl-T en Windows y Linux).
+R (Shift-Ctrl-R en Windows y Linux)
• Editar Código: El botón Editar Código te permite saltar al
• Paso: El botón Paso se utiliza para ejecutar el código una línea
Editor de Código para el método actual que está en el
cada vez. Cada vez que haces clic en Paso, se ejecuta el
depurador. Aquí puedes editar el código (algo que no puedes
código seleccionado y continúas en el depurador. Si haces clic
hacer en el código mostrado por el depurador). Sin embargo,
en Paso sobre una llamada de método, entonces se llama al
los cambios realizados en el Editor de Código no se verán
método y pasas a la siguiente línea de código. Por lo general
reflejados en tu aplicación hasta la próxima vez que lo ejecutes.
utilizarás principalmente Paso durante las sesiones de
depuración. Además de hacer clic sobre el botón, puedes usar Observar Variables
el comando de menú Proyecto ↠ Paso ↠ Saltar o el atajo Shift El panel de observación del depurador te permite ver los valores
+⌘ +O (Shift-Ctrl-O en Windows y Linux). de las variables. Por omisión muestra las variables actuales
declaradas para el método que se esté ejecutando.
• Dentro: El botón Dentro funciona como el botón paso excepto
cuando llegas a la llamada a un método. En vez de llamar al Figura 8.5
método y pasar a la siguiente línea de código, Dentro se mueve
a la primera línea de código en el método. Además de hacer
clic en el botón puedes usar el comando de menú Proyecto ↠
Paso ↠ Dentro o el atajo Shift+⌘ +I (Shift-Ctrl-I en Windows y
Linux).

187
Puedes cambiar los valores de booleanos, enteros, dobles y En alguno sobjetos (como ventanas y RecordSets), hay un enlace
cadenas haciendo clic sobre el valor (o seleccionando el icono especial en la parte superior denominado “Contenidos”.
del lápiz en la derecha), introduciendo un nuevo valor y pulsando Haciendo clic sobre él muestra información sobre el objeto, como
retorno. los controles de una ventana o los campos en un RecordSet.

Nota: En los booleanos, cualquier cosa que se introduzca diferente de True Ver el Código Fuente
o False será tratado como False.
El código fuente para el método actual (o evento) se muestra en
Con las cadenas, el icono de lápiz de la derecha cambia para
el visor de código del depurador. Se destaca la siguiente línea a
mostrar una lupa. Al hacer clic sobre ella se muestra el editor de
ejecutar. En el visor de código puedes definir puntos de parada
cadenas que te permite ver la cadena como texto o binario. Te
adicionales y ver cómo se mueve la parte destacada a medida
permite hacer cambios en la cadena usando una área de edición
que avanzas por el código.
de mayor tamaño.

En el caso de los enteros, puedes cambiar el formato haciendo


clic sobre el valor y seleccionando Ver como Decimal, Hex, Figura 8.6
Binario u Octal.

Para los dobles, puedes cambiar el formato de visualización


haciendo clic derecho sobre los valores y seleccionando Ver
como Científico, Decimal o Redondeado.

Si el tipo de variable es una clase, entonces puedes hacer clic


sobre el tipo para mostrar los valores de propiedad. Si quieres editar el código, haz clic en el botón Editar Código en
la barra de herramientas. Los cambios realizados sobre el código
En la parte superior del panel de variables hay un menú no tendrán efecto hasta la próxima vez que se ejecute el
desplegable que te permite navegar por la jerarquía de variables. proyecto.
Puedes usarlo para regresar rápidamente a las variables del
Figura 8.7
método después de haber mostrado el editor de cadenas o los Ver la Pila de
detalles de la instancia de clase. Llamada
La pila de llamada
muestra la jerarquía de tus métodos. Si el evento Open de tu
188
ventana principal llama a un método denominado Initialize,
entonces la pila de llamada muestra Initialize en la parte superior
(dado que es el método actual), con Window1.Open bajo él.

La pila de llamada es muy útil para ver tu ruta de código y


observar cuáles son los métodos que llaman a otros métodos.

Ver Hilos
Cada hilo en tu aplicación queda registrado de forma
independiente en el depurador.

Utiliza el selector en la sección de Pila para ver y mostrar los hilos


que se estén ejecutando.

189
Section 3

Gestión de Excepciones

El depurador puede ayudarte a verificar que tu código está


funcionando como esperas y permite encontrar errores. Una vez Dim d As Date
que has encontrado la fuente de los errores, querrás asegurarte d.Month = 8 // NilObjectException
de gestionarlos adecuadamente.

Las excepciones son el tipo de error que sucede cuando ocurre Otro lugar en el que puedes comprobarlo es en el valor devuelto
algo inesperado. Estos errores colgarán tu aplicación si no los por los métodos y que devuelven una instancia. Algunos
gestionas de algún modo. El acto de causar una excepción se métodos devuelven Nil en ciertas situaciones, como por ejemplo
denomina elevar una excepción. GetOpenFolderItem que devuelve Nil si el usuario hace clic en
Cancelar:
Todas las excepciones son subclases de la clase
RunTimeException.
Dim f As FolderItem
Excepciones NilObject f = GetOpenFolderItem("")
El tipo más común de excepción es una NilObjectException. If f.Exists Then
Estos errores tienen lugar cuando intentas usar un objeto pero // Cancel causes NilObjectException
no tienes una instancia de él. // Do something
End If
El ejemplo más sencillo de esto es olvidar el uso de New para
obtener una instancia antes de que intentes usar una propiedad
o llamar a un método:
Para evitar este tipo de errores, siempre debes de comprobar si
el valor es Nil antes de intentar acceder a él:

190
Dim f As FolderItem Dim xmlFile As FolderItem
f = GetOpenFolderItem("") xmlFile = GetOpenFolderItem("")
If f <> Nil Then If xmlFile <> Nil Then
If f.Exists Then Try
// Do something Dim xml As New XmlDocument
End If xml.LoadXml(xmlFile)
End If Catch e As XmlException
MsgBox("Not an XML file!")
Return
Try Catch End Try
Algunas veces puedes encontrar que una excepción es una parte End If
normal de un proceso. Lo que querrás hacer en estas situaciones // Process the XML
es atrapar la excepción de modo que puedas gestionarla
adecuadamente. El comando Try…Catch se usa para este
Si no se eleva una XMLException entonces se ejecuta el código
propósito.
de la sección Try. Si se eleva una XMLException, entonces se
Por ejemplo, la carga de un archivo XML puede elevar una ejecuta el código de la sección Catch.
XMLException si el archivo no es un XML válido. Puedes
comprobarlo utilizando un bloque Try…Catch: Excepción
El comando Exception es una versión simplificada de Try…Catch.
En vez de centrarse en un bloque concreto de código, Exception
atrapa los errores correspondientes a todo el método.

El comando Exception se encuentra al final del método y se llama


en el caso de que se genere una excepción en cualquier parte del
método. El anterior ejemplo podría escribirse de esta forma
usando el comando Exception:
191
aplicación continúe funcionando (algo que no es lo más
Dim xmlFile As FolderItem recomendable).
xmlFile = GetOpenFolderItem("")
Este código muestra un mensaje más amigable al usuario,
If xmlFile <> Nil Then
saliendo a continuación de la aplicación:
Dim xml As New XmlDocument
xml.LoadXml(xmlFile)
End If Dim msg As String = "An error occurred.
// Process the XML Please notify the author."
MsgBox(msg)
Catch e As XmlException
MsgBox("Not an XML file!") Quit
Return Return True

Si bien esto es más sencillo, no es tan evidente qué está también puedes mostrar la pila de ejecución de modo que pueda
ocurriendo y también es menos flexible. utilizarse para hallar la ubicación del problema con vistas a
solucionarlo:
App.UnhandledException
Hay un manejador de evento especial en el objeto App de tu
Dim msg As String = "An error occurred.
proyecto y que es llamado en el caso de que no se haya tratado
Please notify the author. Stack: "
una excepción.
MsgBox(msg + Join(error.Stack,
El evento tiene un parámetro, error, y que es la excepción EndOfLine))
propiamente dicha. Puedes poner código en este manejador de
evento para mostrar un mensaje al usuario, capturar información Quit
de registro o cualquier otra cosa que desees. Devuelve True para Return True
ocultar el cuadro de diálogo de error estándar e intentar que tu

192
Crear tus propias Excepciones
Dado que la clase RuntimeException es como cualquier otra,
puedes subclasearla para crear tus propias excepciones. Esto te
permite elevar tus propias excepciones definidas por ti.

Si has creado una subclase de RuntimeException denominada


InvalidXMLFileFormatException entonces puedes elevarla usando
esta sintaxis:

If incorrectXmlFileFormat Then
Raise InvalidXMLFileFormatException
End If

193
Section 4

Depuración Remota

Cuando ejecutas tu proyecto este funciona sobre la misma local. Si tienes configurado el Depurador Remoto, puedes indicar
plataforma que estás utilizando. De modo que si desarrollas en a Xojo que envíe y ejecute tu compilación de depuración sobre
OS X, entonces al hacer clic en Run se ejecutará tu proyecto un ordenador remoto, lo que resulta tremendamente útil para
sobre OS X. probar aplicaciones multiplataforma. Esta compilación remota
aun se comunica con Xojo, de modo que puedes acceder al
Dado que Xojo es una herramienta de desarrollo multiplataforma,
depurador y probarla como si se estuviese ejecutando en local.
es probable que también quieras ejecutar tus proyectos en otras
plataformas con objeto de depurarlas. Puedes hacerlo utilizando Configuración de Depuración Remota de
el Depurador Remoto que tiene dos versiones: Desktop y Escritorio
Consola. En el equipo remoto has de ejecutar Figura 8.8
el Remote Debugger Desktop. Esta
La versión de escritorio se utiliza para depurar en remoto las
aplicación se incluye como parte de
aplicaciones de escritorio. La versión de consola se utiliza para
la instalación de Xojo. Existe una
depurar en remoto las aplicaciones de consola y web.
versión específica para cada
Depurador Remoto plataforma (Windows, OS X, Linux).
El Depurador Remoto es un pequeño programa que te permite Copia la versión que necesites a la
ejecutar tu proyecto sobre diferentes plataformas de destino. plataforma que estés usando.
Xojo se comunica con ella de modo que, cuando ejecutas tu
El Remote Debugger Desktop tiene
proyecto, este la envía a la plataforma de destino en vez de
una ventana de Opciones que te
ejecutarla en local. Normalmente, cuando ejecutas tu proyecto
permite definir los ajustes. En la
desde Xojo (Proyecto ↠ Run), este ejecuta la depuración en
pestaña General, querrás dar un

194
nombre al equipo y seleccionar la ubicación de Descarga. Por lo El Depurador Remoto de Consola se ejecutará desde el Terminal
general, no tienes por qué en la línea de comandos. La primera vez que lo ejecutes se te
cambiar los ajustes de la Figura 8.9 pedirán los siguientes ajustes:
pestaña Red.
• Nombre del equipo
Una vez que hayas
• Directorio de Descarga
configurado las opciones,
haz clic en OK y deja • Dirección IP: Indica la dirección IP sobre la que se escuche
funcionando el Remote (debe de corresponderse con una IP disponible en el
Debugger Desktop. ordenador).

Si estás usando un firewall en el equipo remoto, asegúrate de • Conexiones Máximas: Define la cantidad máxima de
que esté abierto el puerto 44553 para las conexiones UDP y TCP. conexiones.

Remote Debugger Console • Auto ejecutar: Permite que el depurador remoto ejecute
La versión de Consola puede utilizarse para depurar en remoto automáticamente la app
las aplicaciones de consola, las apliaciones web autónomas y las a depurar.
Figura 8.10
aplicaciones web CGI sobre equipos remotos que no tengan
• Público: Indica si el
interfaz de escritorio. Actualmente sólo puede utilizarse en la red
depurador remoto es
local (o VPN).
visible para todos.
Si estás intentando depurar una app CGI, configura el Depurador
• Contraseña: Indica una
Remoto para que NO se ejecute automáticamente, y define la
contraseña de conexión.
ruta para que apunte donde se instalen tus aplicaciones CGI, de
modo que puedan funcionar con el servidor Web. El servidor web Estos ajustes se guardan
ejecutará la app cuando visites la url correspondiente en tu en el archivo RDS.config
navegador. en la carpeta del Console
Remote Debugger.

195
También puedes proporcionar otras Figura 8.11 Depuración Remota
opciones mediante la línea de Ahora puedes probar a ejecutar un proyecto de forma remota. En
comandos. Usa el argumento “-- el equipo de desarrollo, crea un nuevo proyecto.
help” para obtener una lista de las
opciones disponibles desde la línea Para ejecutar el proyecto de forma remota, en vez de seleccionar

de comandos. Proyecto ↠ Ejecutar (o hacer clic en el botón Ejecutar de la barra


de herramientas), selecciona Proyecto ↠ Ejecutar remoto y haz
Configuración del clic sobre el nombre del equipo remoto. Tu proyecto se compila y
Equipo de Desarrollo enlaza como de costumbre, pero ahora verás un paso adicional
En el equipo de desarrollo has de configurar Xojo para que pueda donde esta compilación de depuración se enviará hacia el
ver al Depurador Remoto. En las preferencias, selecciona Remote Debugger Stup en el equipo remoto.
Depurador. Aquí verás un listado de los equipos remotos ya
configurados. Cuando el Remote Debugger Stub haya recibido la compilación
depurada, éste la ejecutará. Interactúa con ella sobre el equipo
Haz clic en Añadir para añadir tu equipo remoto. En función de remoto. Cualquier punto de parada que hayas configurado saltará
cuál sea tu configuración de red, el equipo remoto podría en el depurador sobre el equipo de desarrollo.
aparecer como un “equipo remoto autodetectado”. Si es así, sólo
has de hacer clic sobre su bnombre y en Ok para añadirlo. Si no
aparece, puedes introducir su dirección IP (indicada en el Remote
Debugger Stub sobre el equipo remoto) y darle un nombre.

Si estás usando un firewall en tu equipo de desarrollo, has de


asegurarte de que esté abierto el puerto 44553 para las
conexiones UDP y TCP, y el puerto 13897 para las conexiones
TCP.

Nota: El softare de Equipos Virtuales como VMware Fusion, Parallels


Desktop, VMware Desktop y Microsoft Virtual PC funcionan correctamente
con la depuración Remota.

196
Section 5

Perfi lador

Se utiliza el Perfilador para medir la cantidad de tiempo utilizada Esta sección es un listado con todos
por cada método de la aplicación. Esta información te permite los métodos que han sido llamados Figura 8.13
centrarte en la optimización de las partes de tu aplicación que mientras usabas la aplicación.
puedan considerarse lentas. Mostrará la cantidad de veces que
se ha llamado a cada método y
Usar el Perfilador cuánto tiempo se ha gastado en el
Puedes activar o desactivar el Perfilador usando el menú método. Puedes guardar la
Proyecto ↠ Perfilar código. Cuando el perfilador esté activado información de perfilado a un archivo
Aparecerá una marca de verificación a su lado. de texto utilizando un menú contextual. Sólo has de hacer clic
derecho en cualquier parte del resumen y seleccionar “Guardar
Ahora ejecuta y utiliza tu proyecto tal y como harías
como…”.
normalmente. El perfilador se encargará de reunir información en
silencio sobre los métodos llamados y la cantidad de tiempo
requerida por cada uno de ellos en su ejecución. Ten en cuenta Figura 8.12
que tu aplicación funcionará más lenta de lo habitual cuando se
esté realizando un perfilado, debido a la sobrecarga de registrar
el tiempo para obtener la información de perfilado.

Cuando salgas de la aplicación, aparecerá la sección Perfiles en


el Navegador, con una entrada para la información de perfilado.
Puedes expandir los métodos para ver otros métodos llamados.

197
A medida que optimices tu aplicación, puedes comparar los la información de pefilado se guardará en un archivo llamado
datos actuales del Perfilador con los obtenidos previamente para Profile.txt en la misma carpeta de la
ver si tus cambios están mejorando el rendimiento. Figura 8.15 aplicación.

Nota: Los datos del perfilador se recolectan solamente si tu aplicación sale Usar el Perfilador con el
con normalidad. Si sale como consecuencia de un cuelgue (o si pulsas el
botón de parada en el depurador), no se reunirá la información del Depurador Remoto
perfilador.
El Perfilador no funciona con el Depurador
Nota: La información del Perfilador no puede reunirse para las apps de OS Remoto. Si necesitas utilizar el Perfilador
X que estén en la sandbox.
para reunir información sobre la app
Usar el ejecutándose en otra plataforma, sólo has
perfilador con Figura 8.14 de instalar Xojo sobre dicha plataforma,
aplicaciones copiar tu proyecto sobe ella y ejecutar la
ya creadas aplicación con el perfilado activado.
Puedes crear una
The Profiler does not work with the Remote Debugger. If you
versión autónoma de
need to use the Profiler to gather information about your app
tu aplicación con
running on another platform, just install Xojo on the platform,
código de perfilación
copy your project over to it and run it from there with profiling
embebido que
enabled.
puedas enviar a tus
usuarios. Para ello,
sólo has de crear tu
aplicación con la opción de Perfilar Código seleccionada en los
ajustes. Aparecerá un diálogo para recordarte que se embeberá
código de perfilado como parte de la aplicación.

Con este código de perfilado embebido, tu aplicación registrará


información de perfilado según se esté utilizando. Cuando salga,

198
Section 1

Constantes de Compilación

Antes de compilar tu aplicación, elige la plataforma o • TargetMacOS, TargetCocoa, TargetCarbon: True para los
plataformas sobre la que quieres que funcione. Puedes crear diversos destinos OS X.
aplicaciones para Windows, Linux y OS X como apps de
• TargetLinux: True cuando la app está funcionando sobre Linux.
escritorio, web o consola. Durante la programación puedes
Dado que Xojo crea apps de 32 bits, esto es True
activar o desactivar de forma selectiva segmentos de código que
independientemente de que Linux sea de 32 o 64 bits.
son válidos para una plataforma concreta, usando para ello las
constantes de compilación. • TargetX86: Actualmente esto siempre es True.

Constantes de Compilación • TargetHasGUI: True para aplicaciones desktop y web. False


Las constantes de compilación te proporcionan información para aplicaciones de consola.
sobre la plataforma en la que está ejecutándose la aplicación.
• TargetWeb: True para aplicaciones web.
Cuando se utilizan con la compilación condicional puedes
indicar bloques de código fuente a incluir o excluir para Estas constantes se definen automáticamente a True o False en
plataformas concretas. función de la plataforma para la que se compile. Puedes usar los
comandos de compilación condicional para usar código
Estas son las constantes de compilación:
específico como en este caso:
• DebugBuild: True cuando la aplicación funciona en modo de
Depuración

• TargetWin32: True cuando la aplicación está ejecutándose


sobre Windows. Dado que Xojo crea apps de 32 bits, esto es
True tanto si Windows es de 32 o 64 bits.

200
// Saving Preferences
#If TargetWin32 Then
// Use Registry
#ElseIf TargetMacOS Then
// Use plist
#ElseIf TargetLinux Then
// Use XML
#EndIf

Versiones
También puedes comprobar la versión que se está usando de
Xojo con las constantes XojoVersion y XojoVersionString.

Estas dos constantes pueden utilizarse para bloquear código que


no es compatible con las versiones más antiguas (o recientes) de
Xojo.

Nota: También puede que necesites utilizar las antigua RBVersion y


RBVersionString para no incluir el código que pueda usarse con las
versiones más antiguas.

201
Section 2

Automatizar la Creación

La característica Compilación Automatizada se utiliza para Dicho elemento te permite indicar si dicho Paso de Compilación
realizar automáticamente determinadas tareas a la hora de se realiza antes de crear el proyecto o después de hacerlo.
compilar el proyecto. Dichas tareas se denominan Pasos de Arrastra el Paso de Compilación antes del elemento Build para
Compilación. Hay tres pasos de comilación disponibles: Copiar que se procese antes de que se realice la Creación y arrástralo
Archivos, Script (Guión) y Scripts Externos (Guiones Externos). después del elemento Build para que se ejecute una vez que se
haya completado la Creación.
Para añadir un Paso de Compilación a tu proyecto, selecciona el
menú Insertar en la barra de herramientas y elige el submenú Cada Paso de Compilación tiene un ajuste de “Se aplica a” que
Paso de Compilación. te permite indicar: Ambos, Depuración o Release.

Los Pasos de Compilación se añaden en el área CONTENIDOS Depuración significa que el Paso de Compilación se procesa
del Navegador y están desactivados por omisión. sólo cuando se realice una Ejecución en el Depurador. Release
significa que el Paso de Compilación se procesará cuando se
Para activar un paso de Compilación, necesitas moverlo a un
cree una compilación autónoma. y Ambos indica que se
destino de compilación (Target). Para ello arrastra el Paso de
procesará en ambos casos.
Compilación desde el área CONTENIDOS al área COMPILACIÓN
del Navegador. Suelta el Paso de Compilación sobre uno de los Copiar Archivos
destinos (OS X, Windows o Linux), y pasará a estar activo El paso Copiar Archivos te permite copiar un archivo, una serie
cuando ejecutes o crees el destino. de archivos o carpeta a la ubicación indicada durante el proceso
de creación. En cada una de estas ubicaciones también puedes
Una vez que añades un Paso de Destino al destino, puedes
indicar una subcarpeta para los archivos. Las ubicaciones son:
expandirlo para ver dicho paso. Cuando lo hagas verás el paso
de compilación y un elemento denominado simplemente Build.

202
Carpeta Padre de la Aplicación, Carpeta de Recursos, Carpeta Padre de la App). En OS X, los archivos se copian en el Bundle
Frameworks, Carpeta Padre del Bundle y Carpeta Contents. de la Aplicación (Contents).

• Carpeta Padre de la App: En Windows y Linux, los archivos Script y Script Externo
indicados se copian junto con el ejecutable de la app. En OS X Los guiones suponen un modo muy potente de automatizar las
los archivos se copian junto al ejecutable en el Bundle de la compilaciones de tu aplicación, pueden contener una variedad de
Aplicación (Contents->Mac OS). comandos para controlar el proceso. Todos los comandos están
enumerados en la Referencia del Lenguaje, pero algunos de ello
Carpeta de Recursos: Los archivos indicados se copian en la
son: DoCommand, DoShellCommand y Speak.
carpeta Recursos. En Windows y Linux dicha carpeta se crea
junto con la aplicación propiamente dicha. En OS X, la carpeta de DoCommand se utiliza para ejecutar comandos incluidos en el
Recursos está contenida en el Bundle de la Aplicación (Contents- IDE, quizá el más útil es guardar el proyecto antes de ejecutarlo:
>Resources).

Carpeta Frameworks: En Windows y Linux dichos archivos se DoCommand "SaveFile"


copian en la carpeta Libs de la aplicación. En OS X, dichos
archivos se copian en la carpeta Frameworks del Bundle de la
Para añadir dicho guión a tu proceso de
Aplicación (Contents->Frameworks).
compilación, añade un Paso de Script
Carpeta Padre del Bundle: En Windows y Linux dichos archivos de compilación usando el menú Insertar
se copian en la misma carpeta que incluye el ejecutable (la o el botón Insertar.
misma que la Carpeta Padre de la App). En OS X, los archivos se
El guión se añade al Navegador y
copian en la carpeta que contiene el bundle de la Aplicación
aparece el Editor de Scripts. En el Editor de Scripts, introduce
propiamente dicho.
este código:
Carpeta Contents: En Windows y Linux el archivo se copia en la
misma carpeta que contiene el ejecutable (la misma Carpeta
DoCommand "SaveFile"

203
Ahora puedes arrastrar el guión a un destino en la sección BUILD Un Guión externo funciona de igual modo salvo que está
del Navegador, de modo que se ejecute. Si quieres que el guión almacenado en un archivo externo seleccionado por ti. Puedes
se ejecute antes de iniciar la compilación, arrástralo sobre el usar un guión externo cuando lo utilizas para varios proyectos.
elemento “Build”. Si quieres que se ejecute después, arrástralo
tras el elemento “Build”.

El comando Speak utiliza la voz interna para reproducir el texto


proporcionado. Puedes usarlo para que te permita saber cuando
se ha completado la tarea.

Speak "Build Complete."

DoShellCommand te permite ejecutar un comando del Shell.


Puedes ejecutar cualquier comando del shell que esté disponible
en el Terminal o Comando del Shell del sistema operativo. Un
comando del shell puede utilizarse para realizar una gestión de
archivos más robusta, para manipular permisos, ejecutar
comandos para firmar digitalmente tu aplicación o cualquier otra
cosa que quieras.

Este ejemplo ejecuta el comando codesign de OS X:

DoShellCommand "codesign MyApplication.app"

204
Section 3

Scripting del IDE

El Scripting del IDE es la capacidad de crear guiones que te DoShellCommand entre otros.
permitan manipular la interfaz de usuario de Xojo (IDE).
Todos los comandos de Scripting del IDE están enumerados en
Crear un Script del IDE la Referencia del Lenguaje.
Para crear un guión del IDE, seleciona Nuevo Guión del IDE en el
Este sencillo script guarda el proyecto actual:
menú Archivo. Esta acción abrirá el Editor XojoScript.

En este editor escribes el código utilizando el lenguaje de


Speak("Saving file.")
scripting,
DoCommand("SaveFile")
compuesto por
comandos del
Figura 9.1
lenguaje Xojo Para ejecutar el script, haz clic en el botón Run de la barra de
(como If…Then herramientas correspondiente al Editor de Scripts de Xojo.
o For…Next)
que puedes Guardar y Cargar Scripts del IDE
Los Scripts del IDE se guardan como archivos de texto y no se
utilizar con
incluyen con el proyecto.
comandos
específicos de Para guardar un guión, usa Archivo -> Guardar.
guiones del IDE
como Para cargar un guión existente, usa Archivo -> Abrir.
DoCommand o

205
Grabar un Script El proyecto de ejemplo IDECommunicator (Examples/Advanced/
En la barra de herramientas del Editor XojoScript hay un botón IDE Scripting/IDE Communication) es una app completamente
Grabar. Cuando lo pulses se grabarán cualquiera de los funcional que puede utilizarse para controlar Xojo desde la línea
comandos del IDE que uses y que tengan el correspondiente de comandos enviando Scripts del IDE a Xojo para que los
comando de script. ejecute.

Para probarlo, haz clic en el botón Grabar (el texto cambia a Xojo debe de estar funcionando para que la comunicación
Detener Grabación) y selecciona Archivo -> Abrir para abrir un funcione.
archivo.
Para usarlo, crea la app para tu plataforma y ejecútala a
Verás el comando DoCommand “OpenFile” aparecer en el Editor continuación desde la línea de comandos con la opción “-h” para
de XojoScript. ver cómo funciona. Este comando se ejecuta en OS X y Linux (#
indica el prompt de tu terminal en el Shell):
Haz clic en Ejecutar para ejecutar tu proyecto (no el guión) y
verás como aparece el comando en el editor:
# ./IDECommunicator -h
DoCommand “RunApp”

A medida que uses el IDE aparecerán en el Editor XojoScript


cualquiera de las acciones que tengan un comandos de guión
asociados. Este supone un gran modo de determinar los
comandos que puedes usar para automatizar cualquier cosa.

Controlar Xojo desde la Línea de Comandos


Aunque Xojo no puede ejecutarse des la línea de comandos, sí
puede controlarse usando una app independiente de línea de
comandos que se comunique con Xojo usando sockets IPC y la
ruta del “XojoIDE”.

206
Section 1

Enumeraciones

Una enum o enumeración es un grupo de Cuando añades una enumeración también le proporcionas un
constantes que puedes utilizar por su nombre. Para referirse a una enumeración se utiliza el nombre.
Figura 10.2
nombre. Puedes editar el nombre haciendo clic sobre él para
seleccionarlo y una segunda vez para editarlo.
Cuando creas una enumarión estás
creando un nuevo tipo de dato. Este tipo Aunque los elementos de las Figura 10.3
puede añadirse a un módulo, clase, enumeraciones son enteros en realidad,
ventana o cualquier otro objeto en tu no tienes por qué preocuparte por los
proyecto. Añade una valores interos. Sin embargo, en el caso
enumeración seleccionando de que debas de asignar un valor
Figura 10.1
Enumeración en el menú concreto a una enumeración, debes
Insertar o bien con el botón hacerlo asignándolo como parte del
Insertar. Esto mostrará una nombre.
enumeración en el
Para usar una enumeración en código,
Navegadore y el Editor de
refiérete al contenedor de la enumeración, el nombre de la
Enumeración.
enumeración y el nombre del elemento de la enumeración:
En el Editor de
Enumeración, usa el icono
Dim pictureSize As Window.Size
“+” para añadir un nuevo valor de enumeración al grupo. Utiliza
pictureSize = Window1.Size.Large
el botón “-” para eliminar una enumeración del grupo.

208
Si necesitas utilizar el valor entero almacenado por la
enumeración, debes de hacer un cast a entero:

Dim pictureSize As Integer


pictureSize = Integer(Window1.Size.Large)

Las enumearciones siempre son por defecto de tipo Entero.


Puedes cambiar el tipo a otros tipos de Entero (como por ejemplo
UI64), en el caso de que debas de almacenar valores de mayor
tamaño en la enumeración.

209
Section 2

AddHandler

El comando AddHandler se utiliza para que un método de un Para que funcione, debes de tener un método en la ventana que
objeto gestion el procesado de un evento de otro objeto. se llame MyTimerMethod y debe tener un parámetro para el
temporizador propiamente dicho:
Esto se utiliza generalmente para permitirte instanciar una clase
directasmente e implementar su evento sin tener que crear una
clase independiente. Sub MyTimerMethod(t As Timer)
// Your code goes here
Por ejemplo, si quieres implementar el evento Action de un
End Sub
Timer, necesitas crear en primer lugar una subclase. Puedes
llevarlo a cabo arrastrando un Timer sobre tu ventana o página
web, o bien añadiendo una subclase de Timer a tu proyecto. Nota: si el evento que estás gestionando tiene parámetros adicionales,
inclúyelos tras el parámetro inicial de la clase.
Como alternativa, puedes declarar el temporizador en tu código AddHandler funciona del mismo modo para los hilos:
y utilizar entonces AddHandler para que el evento Action se
gestione mediante un método de la ventana. Por ejemplo:
Dim t As New Thread
AddHandler t.Run, AddressOf MyThreadMethod
Dim t As New Timer t.Run
AddHandler t.Action, AddressOf MyTimerMethod
t.Mode = Timer.ModeMultiple
t.Period = 500
t.Enabled = True

210
Section 3

XojoScript

XojoScript permite que tus aplicaciones ejecuten código fuente Lo siguiente consiste en asignarle un guión. Esto se realiza
Xojo desde una aplicación en ejecución. Esta característica mediante la asignación de código fuente (como cadena) a la
supone un modo fantástico de permitir que tus usuarios amplíen propiedad Source. En este ejemplo, Script es el nombre de un
la funcionalidad de tu aplicación mediante el uso de sus propios objeto XojoScript añadido a una ventana:
guiones.

Los guiones pueden sacar todo el provecho del lenguaje Xojo, si Script.Source = "Print ""Hello, World! """
bien a través de un acceso limitado a su framekork. El tema
sobre Lenguaje de Guiones en la Referencia del Lenguaje
El comando Print da salida del texto proporcionado al evento
contiene los detalles sobre lo que está soportado.
Print de la instancia XojoScript.
Ejecutas guiones usando la clase XojoScript que proporciona
Nota: Puedes acceder al evento Print creando una subclase de XojoScript,
modos por los que puedes asignar código, ejecutarlo, arrastrando un control XojoScript a tu diseño o usando AddHandler.
proporcionar entrada, obtener salida, gestionar errores, conectar
Este código en el evento Print muestra un mensaje con el texto
con tu aplicación, etc.
proporcionado:

Ejecutar Código
Para empezar, necesitas una instancia de la MsgBox(msg)
clase XojoScript. Puedes arrastrar un control
XojoScript sobre una Window, WebPage o
Container. O bien puedes crear una instancia desde código. Por último, puedes ejecutar el guión llamando al método Run:

211
RuntimeError
Script.Source = "Print ""Hello, World!"""
Se llama al evento RunTimeError cuando se produce una
Script.Run
excepción en tiempo de ejecución durante la ejecución del guión.

Ele vento proporciona el error como una RunTimeException que


Gestión de Errores puedes utilizar para determinar el tipo de excepción que se ha
Son dos los eventos utilizados para gestionar los errores: producido.
CompileErrror y RuntimeError.
Es importante gestionar este evento si estás permitiendo que el
CompilerError usuario proporcione sus propios guiones.

El evento CompilerError es llamado en el caso de que haya un Este código puede mostrar la excepción en tiempo de ejecución
problema compilando el guión, debido a un error de sintaxis o que ha tenido lugar:
bien a otro problema. Este evento te proporciona la ubicación del
error (usando la clase XojoScriptLocation), el error (como
enumeración de XojoScript.Errors, y errorInfo como Dictionary).

Es importante gestionar este evento si estás permitiendo que el Dim dialog As New MessageDialog
usuario pueda proporcionar guiones. dialog.Title = "Unhandled Exception"
dialog.Message = "Unhandled Exception"
CompilerWarning dialog.Explanation = "A " +

El manejado de evento CompilerWarning es llamado cuando Introspection.GetType( error ).Name + " was
not caught."
existe una advertencia de compilación sobre el script. Esto podría
If error.Message <> "" Then
indicar variables locales no usadas, deprecaciones y otros avisos.
dialog.Explanation = dialog.Explanation + "
Este evento te peroporciona la ubicación del aviso (usando la " + error.Message
clase XojoScriptLocation), el aviso (como un enumerando de End If
Warnings y warningInfo como Dictionary).
Call dialog.ShowModalWithin( Self )
Es importante gestionar este evento en el caso de que estés
permitiendo que el usuario proporcione guiones.
212
XojoScriptLocation Enumeraciones
Esta clase tiene propiedades para proporcionarte la información Utiliza las enumeraciones XojoScript.Errors y XojoScript.Warnings
sobre un error o aviso. Puedes utilizar esta información para para comprobar específicamente los errores o avisos.
permitir que el usuario sepa donde está el problema he incluso
para destacar el texto en el script. Conectar con tu Aplicación
Los guiones se ejecutan en una sandbox, lo que significa que no
Estas son las propiedades: pueden interactuar con ninguna parte del código de tu aplicación

• Character más allá de los eventos indicados.


El caracter inicial de la parte del script con un error o aviso.
Esto puede sonar demasiado restrictivo, de modo que tienes la
Utilízalo en combinación con EndCharacter para destacar el
opción de que parte del código de la aplicación esté expuesto
texto en el guión.
usando la propiedad Context.

• Column Context es un objeto que ya has instanciado. Tu guión puede


La columna inicial del error o advertencia en el script.
llamar a los métodos de este objeto como si fuesen métodos

• EndCharacter globales definidos en el script.


El caracter final de la parte del script con un error o aviso.
El modo clásico de hacerlo es a través de una clase especial que

• EndColumn sabe como conectar con tu aplicación. Por ejemplo, esta clase
La columna final del error o aviso en el script. podría tener un método Save que llame al código apropiado en tu
aplicación para guardar algo.
• EndLine
El número de la línea final correspondiente al error o aviso del Normalmente tu script no podría activar un guardado en tu
script. aplicación, pero usando el Context como paso intermedio, sí
puede.
• Line
El número de línea inicial correspondiente al error o aviso del Asignas el contexto de este modo:
script.

213
Si Precompile devuelve True, entonces puedes ejecutar el guión
Dim controller As New MyAppController usando el método Run.
Dim script As New XojoScript
Puedes comprobar el estado de un guión comparando la
script.Context = controller
propiedad State con la enumeración States con valores:
Aborted, Complete, Ready, Running.
A continuación en el script puedes llamar al método Save como
si fuese un método global:

Dim controller As New MyAppController


Dim script As New XojoScript
script.Context = controller
script.Source = "Save"
script.Run

Características avanzadas
Puedes optimizar el código antes de ejecutarlo llamando al
método Precompile y proporcionándole un valor de la
enumeración XojoScript.OptimizationLevels: High, Low, None. El
uso de niveles de optimización más elevados crea un guión que
se ejecuta más rápido, pero requerirá de más tiempo para su
compilación. Excepto que los guiones realicen cálculos
matemáticos intensos, no existe una gran necesidad de cambiar
el nivel de optimización.

214
Section 4

Declare y MemoryBlock

Declare
Se utiliza la instrucción declare para crear una referencia a un Dim dcTime As Integer
método en una librería compartida externa. Pueden ser librerías dcTime = GetDoubleClickTime
del sistema operativo o bien otras librerías. En Windows se
denominan DLLs (Dynamic Linked Libraries), en OS X se
Nota: Para crear una instrucción declare, necesitas acceso a la
denominan dylibs (Dynamic Libraries) y en Linux se denominan documentación de la API (Application Programming Interface)
como so (Shared Object). correspondiente a la librería compartida que quieres usar. La API define los
métodos disponibles y sus parámetros.

Para crear una declaración a un método externo, debes de Crear un declare a una librería compartida de Cocoa es un
utilizar la palabra clave Declare (generalmente con el prefijo Soft, proceso similar. Esto declara acceso al método Center capaz de
de modo que la búsqueda se lleve a cabo en tiempo de centrar una ventana de Cocoa en la pantalla:
ejecución), su tipo, nombre, librería y parámetros.

Por ejemplo, este código declara la función GetDoubleClickTime Declare Sub Center Lib "Cocoa" Selector
en la librería User32 de Windows: "center" (windowRef As Integer)

Declare Function GetDoubleClickTime Lib Nota: La palablra clave Selector solo se usa en OS X y únicamente para
identificar un método Cocoa.
"User32.DLL" () As Integer
Puedes usarla en el evento Open de una Window para centrarla
sobre la pantalla:
Puedes llamar a este método tal y como harías con cualquier
otro:

215
Center(Self.Handle) Dim s as String = "Hello!"
Dim mb as MemoryBlock
mb = s
MemoryBlock MsgBox(mb.StringValue(0, mb.Size))
Los MemoryBlocks se utilizan cuando necesitas un contenedor
para cualquier tipo de dato binario.
Para obtener más información sobre MemoryBlock, consulta la
Un objeto MemoryBlock reserva una secuencia de bytes en Referencia del Lenguaje.
memoria y manipula directamente dichos bits. Puede pasarse un
MemoryBlock en vez de un Ptr cuando se utilice en una llamada
Declare.

En los casos en los que necesitas gestionar directametne un


bloque de datos en su forma cruda (byte), necesitas utilizar un
MemoryBlock.

Cuando revises la API para crear instrucciones Declare,


encontrarás que algunos parámetros precisan de un puntero a
una porción de datos. Puedes configurar un bloque de memoria
como ubicación para dichos datos.

Un bloque de memoria puede ser inicializado con un valor String.


Los contenidos de byte de la cadena son asignados simplemente
como datos en cruto para el bloque de memoria.

216
Section 5

Estructuras

Una estructura es un tipo de valor compuesto. Consiste en una donde puedes indicar los campos de la estructura y sus
serie de campos agrupados como un único bloque. Puedes tamaños.
controlar el tamaño y orden de los campos. Una estructura
Usa el botón “+” para añadir un nuevo campo. Los campos se
puede suponer una alternativa adecuada a los MemoryBlock
añaden introduciendo su nombre seguido por el tipo así:
dado a que se suelen emplear con frecuencia para agrupar datos
con el objeto de realizar llamadas a funciones externas.
Name As Type
Los valores de las estructuras tienen tamaños específicos,
dando así el tamaño concreto de la propia estructura.
De modo que para introducir un Integer, sería:
Las estructuras se añaden a elementos del proyecto como los
módulos, clases y ventanas. Para añadir una estructura,
selecciona el botón Añadir en la barra de herramientas y Age As Integer
selecciona Estructura en el menú (o usa el menú Insertar en la
barra de menús). Esta acción mostrará el Editor de Estructuras
Observa que cuando introduces un campo, este muestra el
Figura 10.4 tamaño. Esta es la cantidad de bytes utilizada por el campo en la
estructura. Los enteros utilizan 4 bytes, por ejemplo.

Las cadenas son un caso especial dado que has de indicar el


tamaño exacto de las cadenas en bytes. A diferencia de las
cadenas normales, una cadena en una estructura siempre tiene
un tamaño restringido al indicado en la estructura y no puedes

217
superarlo. De igual modo, las cadenas de las estructuras no la longitud de la cadena). Para la gestión interna de datos en tu
incluyen información sobre la codificación. La sintaxis es: proyecto, deberías de utilizar siempre una clase en vez de una
estructura.

Name As String*size Sin embargo, son pequeñas y muy eficientes en el uso de la


memoria. Además de ser útiles en combinación con los métodos
externos, también pueden ser útiles en situaciones donde la
De modo que para tener una String con un tamaño de 50:
memoria ha de gestionarse con cuidado.

Name As String*50

Cuando estés creando una estructura para pasarla a un método


externo, asegúrate de que tus tamaños concuerden con los
tamaños indicados por la API para dicho método. Puedes hacer
referencia a una estructura utilizando la notación por punto:

Dim cust As CustomerStruct


cust.Name = "Bob Roberts"
cust.Age = 60

MsgBox(cust.Name)

Uso
Las estructuras son útiles para organizar los datos, pero no son
orientadas a objeto y están limitadas de muchas formas (como en

218
Section 6

Referencias Débiles y Gestión de Memoria

Una referencia débil te permite retener una referencia sobre un entonces tienes una fuga de memoria en potencia, dado que
objeto sin necesidad de aumentar su contador de referencia. ninguna de ellas podrá bajar a 0.

Contador de Referencia Las referencias débiles te permiten obtener una referencia a la


Cuando creas una nueva instancia de una clase, se aumenta el instancia de la clase sin cambiar el contador de referencias
contador de referencia interno para la clase. Cuando la clase interno.
sale de ámbito, se reduce la referencia. Cuando la referencia
alcanza el valor de 0, la instancia de la clase es eliminada de la Referencias débiles
Para obtener una referencia débil sobre un objeto has de utilizar
memoria.
la clase WeakRef:
Este tipo de gestión de memoria se denomina contador de
referencia. Es simple, rápido y fácil de comprender. Pero hay
Dim ref As WeakRef
ocasiones en las que no suficiente y puede derivar en fugas de
ref = New WeakRef(object)
memoria. Una fuga de memoria es cuando la memoria reservada
para una clase no llega a liberarse nunca. Esto causa que la
aplicación utilice más memoria continuadamente. Si la aplicación La propiedad Value de la WeakRef devuelve Nil si el objeto ya no
permanece funcionando durante mucho tiempo, podría quedarse está disponible. Si aun está disponible devuelve el objeto (del
sin memoria disponible que querrás hascer un cast al tipo correcto).

El motivo más común para que ocurra esto se debe a las Por ejemplo, este código declara una instancia para un
referencias circulares. Si la clase A tiene una referencia sobre la FolderItem que quedará fuera de ámbito y ve reducido su
clase B y la clase B tiene una referencia sobre la clase A, contador de referencia. Se asigna una referencia débil a la

219
instancia del FolderItem. Mientras que la instancia esté disponible
se mostrará su nombre. Cuando la instancia se elimine de la
memoria, la referencia débil será Nil, de modo que sabrás que ya
no mantiene más referencias sobre él:

Dim ref As WeakRef


If True Then
Dim f As New FolderItem
f.Name = "TestFile.txt"
ref = New WeakRef(f)
If ref.Value <> Nil Then
// This displays
MsgBox(FolderItem(ref.Value).Name)
Else
MsgBox("f is Nil.")
End If
End If

If ref.Value <> Nil Then


MsgBox(FolderItem(ref.Value).Name)
Else
MsgBox("f is Nil.") // This displays
End If

220
Section 7

Delegados

Un tipo de dato Delegado es un objeto que representa un Para usar un delegado debes de
método concreto. apuntarlo a la dirección de un Figura 10.6
método. Puedes hacerlo usando
Los delegados se encargan de desacoplar la interfaz de su
los comandos AddressOf o
implementación. Este desacoplamiento te permite tratar la
WeakAddressOf. Sólo pueden
implementación de un método como una variable que se puede
asignarse a los delegados
cambiar según las condiciones de la ejecución. Éstas
aquellos métodos que coincidan
representan métodos que pueden ser llamados sin conocer cuál
con los parámetros y tipo de
es el objeto de destino. Puedes cambiar la función a la que
retorno (signatura) del delegado.
apunta el delegado sobre la marcha.
Cuando un delegado contiene la
Puede declararse un delegado en
dirección de un método puedes llamar al método usando el
Figura 10.5 un módulo o una clase. Utiliza el
método Invoke del delegado.
comando de menú Insertar →
Delegado o el botón Añadir de la Para probar un ejemplo rápido, crea un delegado en una ventana
barra de herramientas en el Editor y llámalo MethodCaller.
de Código para crear una entrada
También en la ventana, crea dos métodos: TestMehod y
de Delegado, y que aparecerá bajo
AnotherMethod. Cada uno con este código:
el objeto contenedor.

Un delegado debe de tener un nombre y puede tener parámetros


MsgBox(CurrentMethodName)
adicionales y un tipo de retorno.

221
Dado que estos dos métodos tienen la misma signatura, es
posible asignarlos a un delegado y llamarlos usando el método
Invoke. Este código en el manejador de evento Action de un
Button llama a un método diferente en función del estado de un
CheckBox:

Dim callMethod As MethodCaller

If MethodCheck.Value Then
callMethod = AddressOf TestMethod
Else
callMethod = AddressOf AnotherMethod
End If

callMethod.Invoke

También puedes crear un delegado utilizando una función externa


que devuelva un apuntador. Dicho código sería como este:

Dim fp As Ptr = ExternalFunctionThatReturnsAPointer


Dim callMethod As New MethodCaller(fp)

222
Section 8

Introspección

La introspección es un modo por el que puedes obtener no sepa mucho sobre el código que lo está llamando, entonces
información sobre la estructura de tu aplicación en tiempo de la introspección comienza a resultar mucho más útil.
ejecución.
De hecho, la introspección es más útil cuando estás escribiendo
Por ejemplo, puedes usarla para obtener un listado de los código genérico diseñado para ser usado en una amplia
métodos de una instancia de clase, comprobar un método variedad de ubicaciones. Este tipo de código se denomina por lo
concreto y llamar a dicho método. Introspection es un módulo general código de framework.
que contiene varios métodos que pueden actuar sobre las
Algunos ejemplos donde la introspección puede ser útil:
instancias de clase. Por ejemplo, para obtener el tipo (nombre)
de una instancia de clase puedes escribir: • Una Librería de test unitario podría usar la introspección para
ejecutar métodos que terminen en “test”.

Dim f As New FolderItem • Un framework de base de datos podría usar la introspección


Dim oType As Introspection.TypeInfo para obtener el tipo de las propiedades sobre la clase, con
oType = Introspection.GetType(f) objeto de crear las columnas de la base de datos para ella.
MsgBox(oType.FullName)
• Una librería de serialización podría utilizar la introspección para
consultar a una clase de modo que pueda guardarse y
El anterior ejemplo no es realmente muy útil dado que recuperar su información.
probablemente ya sepas su tipo dado que lo has declarado en el
código. Pero si estás escribiendo un método más genérico que Un uso más sencillo pero interesante utiliza el manejador de
evento App.UnhandledException. Aquí generalmente querrás

223
mostrar solamente el tipo de excepción ocurrido. Podrías hacerlo
utilizando una larga instrucción Select Case para cada tipo de Dim excType As Introspection.TypeInfo
excepción. El problema de hacerlo así es que aumenta el tamaño excType = Introspection.GetType(error)

de tu aplicación debido a que todo el código relacionado para la


Dim excName As String
excepción se incluye en la aplicación, incluso si no lo necesitas.
excName = excType.FullName
También resulta en un código más complejo que requiere de tu
parte que recuerdes el nombre de cada excepción y actualizar el Dim stack As String
código cada vez que se añadan nuevas excepciones. stack = Join(error.Stack, EndOfLine)

En vez de ello puedes utilizar la introspección para obtener el tipo


Dim errMsg As String
del parámetro RuntimeException del manejador de evento
errMsg = excName + EndOfLine + EndOfLine + stack
UnhandledException. Este código muestra el tipo de error en
tiempo de ejecución seguido de su trazado de pila: MsgBox(errMsg)

Return True

224
Section 9

Atributos

Los atributos son propiedades en tiempo de compilación. Crear un Atributo


Pueden añadirse a los ítems de proyecto e ítems de código Puedes crear un atributo usando el Editor de Atributos en la
como los métodos y propiedades. Un atributo consiste de su pestaña avanzado del
nombre (Name) y valor (Value). El nombre es requerido, mientras Inspector para el ítem.
Figura 10.7
que el valor es opcional.
Usa los botones “+” o “-”
Los atributos se añaden utilizando la pestaña avanzado del para añadir o eliminar
Inspector. Pueden añadirse atributos a las clases, módulos, atributos. Indica el nombre
ventanas, contenedores, interfaces y barras de herramientas. y valor en la lista de
atributos.
Una subclase hereda los atributos de su súperclase. Estos
valores heredados pueden ser sobreescritos por la subclase con
Acceder a un
solo redefinirlos.
Atributo
Se accede a los atributos desde código mediante el uso de la
Introspección. La clase AttributeInfo se utiliza para obtener los
pares Name-Value de un objeto en concreto.

Este código obtiene los valores de atributo para la ventana por


defecto y los muestra en un ListBox:

225
vez de la actual (asegúrate de encerrar el nombre entre comillas).
Dim myAttributes() As Introspection.AttributeInfo =
Introspection.GetType(Window1).GetAttributes Un ítem deprecado aparece en el Panel de Errores cuando
utilizas Analizar Proyecto.
For i As Integer = 0 To UBound(myAttributes)
ListBox1.AddRow(myAttributes(i).Name)
If myAttributes(i).Value.IsNull Then Figura 10.9
ListBox1.Cell(ListBox1.LastIndex, 1) = "No Value"
Else
ListBox1.Cell(ListBox1.LastIndex, 1) =
Str(myAttributes(i).Value)
End If
Next

Usando Atributos Deprecados y Ocultos


Los atributos Deprecated y Hidden están reservados para El atributo Hidden oculta el elemento indicado para la
realizar acciones especiales cuando se utilizan sobre elementos introspección y el autocompletado. No has de indicar un valor.
de proyecto (como en una clase o módulo) o una propiedad de
Ambos atributos pueden
método (o constante, etc.) que puedas añadir a un elemento de
ser útiles en frameworks Figura 10.10
proyecto.
utilizados por otros
El atributo Deprecated desarrolladores.
Figura 10.8
te permite indicar que
un ítem “tiene un
remplazo”. Define el
valor del atributo al
nombre de la clase/
método/propiedad que
debería de utilizarse en

226
Section 10

Agregación de Interface

La agregación de interfaz es la capacidad de agrupar varias En este ejemplo hay tres interfaces que contienen un único
interfaces en una “súper interface”. método cada una. Si una clase fuese a implementar la interface
Test, debería de implementar sólo el método Foo. Sin embargo,
Considera estas interfaces:
si la clase fuese a implementar la interface Sweet, entonces
debería de implementar Foo, Bar y Blah. Esto es así dado que
Interface Test Sweet agrega las interfaces Test y Awesome.
Sub Foo()
Básicamente cuando una clase implementa una interface debe
End Interface
de implementar todos los métodos de la interface, así como los
métodos de las interfaces agregadas. Cuando una clase
Interface Awesome
implementa una interface, entonces una llamada a IsA sobre la
Sub Bar()
clase devolverá True para la interface así como para cualquier
End Interface
interfaz agregada. Del anterior ejemplo, una clase que
implemente Sweet, entonces IsA Sweet, IsA Test y IsA Awesome
Interface Sweet
serán todas True (dado que implementa las tres interfaces).
Aggregates Test, Awesome
Sub Blah() Esto te proporciona la habilidad de combinar interfaces de
End Interface formas creativas. Aunque las interfaces no se relacionen
necesariamente entre sí, existen ocasiones en las que necesitas
que satisfaga el comando IsA para múltiples interfaces
Sweet es un agregado de las interfaces Test y Awesome.
diferentes; o bien cuando quieras combinar la funcionalidad de
varias interfaces.

227
Por ejemplo, digamos que quieres tener un ítem que pueda ser Disposable no son lo mismo conceptualmente. Por tanto, no
iterado. Una interface común podría ser: tiene sentido forzar la combinación de ambas interfaces en dicho
modo. No hace daño, pero tampoco ayuda. El siguiente es un
ejemplo que sí podría no ser beneficioso.
Interface Iterator
Function Current() as Variant Digamos que tienes un método que va a tomar de forma genérica
Function MoveNext() as Boolean un parámetro que representa algo que puede leer o escribir
Sub Reset() desde o hacia una fuente. En este caso, quieres algo que sea
End Interface tanto Readable como Writeable. Podrías limitarte a declarar el
método tal que así:

Esto te permite iterar sobre el objeto de un modo genérico. Sin


embargo, también podría darse el caso de que la creación de Sub DoSomethingAwesome( foo as Readable )
este iterador causase referencias circulares, o bien problemas en If foo IsA Writeable Then
la gestión de la memoria, como este caso: // Do reading and writing
End If
End Sub
Interface Disposable
Sub Dispose()
End Interface Este código compilará y funcionará, pero no es seguro. El usuario
podría pasar algo que fuese Readable pero no Writeable y
compilaría. Sin embargo, el usuario podría hacer fácilmente lo
En el ejemplo, podrías hacer que la interface Iterator agregase la
siguiente:
interface Disposable. Esto garantizaría que cualquier cosa que
pudiese ser iterada también pudiese ser movido posteriormente.

Podrías preguntarte, “¿por qué no poner simplemente el método


Dispose en la interface Iterator?” Esto es un modo perfectamente
legítimo de obtener el mismo resultado, si bien Iterator y
228
Interface ReadableAndWriteable
Aggregates Readable, Writeable
End Interface

Sub DoSomethingAwesome( foo as ReadableAndWriteable )


End Sub

Ahora habrá un error de compilación si el ítem no implementa


ambas interfaces. Esta es una mejor solución al problema.

229