desarrollo de aplicaciones windows. aspectos avanzados (ejemplo)

55
Desarrollo de aplicaciones Windows. Aspectos avanzados Luis Miguel Blanco Ejemplo de lectura

Upload: grenlis

Post on 18-Feb-2015

169 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

Desarrollo de aplicacionesWindows. Aspectos avanzados

Luis Miguel Blanco

Ejem

plo d

e lec

tura

Page 2: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

Sipnosis

Texto dirigido a aquellas personas que quieran iniciarse en la creación de aplicaciones para entornoWindows, empleando Visual Studio 2005. Como lenguajes de programación se utilizan Visual Basic yC#.

Se trata de una obra organizada alrededor de tres bloques principales de contenidos. El primer bloqueabarca los capítulos versados en la creación de proyectos sencillos, utilizando Visual Studio 2005como herramienta de desarrollo. El segundo bloque se adentra en los dos lenguajes de programación,explicando aquellas características más importantes de cada uno. Finalmente, el tercer bloque seencuentra enfocado en el diseño e implementación de aplicaciones Windows, haciendo uso delconjunto de tipos ofrecidos por la plataforma .NET Framework al programador, así como delnumeroso grupo de diseñadores y asistentes del entorno de desarrollo.

A lo largo de todo el texto, el lector encontrará un gran número de ejemplos prácticos que lepermitirán aplicar de forma inmediata todos aquellos conceptos aprendidos en los diferentes capítulosde que se compone esta obra.

Luis Miguel Blanco trabaja como Arquitecto de Software enAlhambra Eidos, siendo uno de los titulados MCSD dereconocido prestigio entre el colectivo de desarrolladores.NET en España y Latinoamerica. Desde su posición en lacompañía ha partipado en numerosos proyectos de desarrollode software, tanto para la administración pública como para laempresa privada. En líneas generales, sus áreas deespecialización abarcan Visual Basic .NET, ASP.NET y SQLServer en sus aspectos relacionados con los servicios OLAP yReporting Services.

Mantiene un blog relacionado con diversos aspectos deldesarrollo en la plataforma .NET, tales como ASP.NET,AJAX, Silverlight, etc., en la dirección:

http://geeks.ms/blogs/lmblanco/.

Ejem

plo d

e lec

tura

Page 3: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

Luarna

Desarrollo de Aplicaciones Windows. Aspectos Avanzados© Luis Miguel Blanco Alhambra Eidos© De esta edición: 2009, Luarna Ediciones, S.L.

www.luarna.com

Madrid, septiembre de 2009

ISBN: 978-84-92684-52-6

Versión 1.0 (27-09-2009)

Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta obra solo puede ser realizada con laautorización de sus titulares, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos,www.cedro.org) si necesita fotocopiar, escanear o hacer copias digitales de algún fragmento de esta obra.

Ejem

plo d

e lec

tura

Page 4: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

A mi hijo David, un muchacho valiente a pesar de su juventud, cuya contagiosa alegría llena defelicidad mis días.

A mi esposa Olga, la mejor compañera que nadie podría desear para recorrer el viaje de la vida. Sucompañía hace que el camino sea más liviano y alegre.

A mi madre Antonia, por todos los ánimos que me ha dado siempre que me he embarcado en la dura,pero gratificante labor de escribir un libro.

A mi hermano Carlos, por su admirable espíritu libre e indómito.

A mi hermano Roberto, del que espero cumpla su sueño de viajar al país del sol naciente. Ha trabajadopara ello como pocos, y se lo merece más que muchos.

A mi padre Isaías, por transmitirme la afición al séptimo arte.

Ejem

plo d

e lec

tura

Page 5: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

DDEESSAARRRROOLLLLOO DDEE AAPPLLIICCAACCIIOONNEESS WWIINNDDOOWWSS.. AASSPPEECCTTOOSS AAVVAANNZZAADDOOSS

Luis Miguel Blanco

Ejem

plo d

e lec

tura

Page 6: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 6

Indice

Introducción .................................................................................................................................... 12Características avanzadas, pero en gran medida necesarias ............................................................ 12GDI+ y construcción de controles personalizados .......................................................................... 12ADO.NET y el enlace de datos a controles .................................................................................... 12Creación de informes .................................................................................................................... 13Flujos de datos y manipulación de archivos ................................................................................... 13Utilización del Portapapeles y de las operaciones de “Arrastrar y soltar” ....................................... 13Programación de procesos asíncronos y multihebra ....................................................................... 13Instalación de aplicaciones ............................................................................................................ 13

GDI+. Manipulación de gráficos en .NET Framework ................................................................. 14System.Drawing............................................................................................................................ 15La clase Graphics .......................................................................................................................... 15Especificando coordenadas. La clase Point .................................................................................... 15Definiendo tamaños. La clase Size ................................................................................................ 16Combinando coordenadas y tamaños. La clase Rectangle .............................................................. 16La clase Pen. Componiendo todo para dibujar ............................................................................... 17La estructura Color ....................................................................................................................... 18Repintado de áreas invalidadas ...................................................................................................... 18Dibujo de figuras varias con la clase Pen ....................................................................................... 19La clase Brush .............................................................................................................................. 23Creación de figuras complejas con la clase GraphicsPath .............................................................. 25Aplicación de efectos avanzados con la clase Brush ...................................................................... 27Objetos gráficos con características del sistema ............................................................................. 29Dibujo de texto en el formulario .................................................................................................... 30Personalización de la imagen de fondo del formulario ................................................................... 31

Manipulación de los eventos de pintado en la clase form ........................................................... 31El control picturebox ................................................................................................................. 32

Manipulando el grado de opacidad del formulario ......................................................................... 33Creación de formularios con formas irregulares ............................................................................. 37

Usando la clase region ............................................................................................................... 37Usando una imagen para definir la superficie del formulario ...................................................... 40

Dibujo manual de elementos de un control .................................................................................... 44La clase ToolStripRenderer.Aplicando GDI+ en la presentación de la barra de herramientas ......... 49Caso práctico. Creación y aplicación de un degradado de colores .................................................. 55

Creación de controles de usuario.................................................................................................... 56Crear una clase derivada de un control concreto ............................................................................ 56

Provocar por código un evento del control ................................................................................. 59Creación de controles de usuario. Heredando de UserControl ........................................................ 61

Diseño visual ............................................................................................................................ 62Probando el control en el formulario .......................................................................................... 63Agregando propiedades al control ............................................................................................. 65Codificación de los métodos ...................................................................................................... 67Redimensionamiento de los controles constituyentes ................................................................. 68Proporcionar información adicional al control mediante atributos .............................................. 70Asignación de una imagen para el cuadro de herramientas ......................................................... 73

Caso práctico. Desarrollo y utilización de un control de usuario .................................................... 75Ejem

plo d

e lec

tura

Page 7: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 7

Creación de controles personalizados ............................................................................................ 77Heredando de la clase Control ....................................................................................................... 77El control a crear ........................................................................................................................... 77Creación del proyecto ................................................................................................................... 78La clase del control personalizado y la clase Control, muchos aspectos en común.......................... 79El rombo para la casilla de marcado .............................................................................................. 80Dibujando el resto de elementos de la casilla ................................................................................. 82Visualizando el literal del título ..................................................................................................... 84Organizando las operaciones de dibujo en el código de la clase ..................................................... 86Controlando por código el marcado de la casilla ............................................................................ 89Refinando el marcado inicial de la casilla ...................................................................................... 91Marcando la casilla con el ratón .................................................................................................... 92La barra espaciadora también cambia el estado de la casilla ........................................................... 95Detectando el estado desde el código cliente ................................................................................. 95Indicación visual de la captura del foco ......................................................................................... 96Cambiando la alineación de la casilla ............................................................................................ 99Coloreando el rombo ................................................................................................................... 107

Creación de formularios derivados .............................................................................................. 110El selector de herencia de VS 2005 ............................................................................................. 110Desarrollo del formulario base .................................................................................................... 110Creación del formulario derivado mediante el selector de herencia .............................................. 112

Tratamiento de errores ................................................................................................................. 116Errores, ese mal común ............................................................................................................... 116

Errores de escritura ................................................................................................................. 116Errores de ejecución ................................................................................................................ 117Errores lógicos ........................................................................................................................ 118

Errores y excepciones ................................................................................................................. 118Manipuladores de excepciones .................................................................................................... 118Manipulación estructurada de errores .......................................................................................... 118

La estructura try...end try / try {…} ......................................................................................... 118La clase exception ................................................................................................................... 122Captura de excepciones de diferente tipo en el mismo controlador de errores ........................... 123Establecer en vb una condición para un manipulador de excepciones ....................................... 125La influencia del orden de los manipuladores de excepciones .................................................. 127Forzar la salida de un controlador de errores en vb mediante exit try........................................ 129

Manipulación no estructurada de errores en VB ........................................................................... 132El objeto err ............................................................................................................................ 133On error .................................................................................................................................. 133On error goto etiqueta ............................................................................................................. 133On error resume next ............................................................................................................... 134Creación de errores con el objeto err........................................................................................ 134On error goto 0 ........................................................................................................................ 135

Manipulación de datos con ADO.NET ......................................................................................... 136Aplicaciones de gestión de datos ................................................................................................. 136El acceso a datos desde ADO.NET .............................................................................................. 136Trabajo con datos en modo conectado ......................................................................................... 138Crear una copia de la base de datos ............................................................................................. 138Conexión con un origen de datos. La clase Connection ................................................................ 142Ejecutando sentencias. La clase Command .................................................................................. 144Recorriendo conjuntos de resultados. La clase DataReader .......................................................... 149Trabajo con datos en modo desconectado .................................................................................... 153Estructuras de datos en memoria. La clase DataSet y sus complementarias .................................. 154Ejem

plo d

e lec

tura

Page 8: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 8

Adaptadores de datos. La clase DataAdapter ............................................................................... 157

Creación de mantenimientos de datos .......................................................................................... 162Mantenimiento de datos en modo desconectado .......................................................................... 162Creación de un mantenimiento manual ........................................................................................ 163Agregar selección de valores desde tablas catálogo...................................................................... 172Mantenimiento con enlace automático de controles a datos. Data Binding ................................... 179Controlar errores de edición mediante un componente ErrorProvider ........................................... 185Creación manual del enlace entre controles y datos ..................................................................... 188Enlazar controles con campos de valores especiales .................................................................... 191

Búsquedas y vistas de datos .......................................................................................................... 194Proporcionar capacidades de búsqueda, filtro y ordenación de registros ....................................... 194Crear un sistema de búsqueda mediante el componente TableAdapter ......................................... 194

Invocar la consulta por código ................................................................................................. 196Obtener varios registros de la consulta ..................................................................................... 197Restaurar el conjunto de registros inicial ................................................................................. 197

Vistas de datos. La clase DataView ............................................................................................. 198Vista básica y predeterminada ................................................................................................. 199Creación de un filtro mediante una vista .................................................................................. 200Vistas simultáneas de la misma tabla ....................................................................................... 202Ordenación de registros ........................................................................................................... 203

Caso práctico. Realización de un mantenimiento de datos con Data Binding automático .............. 205

El control DataGridView. Diseño visual ...................................................................................... 206Creación y diseño visual ............................................................................................................. 206Agregar y eliminar columnas ...................................................................................................... 207Configuración de sólo consulta.................................................................................................... 209Cambiar las posiciones originales de las columnas ...................................................................... 210Establecer columnas fijas ............................................................................................................ 210Ordenar columnas ....................................................................................................................... 211Estilo de cabeceras de columna ................................................................................................... 211Estilo y propiedades de celdas ..................................................................................................... 212Estilo de cabeceras de fila ........................................................................................................... 214Estilo de selección de columna .................................................................................................... 214Visualización de filas con colores alternos................................................................................... 215Visualización de campos de tipo imagen ..................................................................................... 216

El control DataGridView. Manipulación por código .................................................................. 218Creación y manipulación por código ........................................................................................... 218Manejo básico de columnas y ordenación .................................................................................... 218Creación de estilos y aplicación de formatos ............................................................................... 221Pintado manual de celdas. El evento CellPainting, la claseDataGridViewCellPaintingEventArgs y la clase TextRenderer .................................................... 224

Dividiendo el texto de la celda en varias líneas ........................................................................ 228Edición de datos .......................................................................................................................... 231

Obtener información relativa a la fila y columna actual. El evento cellenter ............................. 233Borrando filas. El evento userdeletingrow ............................................................................... 234Manejo de errores de edición. El evento dataerror .................................................................... 235Validación de celdas. Los eventos cellvalidating y rowvalidating ............................................ 236

Tipos de columna del control DataGridView ............................................................................... 238DataGridViewCheckBoxColumn ............................................................................................ 239DataGridViewButtonColumn .................................................................................................. 240DataGridViewComboBoxColumn ........................................................................................... 244DataGridViewImageColumn ................................................................................................... 248DataGridViewLinkColumn ..................................................................................................... 251Ejem

plo d

e lec

tura

Page 9: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 9

Construcción de un formulario de consulta maestro/detalle utilizando controles DataGridView ... 254Caso práctico. Visualización de datos mediante el control DataGridView .................................... 258

Impresión y generación de informes ............................................................................................ 260El proceso de impresión en la plataforma .NET Framework ........................................................ 260La clase PrintDocument .............................................................................................................. 260El mecanismo de impresión ......................................................................................................... 261Impresión de una línea de texto ................................................................................................... 261Visualización previa de la impresión ........................................................................................... 263

Previsualizando mediante PrintPreviewDialog ......................................................................... 263Previsualizar manualmente con el control PrintPreviewControl ............................................... 264

Impresión de varias líneas de texto .............................................................................................. 266Configuración del trabajo de impresión ....................................................................................... 270Configuración de la página a imprimir ........................................................................................ 272Impresión de gráficos .................................................................................................................. 274

Impresión de gráficos a color................................................................................................... 276El control ReportViewer ............................................................................................................. 278Imprimir con Crystal Reports para Visual Studio .NET ............................................................... 280Espacios de nombres de Crystal Reports para Visual Studio 2005 ............................................... 281Creación de un informe con el asistente de Crystal Reports ......................................................... 281El diseñador de informes ............................................................................................................. 286El control CrystalReportViewer .................................................................................................. 288Establecer por código la información de conexión al origen de datos ........................................... 291Caso práctico. Impresión de un archivo de texto .......................................................................... 292

Flujos de datos .............................................................................................................................. 294El espacio de nombres System.IO ............................................................................................... 294Objetos Stream ........................................................................................................................... 294Las clases TextReader y TextWriter ............................................................................................ 295La clase StreamWriter ................................................................................................................. 295La clase StreamReader ................................................................................................................ 297Las clases StringWriter y StringReader ....................................................................................... 300La clase Stream ........................................................................................................................... 300La clase FileStream ..................................................................................................................... 301

Grabación y recuperación de documentos en una base de datos................................................ 303Manejo de datos binarios ............................................................................................................. 306Caso práctico. Lectura y escritura de archivos mediante flujos de datos ....................................... 307

Manipulación del sistema de archivos .......................................................................................... 308Manipulación de archivos mediante File y FileInfo ..................................................................... 308Manipulación de directorios mediante Directory y DirectoryInfo ................................................. 311La clase Path ............................................................................................................................... 315El control SplitContainer ............................................................................................................. 315El componente ImageList ............................................................................................................ 316El control TreeView .................................................................................................................... 317

Creación y diseño .................................................................................................................... 318Manipulación por código ......................................................................................................... 321Creación a partir de un origen de datos XML........................................................................... 328

El control ListView ..................................................................................................................... 331Creación y diseño .................................................................................................................... 332Manipulación por código ......................................................................................................... 337

Cuadros de diálogo para la gestión del sistema de archivos .......................................................... 343OpenFileDialog ....................................................................................................................... 344FolderBrowserDialog .............................................................................................................. 346SaveFileDialog........................................................................................................................ 347

El componente FileSystemWatcher. Monitorización del sistema de archivos ............................... 349Ejem

plo d

e lec

tura

Page 10: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 10

Detección con espera, de eventos producidos sobre archivos ................................................... 352Manipulación de archivos mediante funciones de VB .................................................................. 353Caso práctico. Creación de un formulario de navegación por tipo de archivo ............................... 354

Manejo del Portapapeles y procesos de “Arrastrar y soltar” ...................................................... 355El Portapapeles del sistema. Un medio efectivo para intercambiar información ........................... 355Situar información en el Portapapeles .......................................................................................... 355Obtener contenidos del Portapapeles ........................................................................................... 356Manipulando un objeto propio en el Portapapeles ........................................................................ 357Desplazamiento de información mediante “Arrastrar y soltar” ..................................................... 359

Creación de una operación de arrastrar y soltar sencilla ........................................................... 360Comienzo de la operación. Arrastrar un valor ...................................................................... 360Fin de la operación. Soltar el valor ....................................................................................... 361

Copiar y mover datos al arrastrar y soltar ................................................................................. 362Copiar archivos mediante un formulario explorador propio...................................................... 366

Caso práctico. Desarrollo de un proceso de arrastrar y soltar........................................................ 369

Programación de procesos asíncronos. Manipulación de hebras de ejecución ........................... 370El problema en la ejecución de tareas simultáneas ....................................................................... 370BackgroundWorker ..................................................................................................................... 371

Controlando el progreso de la ejecución en segundo plano ....................................................... 376Cancelando la ejecución de la tarea en segundo plano .............................................................. 379

Manejo de controles con soporte de funcionamiento asíncrono .................................................... 381Hebras de ejecución .................................................................................................................... 383La clase Thread ........................................................................................................................... 383Ejecución de procesos en hebras.................................................................................................. 384

La clase manipuladora de hebras ............................................................................................. 384Comunicación entre hebras y formulario ................................................................................. 390

Creación y ejecución de hebras desde el propio formulario .......................................................... 395Control de procesos indefinidos .................................................................................................. 397Ejecución multihebra .................................................................................................................. 400Ejecución multihebra de múltiples procesos ................................................................................ 402Creando un proceso de monitorización ........................................................................................ 412Inicios de aplicación con dos formularios empleando hebras ....................................................... 415Caso práctico. Ejecución de una tarea en segundo plano .............................................................. 417

Configuración de programas mediante ajustes de aplicación ..................................................... 418Configurando la aplicación desde el exterior ............................................................................... 418Creación de ajustes de aplicación ................................................................................................ 419

Creación de un ajuste de aplicación desde la ventana de propiedades del formulario ................ 419Creación de un ajuste de aplicación desde la ventana de propiedades del proyecto ................... 421

El contenido del archivo de configuración ................................................................................... 422Modificación de ajustes de aplicación ......................................................................................... 423Manteniendo los cambios realizados a los ajustes ........................................................................ 424Restaurando los valores originales de los ajustes ......................................................................... 424Enlace entre ajustes de aplicación y propiedades de objetos ......................................................... 425Caso práctico. Creación de ajustes de aplicación ......................................................................... 427

El control PropertyGrid ............................................................................................................... 428Edición visual de propiedades con PropertyGrid .......................................................................... 428

Cambiando la presentación del control PropertyGrid ............................................................... 436Caso práctico. Configuración de las propiedades de un objeto mediante el control PropertyGrid .. 438

Resolución Casos prácticos ........................................................................................................... 439Capítulo 2 ................................................................................................................................... 439Capítulo 3 ................................................................................................................................... 442Ejem

plo d

e lec

tura

Page 11: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 11

Capítulo 9 ................................................................................................................................... 450Capítulo 11 ................................................................................................................................. 456Capítulo 12 ................................................................................................................................. 460Capítulo 13 ................................................................................................................................. 464Capítulo 14 ................................................................................................................................. 466Capítulo 15 ................................................................................................................................. 470Capítulo 16 ................................................................................................................................. 472Capítulo 17 ................................................................................................................................. 477Capítulo 18 ................................................................................................................................. 480

Ejem

plo d

e lec

tura

Page 12: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 12

Introducción

Características avanzadas, pero en gran medida necesariasEn el presente texto realizaremos un recorrido por aquellos aspectos avanzados de la programacióncon formularios Windows, lo cual no significa que algunos de ellos no se encuentren entre lascaracterísticas que debemos implementar en la mayoría de las aplicaciones que habitualmentedesarrollamos. A grandes rasgos, los siguientes apartados describen las áreas del desarrollo queabordaremos en la actual obra.

GDI+ y construcción de controles personalizadosGDI+ representa la API de programación gráfica de la plataforma .NET, mediante la cual podremosdotar a nuestras aplicaciones de notables efectos visuales. También nos permitirá aplicar su potencialen la creación de controles de usuario derivados de los ya existentes, o bien en el desarrollo de nuevoscontroles creados a partir de cero, sin olvidar la herencia visual de formularios, un aspecto deldesarrollo que facilita la creación visual de formularios derivados.

ADO.NET y el enlace de datos a controlesEn el apartado de los mantenimientos de datos con ADO.NET, el enlace entre orígenes de datos ycontroles –Data Binding- incorpora nuevas clases y componentes, destinados a facilitar la labor delEj

emplo

de

lectu

ra

Page 13: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 13

programador en el tratamiento de la información. El nuevo control DataGridView, sucesor deDataGrid, ofrece un elevado número de características que le confieren una potencia en la presentacióny edición de datos muy superior a su antecesor.

Creación de informesLa gestión de los procesos de impresión y generación de informes también es abordada en esta obra,donde se tratan tanto las técnicas para la impresión manual de documentos –mediante los tiposdisponibles en .NET Framework-, como a través del control ReportViewer, que permite el diseño deinformes para SQL Server Reporting Services, y el también generador de informes Crystal Reports.

Flujos de datos y manipulación de archivosMediante el conjunto de clases de tipo Stream, manejaremos flujos de datos que nos permitirán lalectura y escritura de información en el sistema de archivos de la máquina, pudiendo desarrollarprocesos de monitorización en dicho sistema, que nos permitan rastrear los cambios a nivel dearchivos y directorios que se produzcan.

Utilización del Portapapeles y de las operaciones de“Arrastrar y soltar”La plataforma .NET ofrece soporte para implementar en nuestras aplicaciones dos de lascaracterísticas de mayor uso entre los usuarios de sistemas Windows, como son el Portapapeles y“Arrastrar y soltar”, que nos permiten realizar intercambio de información entre diferentesaplicaciones. Con ello, nuestros desarrollos ganarán en productividad y facilidad de manejo.

Programación de procesos asíncronos y multihebraCon el fin de poder ejecutar más de una tarea simultáneamente, disponemos de un conjunto de clases ycomponentes, que nos permitirán implementar en un proyecto capacidades de ejecución asíncrona, demanera que podemos lanzar la ejecución de un proceso en una hebra aparte, impidiendo que loscontroles del formulario de trabajo queden bloqueados debido a tareas intensivas que realicen un granconsumo de recursos del sistema.

Instalación de aplicacionesGracias a la tecnología ClickOnce, el despliegue e instalación de una aplicación se simplificaenormemente. Podemos configurar un proyecto de instalación desde variados orígenes: ruta de archivotradicional, sitio Web, servidor FTP, etc., lo cual facilita y flexibiliza las posibilidades de distribucióndel programa.

También es posible configurar el despliegue de una aplicación para que su ejecución se realice desdeun servidor centralizado, o sea descargada en las máquinas cliente del usuario, así como efectuaractualizaciones periódicas de dicha aplicación.Ej

emplo

de

lectu

ra

Page 14: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 14

GDI+. Manipulación de gráficos en.NET Framework

GDI+ (Graphics Device Interface) representa aquella parte en la arquitectura de la plataforma .NETencargada de la comunicación con el motor gráfico del sistema operativo. Se trata de una interfaz deprogramación independiente del dispositivo físico sobre el que se van a generar los gráficos; con ello,el programador gana en flexibilidad, ya que no debe preocuparse de si el gráfico generado se va amostrar por el monitor, impresora, etc., siendo esta una labor resuelta por GDI+, que aísla el programadel hardware a manejar.

Se trata de un subsistema gráfico que ha evolucionado del antiguo GDI, perteneciente a versionesanteriores de Windows, por lo que incluye una gran cantidad de mejoras y potencia en el tratamientode imágenes, texto, etc., con respecto a su predecesor.

GDI+ divide su campo de trabajo en tres áreas principales.

· Generación de gráficos vectoriales 2D

· Manipulación de imágenes en los formatos gráficos más habituales.

· Visualización de texto en un amplio abanico de tipos de letra.

Si somos programadores de VB, recordaremos que en las versiones de este lenguaje anteriores a .NET,el objeto Form disponía de una serie de métodos y controles para el dibujo sobre la superficie delformulario. La mayor parte de esos elementos han desaparecido en la versión de VB para .NET,integrándose todas las operaciones de dibujo en los diversos tipos de la plataforma .NET Frameworkrelacionados con el apartado gráfico; gracias a esta característica, lo que aprendamos trabajando congráficos en un lenguaje orientado a .NET utilizando GDI+, nos servirá igualmente si en un futuroEj

emplo

de

lectu

ra

Page 15: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 15

debemos abordar un proyecto en otro lenguaje de la plataforma, ya que las clases pertenecen a .NETFramework y no a un lenguaje en particular.

Otro problema con el que nos enfrentábamos anteriormente era el hecho de que al necesitar algunamanipulación gráfica especial, teníamos que recurrir al acceso directo a la API de Windows, con lascomplejidades que ello comportaba. A partir de ahora esto no será necesario, ya que como hemoscomentado, es el propio entorno de ejecución de .NET el que nos proporciona dicho acceso, por lo queno será necesario acudir a la API del sistema operativo.

System.DrawingSe trata del espacio de nombres contenedor de los tipos principales para el trabajo con gráficos enNET, por lo que deberemos declararlo al comienzo de nuestros archivos de código, o especificarlo alacceder a cualquiera de sus clases, estructuras, etc.

System.Drawing contiene el conjunto de tipos principales, aunque no es el único espacio de nombresde GDI+. Para tareas de mayor especialización con gráficos deberemos recurrir a otros espacios denombres que también están dentro de System.Drawing: Drawing2D (generación de gráficoscomplejos), Imaging (tratamiento de archivos de imagen), Text (manipulación de texto), Printing(impresión de gráficos), etc.

La clase GraphicsLa clase Graphics representa el denominado Contexto de dispositivo gráfico (Graphics devicecontext), o elemento (objeto) sobre el que se va a realizar una operación de dibujo, por ejemplo, elformulario.

Debido a la arquitectura del sistema gráfico, no es posible tomar un objeto Form y realizar unaoperación directa de dibujo sobre el mismo, sino que precisamos en primer lugar, obtener unareferencia hacia el área de dibujo de dicho formulario, o contexto gráfico, y una vez obtenida esareferencia, efectuar el dibujo.

Esta área la vamos a obtener mediante el método CreateGraphics() de la clase Form, que devuelve unobjeto Graphics con la información del contexto de dispositivo gráfico del formulario. Ver el Códigofuente 1.

Dim oGraphics As GraphicsoGraphics = Me.CreateGraphics()

Graphics oGraphics;oGraphics=this.CreateGraphics();

Código fuente 1

Obtenido el contexto gráfico sobre el que vamos a dibujar, ya podemos generar gráficos en elformulario utilizando alguno de los métodos de esta clase, como veremos posteriormente.

Especificando coordenadas. La clase PointEsta clase nos permite especificar puntos o coordenadas en las que se dibujarán los gráficos. Se tratade una clase auxiliar, que deberemos utilizar con diversos objetos de dibujo para indicar dóndedibujaremos un objeto gráfico.Ej

emplo

de

lectu

ra

Page 16: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 16

Point trabaja con valores enteros, pero si necesitamos ser más precisos al indicar las coordenadas dedibujo, disponemos también de la clase PointF, que nos permite establecer coordenadas con precisióndecimal. Ver el Código fuente 2.

Dim oPuntoUno As New Point(10, 150)

Dim oPuntoDos As PointFoPuntoDos = New PointF(45.6, 88.2)

Point oPuntoUno = new Point(10, 150);

PointF oPuntoDos;oPuntoDos = new PointF(45.6, 88.2);

Código fuente 2

Definiendo tamaños. La clase SizeAl igual que la clase anterior, Size es una clase auxiliar, que nos permite definir tamaños y queutilizaremos en diversas situaciones durante nuestro trabajo manipulando gráficos.

Podemos crear objetos Size con tamaños basados en valores enteros, o bien usar la clase SizeF paradefinir tamaños con valores de precisión decimal. Ver el Código fuente 3.

Dim oSize As New Size(50, 100)

Dim oSizeF As SizeFoSizeF = New SizeF(58.4, 125.6)

Size oSize = new Size(50,100);

SizeF oSizeF;oSizeF = new SizeF(58.4, 125.6);

Código fuente 3

Combinando coordenadas y tamaños. La clase RectanglePara dibujar un objeto gráfico debemos especificar un área rectangular, dentro de la cual será dibujadoel gráfico. Esto lo conseguiremos mediante la clase Rectangle o RectangleF, a cuyos constructorespasaremos valores numéricos (enteros o decimales), o bien sendos objetos Point y Size. Ver el Códigofuente 4.

Dim oRect As RectangleoRect = New Rectangle(New Point(10, 40), New Size(100, 120))

Dim oRectF As New RectangleF(10.5, 40.8, 100.6, 120.9)

Rectangle oRect;oRect = new Rectangle(new Point(10,40), new Size(100,120));

RectangleF oRectF = new RectangleF(10.5, 40.8, 100.6, 120.9);

Código fuente 4

Ejem

plo d

e lec

tura

Page 17: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 17

La clase Pen. Componiendo todo para dibujarLa clase Pen representa un objeto de tipo lapicero, que con un determinado color y grosor, utilizará losmétodos DrawTipoObjeto() de un objeto Graphics para dibujar líneas y formas sobre un contexto dedispositivo, es decir, el formulario.

El Código fuente 5 muestra un ejemplo de dibujo de un círculo sobre el formulario utilizando elmétodo DrawEllipse() de la clase Graphics. Este método recibe como parámetro un objeto Pen con uncolor y grosor determinados, y un objeto Rectangle con las coordenadas y medidas necesarias paradibujar el círculo. Finalizado el dibujo, y ya que las operaciones gráficas consumen gran cantidad derecursos, es recomendable llamar al método Dispose() de la clase Graphics, para poder liberarcorrectamente los recursos que esté utilizando.

' crear objeto PenDim oPen As New Pen(Color.DeepPink, 10)

' obtener el contexto de dispositivo' gráfico del formularioDim oGraphics As Graphics = Me.CreateGraphics()

' dibujar en el formulariooGraphics.DrawEllipse(oPen, New Rectangle(150, 20, 100, 100))oGraphics.Dispose()

// crear objeto PenPen oPen = new Pen(Color.DeepPink,10);

// obtener el contexto de dispositivo// gráfico del formularioGraphics oGraphics = this.CreateGraphics();

// dibujar en el formulariooGraphics.DrawEllipse(oPen, new Rectangle(150, 20, 100, 100));oGraphics.Dispose();

Código fuente 5

Asociando este código a la pulsación de un botón en el formulario, el resultado será el dibujo delcírculo mostrado en la Figura 1.

Figura 1. Dibujar un círculo con un objeto Pen.

Ejem

plo d

e lec

tura

Page 18: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 18

La estructura ColorEsta estructura nos permite seleccionar los colores existentes y definir colores personalizados. Su usomás simple consiste en escribir su nombre seguido del operador punto y el nombre del color a utilizar,tal y como acabamos de comprobar en el ejemplo previo.

También posible la creación de un color mediante su método FromArgb(), en el que definimos uncolor mediante un conjunto de valores numéricos que permiten especificar el grado de transparencia(Alpha) y los colores básicos rojo, verde y azul. El parámetro Alpha es opcional, no siendo necesariosu uso si no necesitamos usar el grado de transparencia.

Otros métodos disponibles para la creación de un color son FromName(), que recibe una cadena con elnombre del color a crear, y FromKnownColor(), al que pasamos un valor de la enumeraciónKnownColor, que contiene un conjunto de colores predefinidos. Veamos unos ejemplos en el Códigofuente 6.

Dim oColor As ColoroColor = Color.FromArgb(140, 200, 30, 60)oColor = Color.FromName("Yellow")oColor = Color.FromKnownColor(KnownColor.Firebrick)

Color oColor;oColor = Color.FromArgb(140, 200, 30, 60);oColor = Color.FromName("Yellow");oColor = Color.FromKnownColor(KnownColor.Firebrick);

Código fuente 6

Repintado de áreas invalidadasEl dibujo de figuras en la forma que acabamos de describir tiene un inconveniente: si la figura quehemos dibujado queda oculta parcial o totalmente por otro formulario o elemento de la interfaz deusuario, dicha zona ocultada será borrada.

Al área de un formulario que queda oculta de esta manera se le denomina zona invalidada, requiriendoser repintada para poder recuperar su aspecto original. Esta es una labor que actualmente norealizamos en el código del formulario.

Para mantener las figuras dibujadas en todo momento en el formulario, debemos recurrir al eventoPaint() de la clase Form. Dicho evento se produce cada vez que el formulario necesita repintarse,debido a que parte o la totalidad de su superficie ha quedado invalidada

Vamos por lo tanto a solucionar este problema, escribiendo un método manipulador para elmencionado evento Paint(), en el que dibujaremos un rectángulo mediante el métodoGraphics.DrawRectangle(). Como puede observar el lector, este evento recibe un parámetro de tipoPaintEventArgs, que utilizaremos para obtener el objeto Graphics con el que realizar el dibujo de lafigura. Ver el Código fuente 7.

Private Sub Form1_Paint(ByVal sender As Object, ByVal e AsSystem.Windows.Forms.PaintEventArgs) Handles MyBase.Paint ' crear objeto Pen Dim oPen As New Pen(Color.MediumVioletRed, 6)

' obtener el contexto de dispositivo ' gráfico del formulario; ' en este caso usaremos el objeto que contiene ' los argumentos de evento para obtener dicho ' contexto de dispositivoEj

emplo

de

lectu

ra

Page 19: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 19

Dim oGraph As Graphics = e.Graphics

' dibujar en el formulario oGraph.DrawRectangle(oPen, New Rectangle(40, 80, 70, 70)) oGraph.Dispose()End Sub

private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e){

// crear objeto PenPen oPen = new Pen(Color.MediumVioletRed, 6);

// obtener el contexto de dispositivo// gráfico del formulario;// en este caso usaremos el objeto que contiene// los argumentos de evento para obtener dicho// contexto de dispositivoGraphics oGraph = e.Graphics;

// dibujar en el formulariooGraph.DrawRectangle(oPen, new Rectangle(40, 80, 70, 70));

}

Código fuente 7

Al ejecutar el programa, el rectángulo será mostrado en todo momento, aunque minimicemos elformulario, lo ocultemos parcial o totalmente, etc., ya que este evento se ejecuta automáticamente cadavez que el formulario detecta que su superficie ha quedado invalidada y necesita repintarse. Ver laFigura 2.

Figura 2. Figura persistente en la superficie del formulario gracias al evento Paint.

Dibujo de figuras varias con la clase PenEn este apartado veremos diversos métodos que también ofrece la clase Pen para el dibujo de otrostipos de figuras. Para organizar el código, añadiremos un menú al formulario, creando una opción demenú por cada operación de dibujo.

En primer lugar volveremos a utilizar el método DrawEllipse() para dibujar una figura de este tipo conforma alargada. Ver el Código fuente 8.

Private Sub mnuElipse_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuElipse.Click ' crear objeto Pen Dim oPen As New Pen(Color.DarkTurquoise, 2)Ej

emplo

de

lectu

ra

Page 20: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 20

' obtener el contexto de dispositivo ' gráfico del formulario Dim oGraphics As Graphics = Me.CreateGraphics()

' dibujar en el formulario oGraphics.DrawEllipse(oPen, New Rectangle(250, 150, 65, 120)) oGraphics.Dispose()End Sub

private void mnuElipse_Click(object sender, EventArgs e){ // crear objeto Pen Pen oPen = new Pen(Color.DarkTurquoise, 2);

// obtener el contexto de dispositivo // gráfico del formulario Graphics oGraphics = this.CreateGraphics();

// dibujar en el formulario oGraphics.DrawEllipse(oPen, new Rectangle(250, 150, 65, 120)); oGraphics.Dispose();}

Código fuente 8

Al dibujar un rectángulo vamos a modificar el estilo de línea mediante la propiedad DashStyle. Estapropiedad contiene una enumeración con la que podemos hacer que la línea se muestre como guiones,guiones y puntos, etc. Ver el Código fuente 9.

Private Sub mnuRectangulo_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuRectangulo.Click ' crear objeto Pen Dim oPen As New Pen(Color.Firebrick, 4)

' aplicar un estilo de línea con la propiedad ' DashStyle --> guión, punto oPen.DashStyle = Drawing.Drawing2D.DashStyle.DashDot

' obtener el contexto de dispositivo ' gráfico del formulario Dim oGraphics As Graphics = Me.CreateGraphics()

' dibujar en el formulario oGraphics.DrawRectangle(oPen, New Rectangle(280, 75, 120, 40)) oGraphics.Dispose()End Sub

private void mnuRectangulo_Click(object sender, EventArgs e){ // crear objeto Pen Pen oPen = new Pen(Color.Firebrick, 4);

// aplicar un estilo de línea con la propiedad // DashStyle --> guión, punto oPen.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot;

// obtener el contexto de dispositivo // gráfico del formulario Graphics oGraphics = this.CreateGraphics();

// dibujar en el formulario oGraphics.DrawRectangle(oPen, new Rectangle(280, 75, 120, 40)); oGraphics.Dispose();}

Código fuente 9Ejem

plo d

e lec

tura

Page 21: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 21

Si queremos aplicar más estilos a la línea del objeto Pen, disponemos también de las propiedadesStartCap, EndCap, DashCap. El Código fuente 10 muestra el dibujo de una curva con varios efectos delínea. Al dibujar una curva, necesitamos pasar al método DrawCurve() un array de tipos Point, con lascoordenadas de referencia a usar para el dibujo de este tipo de figura.

Private Sub mnuCurva_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuCurva.Click ' crear objeto Pen Dim oPen As New Pen(Color.MediumPurple, 5)

' configurar estilo de línea oPen.DashStyle = Drawing.Drawing2D.DashStyle.DashDot oPen.StartCap = Drawing.Drawing2D.LineCap.Triangle oPen.EndCap = Drawing.Drawing2D.LineCap.DiamondAnchor oPen.DashCap = Drawing.Drawing2D.DashCap.Triangle

' obtener el contexto de dispositivo ' gráfico del formulario Dim oGraphics As Graphics = Me.CreateGraphics()

' crear un array de puntos-coordenadas necesario ' para dibujar una curva Dim oPuntos(4) As Point oPuntos(0) = New Point(10, 300) oPuntos(1) = New Point(40, 200) oPuntos(2) = New Point(100, 175) oPuntos(3) = New Point(130, 200) oPuntos(4) = New Point(200, 300)

' dibujar en el formulario oGraphics.DrawCurve(oPen, oPuntos) oGraphics.Dispose()End Sub

private void mnuCurva_Click(object sender, EventArgs e){ // crear objeto Pen Pen oPen = new Pen(Color.MediumPurple, 5);

// configurar estilo de línea oPen.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot; oPen.StartCap = System.Drawing.Drawing2D.LineCap.Triangle; oPen.EndCap = System.Drawing.Drawing2D.LineCap.DiamondAnchor; oPen.DashCap = System.Drawing.Drawing2D.DashCap.Triangle;

// obtener el contexto de dispositivo // gráfico del formulario Graphics oGraphics = this.CreateGraphics();

// crear un array de puntos-coordenadas necesario // para dibujar una curva Point[] oPuntos = new Point[5]; oPuntos[0] = new Point(10, 300); oPuntos[1] = new Point(40, 200); oPuntos[2] = new Point(100, 175); oPuntos[3] = new Point(130, 200); oPuntos[4] = new Point(200, 300);

// dibujar en el formulario oGraphics.DrawCurve(oPen, oPuntos); oGraphics.Dispose();}

Código fuente 10

En cuanto a las curvas de tipo Bezier, el método DrawBezier() recibe como parámetros un objeto Peny una lista de coordenadas para el dibujo. Ver el Código fuente 11.Ej

emplo

de

lectu

ra

Page 22: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 22

Private Sub mnuBezier_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuBezier.Click ' dibujar curva estilo Bezier Dim oGraphics As Graphics = Me.CreateGraphics() oGraphics.DrawBezier(New Pen(Color.MediumSeaGreen, 2), 20, 45, 100, 90, 140,200, 300, 25) oGraphics.Dispose()End Sub

private void mnuBezier_Click(object sender, EventArgs e){ // dibujar curva estilo Bezier Graphics oGraphics = this.CreateGraphics(); oGraphics.DrawBezier(new Pen(Color.MediumSeaGreen, 2), 20, 45, 100, 90, 140,200, 300, 25); oGraphics.Dispose();}

Código fuente 11

Para dibujar una figura con forma irregular utilizaremos el método DrawPolygon(), en el que al igualque con el dibujo de curvas, debemos pasarle un objeto Pen y un array de puntos. Ver el Código fuente12.

Private Sub mnuPoligono_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuPoligono.Click Dim oGraphics As Graphics = Me.CreateGraphics()

Dim oPuntos(5) As Point oPuntos(0) = New Point(30, 70) oPuntos(1) = New Point(90, 115) oPuntos(2) = New Point(220, 90) oPuntos(3) = New Point(120, 80) oPuntos(4) = New Point(100, 100) oPuntos(5) = New Point(110, 65)

oGraphics.DrawPolygon(New Pen(Color.SandyBrown, 3), oPuntos) oGraphics.Dispose()End Sub

private void mnuPoligono_Click(object sender, EventArgs e){ Graphics oGraphics = this.CreateGraphics();

Point[] oPuntos = new Point[6]; oPuntos[0] = new Point(30, 70); oPuntos[1] = new Point(90, 115); oPuntos[2] = new Point(220, 90); oPuntos[3] = new Point(120, 80); oPuntos[4] = new Point(100, 100); oPuntos[5] = new Point(110, 65);

oGraphics.DrawPolygon(new Pen(Color.SandyBrown, 3), oPuntos); oGraphics.Dispose();}

Código fuente 12

La Figura 3 muestra el resultado de la ejecución de los anteriores ejemplos de código.

Ejem

plo d

e lec

tura

Page 23: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 23

Figura 3. Figuras dibujadas con objetos de la clase Pen.

Si en un momento dado, necesitamos borrar los elementos gráficos dibujados en la superficie delformulario, utilizaremos el método Invalidate() de la clase Form, tal como vemos en el Código fuente13.

Private Sub mnuBorrar_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuBorrar.Click ' borrar las figuras dibujadas en el formulario Me.Invalidate()End Sub

private void mnuBorrar_Click(object sender, System.EventArgs e){ // borrar las figuras dibujadas en el formulario this.Invalidate();}

Código fuente 13

La clase BrushEsta clase representa un objeto de tipo pincel, utilizado para rellenar las figuras dibujadas sobre elformulario.

Se trata de una clase abstracta, por lo que tendremos que utilizar alguna de sus diversas clasesderivadas, según el estilo de pincel que necesitemos aplicar. Debido a las características 2D de algunasde estas clases, será necesario declarar en nuestro código el espacio de nombres Drawing2D.

Los métodos de la clase Graphics que utilizaremos para dibujar con pinceles serán los que comienzanpor el nombre FillTipoObjeto().

La clase más básica es SolidBrush, que permite rellenar en un estilo sencillo un área dibujada. Ver elCódigo fuente 14.

Private Sub mnuBrushSolid_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuBrushSolid.Click Dim oBrush As New SolidBrush(Color.MidnightBlue) Dim oGraphics As Graphics = Me.CreateGraphics()Ej

emplo

de

lectu

ra

Page 24: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 24

oGraphics.FillRectangle(oBrush, New Rectangle(150, 160, 150, 50)) oGraphics.Dispose()End Sub

private void mnuBrushSolid_Click(object sender, EventArgs e){ SolidBrush oBrush = new SolidBrush(Color.MidnightBlue); Graphics oGraphics = this.CreateGraphics(); oGraphics.FillRectangle(oBrush, new Rectangle(150, 160, 150, 50)); oGraphics.Dispose();}

Código fuente 14

A continuación tenemos la clase HatchBrush, que permite la creación de pinceles que al pintar aplicanun efecto de tramado, con un color de fondo y otro de frente. Ver el Código fuente 15.

Imports System.Drawing.Drawing2D'....Private Sub mnuBrushHatch_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuBrushHatch.Click ' pintar con pincel de tipo hatch; ' para utilizar este tipo de pincel ' necesitamos importar System.Drawing.Drawind2D

' crear objeto HatchBrush Dim oHatchBrush As New HatchBrush(HatchStyle.Vertical, Color.Fuchsia,Color.Aqua)

' dibujar y pintar un polígono Dim oGraphics As Graphics = Me.CreateGraphics() oGraphics.FillEllipse(oHatchBrush, New Rectangle(25, 40, 150, 50)) oGraphics.Dispose()End Sub

using System.Drawing.Drawing2D;//....private void mnuBrushHatch_Click(object sender, EventArgs e){

// pintar con pincel de tipo hatch;// para utilizar este tipo de pincel// necesitamos usar System.Drawing.Drawind2D

// crear objeto HatchBrushHatchBrush oHatchBrush = new HatchBrush(HatchStyle.Vertical, Color.Fuchsia,

Color.Aqua);

// dibujar y pintar un polígonoGraphics oGraphics = this.CreateGraphics();oGraphics.FillEllipse(oHatchBrush, new Rectangle(25, 40, 150, 50));oGraphics.Dispose();

}

Código fuente 15

Podemos emplear un bitmap como base para la zona de relleno que tendrá que pintarse, para ellousaremos la clase TextureBrush, pasándole como parámetro un objeto Bitmap, que previamentehabremos creado a partir de un archivo gráfico que contenga la textura necesaria. Ver el Código fuente16.

Private Sub mnuBrushTexture_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuBrushTexture.Click ' crear un bitmap Dim oBitmap As New Bitmap("textura1.bmp")

' crear una brocha de textura Dim oTextureBrush As New TextureBrush(oBitmap)Ej

emplo

de

lectu

ra

Page 25: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 25

' dibujar una figura y pintarla con la brocha de textura Dim oGraphics As Graphics = Me.CreateGraphics() oGraphics.FillEllipse(oTextureBrush, New Rectangle(220, 50, 100, 75)) oGraphics.Dispose()End Sub

private void mnuBrushTexture_Click(object sender, EventArgs e){ // crear un bitmap Bitmap oBitmap = new Bitmap("textura1.bmp");

// crear una brocha de textura TextureBrush oTextureBrush = new TextureBrush(oBitmap);

// dibujar una figura y pintarla con la brocha de textura Graphics oGraphics = this.CreateGraphics(); oGraphics.FillEllipse(oTextureBrush, new Rectangle(220, 50, 100, 75)); oGraphics.Dispose();}

Código fuente 16

La Figura 4 muestra estas figuras con el relleno correspondiente.

Figura 4. Figuras con efecto de relleno.

Creación de figuras complejas con la clase GraphicsPathLa clase GraphicsPath nos permite la generación de dibujos más complejos que los conseguidos hastaahora con la clase Graphics.

Un objeto GraphicsPath consiste en un contenedor de figuras, las cuales iremos añadiendo mediantelos métodos AddTipoObjeto() de esta clase, hasta conseguir el gráfico final resultante. Ver el Códigofuente 17.

Private Sub mnuGPathAbierta_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuGPathAbierta.Click ' crear el objeto GraphicsPath Dim oGraphPath As New GraphicsPath

' añadir figuras dentro del objeto oGraphPath.AddEllipse(40, 40, 100, 100) oGraphPath.AddLine(New Point(50, 70), New Point(115, 70)) oGraphPath.AddCurve(New Point() {New Point(60, 90), New Point(70, 125), NewPoint(100, 100)})Ej

emplo

de

lectu

ra

Page 26: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 26

' dibujar el GraphicsPath en el formulario Dim oGraph As Graphics = Me.CreateGraphics oGraph.DrawPath(New Pen(Color.DarkTurquoise, 8), oGraphPath) oGraph.Dispose()End Sub

private void mnuGPathAbierta_Click(object sender, EventArgs e){ // crear el objeto GraphicsPath GraphicsPath oGraphPath = new GraphicsPath();

// añadir figuras dentro del objeto oGraphPath.AddEllipse(40,40 , 100, 100); oGraphPath.AddLine(new Point(50,70), new Point(115,70)); oGraphPath.AddCurve(new Point[] {new Point(60,90), new Point(70, 125), new Point(100, 100)});

// dibujar el GraphicsPath en el formulario Graphics oGraph = this.CreateGraphics(); oGraph.DrawPath(new Pen(Color.DarkTurquoise, 8), oGraphPath); oGraph.Dispose();}

Código fuente 17

La Figura 5 muestra el resultado de una figura creada con GraphicsPath.

Figura 5. Imagen generada con un objeto GraphicsPath.

Si queremos que las figuras añadidas al objeto GraphicsPath, queden unidas desde el punto inicial dela primera figura hasta el punto final de la última, llamaremos al método StartFigure() antes deempezar a añadir la primera figura, y a CloseFigure() después de añadir la última. Ver el Códigofuente 18.

Private Sub mnuGPathCerrada_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuGPathCerrada.Click ' crear el objeto GraphicsPath Dim oGraphPath As New GraphicsPath

' comenzar a añadir figuras dentro del objeto oGraphPath.StartFigure() oGraphPath.AddEllipse(40, 40, 120, 100) oGraphPath.AddLine(New Point(30, 30), New Point(90, 30)) oGraphPath.AddCurve(New Point() {New Point(40, 40), New Point(70, 110), NewPoint(100, 60)}) ' cerrar el objeto oGraphPath.CloseFigure()

' dibujar el GraphicsPath en el formulario Dim oGraph As Graphics = Me.CreateGraphics oGraph.DrawPath(New Pen(Color.LightSteelBlue, 5), oGraphPath) oGraph.Dispose()

End SubEjem

plo d

e lec

tura

Page 27: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 27

private void mnuGPathCerrada_Click(object sender, System.EventArgs e){

// crear el objeto GraphicsPathGraphicsPath oGraphPath = new GraphicsPath();

// comenzar a añadir figuras dentro del objetooGraphPath.StartFigure();oGraphPath.AddEllipse(40, 40, 120, 100);oGraphPath.AddLine(new Point(30, 30), new Point(90, 30));oGraphPath.AddCurve(new Point[] {new Point(40, 40),

new Point(70, 110), new Point(100, 60)});// cerrar el objetooGraphPath.CloseFigure();

// dibujar el GraphicsPath en el formularioGraphics oGraph = this.CreateGraphics();oGraph.DrawPath(new Pen(Color.LightSteelBlue, 5), oGraphPath);oGraph.Dispose();

}

Código fuente 18

La Figura 6 muestra un conjunto de imágenes unidas por todos sus puntos, creadas con GraphicsPath.

Figura 6. Imágenes unidas creadas con un objeto GraphicsPath.

Aplicación de efectos avanzados con la clase BrushPara efectos avanzados de relleno, consistentes en degradados de colores, utilizaremos las clasesLinearGradientBrush y PathGradientBrush. Una vez instanciado un objeto de estas clases, aplicaremosun conjunto de colores, que serán mezclados para crear un efecto de degradado o fundido en el área apintar, utilizando el constructor y/o las propiedades de la clase que corresponda. Ver el Código fuente19.

Private Sub mnuBrushLinearGradient_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuBrushLinearGradient.Click ' crear pincel de tipo LinearGradient Dim oLGB As New LinearGradientBrush(New Rectangle(10, 50, 40, 60),Color.Aquamarine, Color.Azure, LinearGradientMode.Horizontal)

' crear array de coordenadas Dim oPuntos(2) As Point oPuntos(0) = New Point(20, 200) oPuntos(1) = New Point(75, 100) oPuntos(2) = New Point(140, 220)

' obtener contexto gráfico Dim oGraphics As Graphics = Me.CreateGraphics()

' dibujar y pintar una curva cerradaEjem

plo d

e lec

tura

Page 28: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 28

oGraphics.FillClosedCurve(oLGB, oPuntos) oGraphics.Dispose()End Sub'------------------------------------Private Sub mnuBrushPathGradient_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuBrushPathGradient.Click ' array de coordenadas Dim oPuntos(2) As Point oPuntos(0) = New Point(150, 150) oPuntos(1) = New Point(225, 80) oPuntos(2) = New Point(260, 150)

' crear pincel de tipo PathGradient, ' y configurar el objeto Dim oPGB As New PathGradientBrush(oPuntos) oPGB.CenterColor = Color.Indigo oPGB.SurroundColors = New Color() {Color.Beige, Color.LightGreen}

' crear gráfico y pintar polígono Dim oGraphics As Graphics = Me.CreateGraphics() oGraphics.FillPolygon(oPGB, oPuntos) oGraphics.Dispose()End Sub

private void mnuBrushLinearGradient_Click(object sender, EventArgs e){ // crear brocha de tipo LinearGradient LinearGradientBrush oLGB = new LinearGradientBrush(new Rectangle(10, 50, 40,60), Color.Aquamarine, Color.Azure, LinearGradientMode.Horizontal);

// crear array de coordenadas Point[] oPuntos = new Point[3]; oPuntos[0] = new Point(20, 200); oPuntos[1] = new Point(75, 100); oPuntos[2] = new Point(140, 220);

// obtener contexto gráfico Graphics oGraphics = this.CreateGraphics();

// dibujar y pintar una curva cerrada oGraphics.FillClosedCurve(oLGB, oPuntos); oGraphics.Dispose();}//------------------------------------private void mnuBrushPathGradient_Click(object sender, EventArgs e){ // array de coordenadas Point[] oPuntos = new Point[3]; oPuntos[0] = new Point(150, 150); oPuntos[1] = new Point(225, 80); oPuntos[2] = new Point(260, 150);

// crear brocha de tipo PathGradient, // y configurar el objeto PathGradientBrush oPGB = new PathGradientBrush(oPuntos); oPGB.CenterColor = Color.Indigo; oPGB.SurroundColors = new Color[] { Color.Beige, Color.LightGreen };

// crear gráfico y pintar polígono Graphics oGraphics = this.CreateGraphics(); oGraphics.FillPolygon(oPGB, oPuntos); oGraphics.Dispose();}

Código fuente 19

La Figura 7 muestra alguna de las formas dibujadas utilizando objetos con efecto de gradienteEjem

plo d

e lec

tura

Page 29: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 29

Figura 7. Figuras con efecto de gradiente.

Objetos gráficos con características del sistemaLas clases SystemPens, SystemBrushes y SystemColors, nos permiten crear y manipular elementosgráficos con la particularidad de que algunas de sus características serán iguales a las definidas para elsistema operativo.

El Código fuente 20 muestra unos ejemplos en los que creamos un objeto Pen del sistema, y un objetoBrush que tiene un color del sistema.

Private Sub mnuPenSistema_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuPenSistema.Click ' crear objeto Pen del sistema Dim oPen As Pen oPen = SystemPens.ControlDark

' obtener el contexto de dispositivo ' gráfico del formulario Dim oGraphics As Graphics = Me.CreateGraphics()

' dibujar en el formulario oGraphics.DrawEllipse(oPen, New Rectangle(150, 120, 100, 100)) oGraphics.Dispose()

End Sub'------------------------------------Private Sub mnuBrushSistema_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuBrushSistema.Click

' crear un objeto brush con un color de sistema Dim oBrush As New SolidBrush(SystemColors.ControlDark) Dim oGraphics As Graphics = Me.CreateGraphics() oGraphics.FillEllipse(oBrush, New Rectangle(200, 200, 150, 50)) oGraphics.Dispose()

End Sub

private void mnuPenSistema_Click(object sender, System.EventArgs e){

// crear objeto Pen del sistemaPen oPen;oPen = SystemPens.ControlDark;

// obtener el contexto de dispositivoEjem

plo d

e lec

tura

Page 30: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 30

// gráfico del formularioGraphics oGraphics = this.CreateGraphics();

// dibujar en el formulariooGraphics.DrawEllipse(oPen, new Rectangle(150, 120, 100, 100));oGraphics.Dispose();

}//----------------------private void mnuBrushSistema_Click(object sender, System.EventArgs e){

// crear un objeto brush con un color de sistemaSolidBrush oBrush = new SolidBrush(SystemColors.ControlDark);Graphics oGraphics = this.CreateGraphics();oGraphics.FillEllipse(oBrush, new Rectangle(200, 200, 150, 50));oGraphics.Dispose();

}

Código fuente 20

Dibujo de texto en el formularioAparte de los controles que nos permiten visualizar y editar texto, como Label, TextBox, etc.,podemos realizar operaciones de dibujo de texto en la superficie del formulario, empleando el métodoDrawString() de la clase Graphics.El texto visualizado mediante esta técnica no es, evidentemente, editable, sino que se encaminafundamentalmente a ser mostrado con efectos adicionales, que no podríamos conseguir mediante loscontroles típicos.En el Código fuente 21 creamos un objeto HatchBrush con un tramado específico, un objeto Font deuna determinada familia, y con ambos elementos, pintamos el texto mediante un objeto Graphics.

Private Sub mnuTextoGrafico_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuTextoGrafico.Click ' crear un objeto Brush con efectos Dim oBrush As New HatchBrush(HatchStyle.Wave, Color.Aqua, Color.DarkGreen)

' crear el tipo de letra Dim oFont As New Font(New FontFamily("Georgia"), 50)

' obtener el dispositivo gráfico del formulario ' y pintar el texto Dim oGraf As Graphics = Me.CreateGraphics() oGraf.DrawString("Texto en modo gráfico", _ oFont, oBrush, New RectangleF(20, 20, 500, 200))End Sub

private void mnuTextoGrafico_Click(object sender, EventArgs e){ // crear un objeto Brush con efectos HatchBrush oBrush = new HatchBrush(HatchStyle.Wave, Color.Aqua,Color.DarkGreen);

// crear el tipo de letra Font oFont = new Font(new FontFamily("Georgia"), 50);

// obtener el dispositivo gráfico del formulario // y pintar el texto Graphics oGraf = this.CreateGraphics(); oGraf.DrawString("Texto en modo gráfico", oFont, oBrush, new RectangleF(20, 20, 500, 200)); oGraf.Dispose();}

Código fuente 21Ejem

plo d

e lec

tura

Page 31: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 31

La Figura 8 muestra el texto dibujado en el formulario.

Figura 8. Dibujo de texto empleando las clases para manipulación de gráficos.

Personalización de la imagen de fondo del formularioPara establecer una imagen de fondo en el formulario disponemos de la propiedad BackgroundImage,aunque su facilidad de uso tiene desafortunadamente un lado negativo, ya que el tamaño de la imagenasignada, no se adapta automáticamente al del formulario.

Por tal motivo, en este apartado vamos a proporcionar al lector un par de técnicas, que le permitiráncrear imágenes de fondo para formularios con ajuste dinámico, de forma que la imagen adapte susdimensiones a las del formulario, cuando este cambie su tamaño en tiempo de ejecución.

Manipulación de los eventos de pintado en la clase formEl primer ejemplo se basa en la codificación del evento Paint() de la clase Form, dentro de cuyométodo manipulador creamos un objeto Bitmap, que contenga la ruta hacia un archivo con el gráfico amostrar de fondo. A continuación, mediante el objeto Graphics obtenido del parámetro EventArgs delevento, pintamos el objeto Bitmap. Ver el Código fuente 22.

Private Sub Form1_Paint(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.PaintEventArgs) Handles MyBase.Paint ' crear un objeto bitmap Dim oBitmap As New Bitmap("LogoWinXP.gif")

' pintar el objeto con el contexto ' gráfico del formulario; ' el area a pintar abarca desde la ' coordenada 0,0 y toma el ancho y alto ' del formulario de su propiedad Size Dim oGraphics As Graphics = e.Graphics oGraphics.DrawImage(oBitmap, 0, 0, Me.Size.Width, Me.Size.Height) oGraphics.Dispose()End Sub

private void Form1_Paint(object sender, PaintEventArgs e){ // crear un objeto bitmap Bitmap oBitmap = new Bitmap("LogoWinXP.gif");

// pintar el objeto con el contexto // gráfico del formulario; // el area a pintar abarca desde la // coordenada 0,0 y toma el ancho y alto // del formulario de su propiedad SizeEj

emplo

de

lectu

ra

Page 32: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 32

Graphics oGraphics = e.Graphics; oGraphics.DrawImage(oBitmap, 0, 0, this.Size.Width, this.Size.Height); oGraphics.Dispose();}

Código fuente 22

Queda todavía un paso más, ya que aunque la imagen se muestra como fondo del formulario, siredimensionamos este, sólo se repinta la parte nueva redimensionada, produciendo un efecto nodeseado. Ver la Figura 9.

Figura 9. Imagen de fondo con repintado irregular.

Para conseguir que se pinte de nuevo toda la imagen, debemos utilizar el evento Resize para invalidarla zona gráfica del formulario mediante el método Form.Invalidate(). Este evento se produce cada vezque cambia el tamaño del formulario. Ver el Código fuente 23.

Private Sub Form1_Resize(ByVal sender As Object, ByVal e As System.EventArgs)Handles MyBase.Resize ' cada vez que cambie el tamaño del formulario ' invalidamos su área de dibujo para que ' el evento Paint pinte toda la superficie Me.Invalidate()End Sub

private void Form1_Resize(object sender, System.EventArgs e){

// cada vez que cambie el tamaño del formulario// invalidamos su área de dibujo para que// el evento Paint pinte toda la superficiethis.Invalidate();

}

Código fuente 23

El control pictureboxDespués de la técnica compleja (sólo en parte), pasemos ahora al modo fácil para crear una imagen defondo, empleando el control PictureBox.

Este control nos permite la visualización de imágenes en el formulario de un modo sencillo, ya quetoda la mecánica de generación la lleva incorporada, con lo que el programador se despreocupa de lamanipulación del gráfico a mostrar.

Una vez insertado un PictureBox en el formulario, asignaremos a su propiedad Dock el valor Fill, deforma que el control ocupe por completo la superficie del formulario.Ej

emplo

de

lectu

ra

Page 33: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 33

A continuación asignaremos el archivo de imagen a la propiedad Image, y por último estableceremosla propiedad SizeMode al valor StretchImage. Estas operaciones podemos realizarlas tanto en modo dediseño como por código, lo cual vemos en el Código fuente 24.

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles MyBase.Load Me.picImagenFondo.Image = New Bitmap("LogoWinXP.gif") Me.picImagenFondo.SizeMode = PictureBoxSizeMode.StretchImageEnd Sub

private void Form1_Load(object sender, EventArgs e){ this.picImagenFondo.Image = new Bitmap("LogoWinXP.gif"); this.picImagenFondo.SizeMode = PictureBoxSizeMode.StretchImage;}

Código fuente 24

Al ejecutar la aplicación, la imagen quedará en todo momento ajustada al formulario; el mismoresultado que con el anterior ejemplo, pero más sencillo en este caso. Ver la Figura 10.

Figura 10. Imagen de fondo del formulario empleando un PictureBox.

Manipulando el grado de opacidad del formularioLa propiedad Opacity de la clase Form, contiene un valor numérico de tipo Double que permiteestablecer el grado de opacidad del formulario. Cuando esta propiedad contenga el valor uno, elformulario se mostrará en la forma habitual, y cuando el valor sea cero, el formulario serátransparente.

Podemos asignar valores intermedios de modo que hagamos parcialmente transparente el formulario,mostrando los elementos que quedan bajo el mismo. Debemos tener en cuenta que cuando asignemoseste valor mediante la ventana de propiedades del formulario en modo de diseño, el número asignadoserá el porcentaje de opacidad. La Figura 11 muestra la ventana de propiedades para este caso con unsetenta y cinco por ciento de opacidad para el formulario.

Figura 11. Propiedad Opacity establecida desde la ventana de propiedades del IDE.

La Figura 12 muestra este formulario con el anterior porcentaje de opacidad.

Ejem

plo d

e lec

tura

Page 34: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 34

Figura 12. Formulario parcialmente transparente debido a la propiedad Opacity.

En el Código fuente 25 establecemos, desde el evento DoubleClick del formulario, la opacidad de esteúltimo a un grado del cuarenta y cinco por ciento.

Private Sub Form1_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs)Handles MyBase.DoubleClick Me.Opacity = 0.45End Sub

private void Form1_DoubleClick(object sender, System.EventArgs e){

this.Opacity = 0.45;}

Código fuente 25

Podemos proporcionar una mayor interacción al usuario para esta propiedad, añadiendo al formularioun control TrackBar, que con el estilo de un control de volumen, nos va a permitir graduar el nivel deopacidad del formulario.

Mediante las propiedades Maximum y Minimum estableceremos el rango de opacidadrespectivamente a diez y cero. Por otra parte, asignaremos a la propiedad LargeChange el valor uno,para que el desplazamiento del indicador del control sea más suave; mientras que en la propiedadValue asignaremos el valor diez, para partir de la máxima opacidad e ir disminuyendo.

Como efectos visuales de este control, las propiedades Orientation y TickStyle nos permiten establecerla orientación del indicador de posición y su apariencia.

Finalmente, el evento Scroll se producirá cada vez que movamos el indicador de posición en elcontrol, ejecutando el código de su procedimiento manipulador, que vemos en el Código fuente 26.

Private Sub tkbOpaco_Scroll(ByVal sender As Object, ByVal e As System.EventArgs)Handles tkbOpaco.Scroll ' cada vez que se mueve el desplazador ' del TrackBar se modifica la opacidad Select Case Me.tkbOpaco.Value Case 0 Me.Opacity = 0

Case 10 Me.Opacity = 1

Case Else Me.Opacity = Me.tkbOpaco.Value / 10Ej

emplo

de

lectu

ra

Page 35: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 35

End SelectEnd Sub

private void tkbOpaco_Scroll(object sender, System.EventArgs e){

// cada vez que se mueve el desplazador// del TrackBar se modifica el grado de opacidadswitch (this.tkbOpaco.Value){

case 0:this.Opacity=0;break;

case 10:this.Opacity=1;break;

default:this.Opacity=(double)this.tkbOpaco.Value / (double)10;break;

}}

Código fuente 26

La Figura 13 muestra este ejemplo en ejecución.

Figura 13. Utilizando un TrackBar para graduar la opacidad de un formulario.

Esta útil característica de los formularios nos permite, por ejemplo, proporcionar un efecto de fundidodurante su proceso de cierre. Para conseguirlo, escribiremos el código necesario en el manipulador delevento FormClosing del formulario. Este evento es producido cuando el formulario está a punto decerrarse, y lo que haremos será cancelar el cierre del formulario, creando un temporizador que será elque a lo largo de varios intervalos de tiempo, realizará el fundido y cierre.

En el código del evento Tick crearemos el efecto de fundido, disminuyendo el grado de opacidad delformulario hasta hacerlo invisible, punto en el que cerraremos el formulario. Esto hará que se vuelva apasar por el evento FormClosing, pero en esta ocasión, como la opacidad del formulario estará a cero,no se volverá a realizar el proceso de fundido. Ver el Código fuente 27.

Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e AsSystem.ComponentModel.CancelEventArgs) Handles MyBase.FormClosing Dim oTiempo As Timer

' el formulario debe tener el valor 1 ' en Opacity para conseguir el efecto If Me.Opacity <> 0 ThenEj

emplo

de

lectu

ra

Page 36: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 36

' cancelamos el cierre del formulario e.Cancel = True

' iniciamos un temporizador cada medio segundo oTiempo = New Timer() oTiempo.Interval = 500 ' conectamos el temporizador con un manipulador ' de su evento Tick AddHandler oTiempo.Tick, AddressOf TickTiempo oTiempo.Start() End IfEnd Sub'----------------------------------------Private Sub TickTiempo(ByVal sender As Object, ByVal e As EventArgs) ' este manipulador del evento del temporizador, ' cada medio segundo irá disminuyendo el grado ' de opacidad del formulario hasta hacerlo invisible Static dbContador As Double = 1 dbContador -= 0.1 Me.Opacity = dbContador

' cuando el formulario es invisible If Me.Opacity = 0 Then ' parar el temporizador CType(sender, Timer).Stop() ' cerrar el formulario Me.Close() End IfEnd Sub

private void Form1_FormClosing(object sender, System.ComponentModel.CancelEventArgse){

Timer oTiempo;

// el formulario debe tener el valor 1// en Opacity para conseguir el efectoif (this.Opacity != 0){

// cancelamos el cierre del formularioe.Cancel = true;

// iniciamos un temporizador cada medio segundooTiempo = new Timer();oTiempo.Interval = 500;// conectamos el temporizador con un manipulador// de su evento TickoTiempo.Tick += new EventHandler(TickTiempo);oTiempo.Start();

}}//----------------------------------------private double dbContador = 1;

private void TickTiempo(Object sender, EventArgs e){

// este manipulador del evento del temporizador,// cada medio segundo irá disminuyendo el grado// de opacidad del formulario hasta hacerlo invisibledbContador -= 0.1;this.Opacity = dbContador;

// cuando el formulario es invisibleif (this.Opacity == 0){

// parar el temporizador((Timer)sender).Stop();Ejem

plo d

e lec

tura

Page 37: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 37

// cerrar el formulariothis.Close();

}}

Código fuente 27

Creación de formularios con formas irregularesLas capacidades gráficas de GDI+ nos permiten dotar a los formularios de efectos hasta la fecha muydifíciles de conseguir, si no se conocía en profundidad el API de Windows.

Aspectos como la transparencia del formulario tratada en el apartado anterior, o el cambio en su formaestándar, que veremos seguidamente, permiten la creación de ventanas con una apariencia noconvencional.

Usando la clase regionPara cambiar el aspecto rectangular de un formulario, dotándole de una forma irregular, debemos, enprimer lugar, declarar en nuestro código el espacio de nombres System.Drawing.Drawing2D.

A continuación crearemos un objeto de la clase GraphicsPath, y mediante alguno de sus métodosAddTipoObjeto(), le añadiremos una figura que será la que se utilice para la forma del formulario.

Finalmente crearemos un objeto de tipo Region, al que pasaremos el objeto GraphicsPath con la figuracreada previamente. Esto creará una región con dicha figura, que asignaremos a la propiedad Regiondel formulario, consiguiendo de esta manera, modificar el aspecto del formulario en cuanto a su forma.

El Código fuente 28 muestra unos ejemplos de cómo modificar el aspecto del formulario, queincorpora sendos botones para cambiar su forma a un círculo y un triángulo respectivamente.

Imports System.Drawing.Drawing2D'....Private Sub btnCirculo_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles btnCirculo.Click ' crear un GraphicsPath y añadirle un círculo Dim oGPath As GraphicsPath = New GraphicsPath() oGPath.AddEllipse(New Rectangle(0, 0, 200, 260))

' crear una región con el objeto GraphicsPath ' y asignarla a la región del formulario Me.Region = New Region(oGPath)End Sub

Private Sub btnPoligono_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles btnPoligono.Click ' mostrar el formulario con la forma ' de un triángulo utilizando también ' un objeto GraphicsPath y un Region Dim oPuntos(2) As Point oPuntos(0) = New Point(-20, -20) oPuntos(1) = New Point(-20, 200) oPuntos(2) = New Point(220, 90)

Dim oGPath As GraphicsPath = New GraphicsPath() oGPath.AddPolygon(oPuntos)

Me.Region = New Region(oGPath)End SubEj

emplo

de

lectu

ra

Page 38: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 38

using System.Drawing.Drawing2D;//....private void btnCirculo_Click(object sender, EventArgs e){ // crear un GraphicsPath y añadirle un círculo GraphicsPath oGPath = new GraphicsPath(); oGPath.AddEllipse(new Rectangle(0, 0, 200, 260));

// crear una región con el objeto GraphicsPath // y asignarla a la región del formulario this.Region = new Region(oGPath);}

private void btnPoligono_Click(object sender, EventArgs e){ // mostrar el formulario con la forma // de un triángulo utilizando también // un objeto GraphicsPath y un Region Point[] oPuntos = new Point[3]; oPuntos[0] = new Point(-20, -20); oPuntos[1] = new Point(-20, 200); oPuntos[2] = new Point(220, 90);

GraphicsPath oGPath = new GraphicsPath(); oGPath.AddPolygon(oPuntos);

this.Region = new Region(oGPath);}

Código fuente 28

La Figura 14 muestra el formulario con forma de elipse.

Figura 14. Formulario con forma de elipse.

La Figura 15 muestra el formulario con forma de triángulo.

Ejem

plo d

e lec

tura

Page 39: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 39

Figura 15. Formulario con forma triangular.

Los efectos que acabamos de aplicar sobre la forma del formulario tienen el inconveniente de queocultan los botones de la barra de título del mismo. Para modificar la forma de una ventana peromanteniendo su barra de títulos visible al completo, tenemos que seguir los siguientes pasos: crear unobjeto Rectangle con el tamaño del formulario, aplicar la modificación de forma al objetoGraphicsPath; crear una nueva región de dibujo; modificar el rectángulo adaptándolo al tamaño de labarra de títulos; unir el rectángulo con la región; y finalmente asignar la región a la propiedad Regiondel formulario. El Código fuente 29 muestra un ejemplo con las operaciones que acabamos dedescribir.

Private Sub btnBarraTitulo_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles btnBarraTitulo.Click ' crear un rectángulo con las dimensiones del formulario Dim oRectangulo As Rectangle oRectangulo = New Rectangle(0, 0, Me.Size.Width, Me.Size.Height)

' crear un GraphicsPath Dim oGraphPath As GraphicsPath = New GraphicsPath

' añadir al GraphicsPath una elipse ' con las dimensiones del rectángulo oGraphPath.AddEllipse(oRectangulo)

' crear un objeto Region usando el objeto GraphicsPath Dim oRegion As Region = New Region(oGraphPath)

' modificar tamaño del rectángulo para ' que se ajuste a las dimensiones de la ' barra de título del formulario oRectangulo.Width = 400 oRectangulo.Height = 25

' une el objeto region con el rectángulo oRegion.Union(oRectangulo)

' asigna el objeto Region ' a la región de dibujo del formulario Me.Region = oRegionEnd Sub

private void btnBarraTitulo_Click(object sender, System.EventArgs e){

// crear un rectángulo con las dimensiones del formularioRectangle oRectangulo;oRectangulo = new Rectangle(0, 0, this.Size.Width,

this.Size.Height);

// crear un GraphicsPathEjem

plo d

e lec

tura

Page 40: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 40

GraphicsPath oGraphPath = new GraphicsPath();

// añadir al GraphicsPath una elipse// con las dimensiones del rectángulooGraphPath.AddEllipse(oRectangulo);

// crear un objeto Region usando el objeto GraphicsPathRegion oRegion = new Region(oGraphPath);

// modificar tamaño del rectángulo para// que se ajuste a las dimensiones de la// barra de título del formulariooRectangulo.Width = 400;oRectangulo.Height = 25;

// une el objeto region con el rectángulooRegion.Union(oRectangulo);

// asigna el objeto Region// a la región de dibujo del formulariothis.Region = oRegion;

}

Código fuente 29

La Figura 16 muestra el resultado de un formulario con su forma modificada, pero con la barra detítulo completa.

Figura 16. Formulario con forma y barra de título visible.

Usando una imagen para definir la superficie del formularioEsta técnica consiste en tomar cualquier aplicación de manipulación gráfica y realizar un dibujo queconstituirá la superficie de nuestro formulario. Asignaremos un color al dibujo y otro color al fondo dela imagen, o bien la dejaremos con fondo transparente, que será el caso del ejemplo que expondremosa continuación. La Figura 17 muestra la imagen que usaremos como formulario.Ej

emplo

de

lectu

ra

Page 41: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 41

Figura 17. Imagen para establecer la superficie del formulario.

Seguidamente crearemos un proyecto y comenzaremos definiendo la parte visual del formulario.

En primer lugar asignaremos a la propiedad FormBorderStyle el valor None; en la propiedadBackgroundImage estableceremos el archivo gráfico que hemos creado inicialmente, y por último, a lapropiedad TransparencyKey le asignaremos el valor Transparent.

A continuación agregaremos dos botones para cerrar el formulario y visualizar un mensaje, cuyocódigo vemos en el Código fuente 30.

Private Sub btnSalir_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles btnSalir.Click Me.Close()End Sub

Private Sub btnMensaje_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles btnMensaje.Click MessageBox.Show("Formulario con forma irregular")End Sub

private void btnSalir_Click(object sender, EventArgs e){ this.Close();}

private void btnMensaje_Click(object sender, EventArgs e){ MessageBox.Show("Formulario con forma irregular");}

Código fuente 30

La Figura 18 muestra el formulario después de añadirle estos controles.

Ejem

plo d

e lec

tura

Page 42: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 42

Figura 18. Controles para el formulario.

Puesto que hemos eliminado la barra de título del formulario, nos encontramos actualmente con elproblema de proporcionar al usuario la capacidad de mover la ventana por el escritorio de Windows;ello significa que tendremos que escribir código para los eventos del ratón, que posibiliten suministrarde nuevo esta funcionalidad, como vemos en el Código fuente 31.

Public Class frmImagen Private oUbicacionRaton As Point Private bRatonPulsado As Boolean '.... Private Sub frmImagen_MouseDown(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown Dim nCoordX As Integer Dim nCoordY As Integer

' al pulsar el botón izquierdo, activar el movimiento del formulario If e.Button = Windows.Forms.MouseButtons.Left Then bRatonPulsado = True nCoordX = -e.X - SystemInformation.FrameBorderSize.Width nCoordY = -e.Y - SystemInformation.CaptionHeight -SystemInformation.FrameBorderSize.Height oUbicacionRaton = New Point(nCoordX, nCoordY) End If End Sub

Private Sub frmImagen_MouseMove(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.MouseEventArgs) Handles MyBase.MouseMove ' si mantenemos pulsado el botón del ratón, arrastrar el formulario If bRatonPulsado Then Dim oPosicionRaton As Point = Control.MousePosition oPosicionRaton.Offset(oUbicacionRaton.X, oUbicacionRaton.Y)

Me.Location = oPosicionRaton End If End Sub Private Sub frmImagen_MouseUp(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.MouseEventArgs) Handles MyBase.MouseUp ' al soltar el botón izquierdo, desactivar el movimiento del formulario If e.Button = Windows.Forms.MouseButtons.Left Then bRatonPulsado = False End IfEj

emplo

de

lectu

ra

Page 43: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 43

End SubEnd Class

public partial class frmImagen : Form{ Point oUbicacionRaton; Boolean bRatonPulsado; //.... private void frmImagen_MouseDown(object sender, MouseEventArgs e) { int nCoordX; int nCoordY;

// al pulsar el botón izquierdo, activar el movimiento del formulario if (e.Button == MouseButtons.Left) { bRatonPulsado = true; nCoordX = -e.X - SystemInformation.FrameBorderSize.Width; nCoordY = -e.Y - SystemInformation.CaptionHeight -SystemInformation.FrameBorderSize.Height; oUbicacionRaton = new Point(nCoordX, nCoordY); } }

private void frmImagen_MouseMove(object sender, MouseEventArgs e) { // si mantenemos pulsado el botón del ratón, arrastrar el formulario if (bRatonPulsado) { Point oPosicionRaton = Control.MousePosition; oPosicionRaton.Offset(oUbicacionRaton.X, oUbicacionRaton.Y); this.Location = oPosicionRaton; } }

private void frmImagen_MouseUp(object sender, MouseEventArgs e) { // al soltar el botón izquierdo, desactivar el movimiento del formulario if (e.Button == MouseButtons.Left) { bRatonPulsado = false; } }}

Código fuente 31

La Figura 19 muestra en ejecución, este formulario con la superficie basada en una imagen.

Ejem

plo d

e lec

tura

Page 44: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 44

Figura 19. Formulario basado en imagen.

Dibujo manual de elementos de un controlSiguiendo con estos aspectos de personalización gráfica de la interfaz de usuario, le llega el turno enesta ocasión a los controles, más concretamente a los controles de lista como ComboBox, que será elobjeto del ejemplo a desarrollar en el presente apartado.

Por defecto, al desplegar la lista de elementos de un control ComboBox, el color de fondo de la mismase muestra en blanco, o a lo sumo, de un color asignado a través de la propiedad BackColor. Sinembargo, hay ocasiones en las que sería deseable poder mostrar cada uno de los elementos de la listacon un color de fondo diferente.

El control ComboBox expone esta funcionalidad al programador, la cual requerirá por su parte algo detrabajo extra en codificación. No obstante, los resultados obtenidos merecerán el esfuerzo extraempleado.

Para acceder a las operaciones de dibujo de forma personalizada, en primer lugar debemos cambiar elvalor por defecto de la propiedad DrawMode, cuyo contenido es un tipo enumerado del mismonombre que dispone de los valores mostrados en la Tabla 1.

Valor Descripción

Normal El sistema operativo es el encargado de dibujar los elementos.Todos tendrán el mismo tamaño

OwnerDrawFixed El programador es el encargado de dibujar los elementos.Todos tendrán el mismo tamaño

OwnerDrawVariable El programador es el encargado de dibujar los elementos, loscuales pueden tener tamaños distintos

Tabla 1. Valores de la enumeración DrawMode.

Una vez realizada esta asignación, deberemos crear un método manipulador del evento MeasureItem,que se producirá por cada elemento cuyas dimensiones tengan que ser calculadas antes de dibujarse enEj

emplo

de

lectu

ra

Page 45: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 45

la lista. En este método estableceremos el tamaño del elemento utilizando el parámetroMeasureItemEventArgs, que entre otras propiedades dispone de ItemWidth e ItemHeight para estecometido.

El otro evento que debemos codificar para realizar este proceso es DrawItem, que sucede cada vez queun elemento de la lista necesita ser dibujado, y resulta más importante si cabe que el anterior, ya queserá aquí donde tendremos que codificar toda la lógica de dibujo.

Como ejemplo demostrativo crearemos un proyecto con el nombre DibujoElementosLista1:http://www.elcampusdigital.com/FtpTextos/AplicacionWindowsAvanzado/DibujoElementosLista.zipEn el formulario agregaremos un ComboBox que será utilizado para nuestras pruebas, y al queañadiremos tres elementos. La Figura 20 muestra este control con su aspecto estándar, antes de queprocedamos a manipular sus operaciones de dibujo.

Figura 20.

De cara a conseguir el mejor resultado en nuestra personalización del dibujo de este control, debemostener en cuenta una importante característica de su comportamiento relacionada con los eventos quevamos a manejar: cuando la lista de elementos del ComboBox está cerrada y se pulsa su botón dedespliegue, los eventos DrawItem y MeasureItem ocurrirán tantas veces como elementos tenga la lista.Una vez abierta dicha lista, según nos vayamos desplazando por la misma bien con el teclado o ratón,por cada desplazamiento que hagamos a un nuevo elemento, los eventos DrawItem y MeasureItem seproducirán dos veces: una por el elemento que hasta el momento era el seleccionado y está a punto dedejar de serlo; y otra por el elemento que pasa a ser el nuevo seleccionado. Todo este funcionamientoserá el que tengamos que aprovechar para que nuestras operaciones de dibujo de elementos seancorrectas, ya que deberemos pintar el elemento seleccionado con un color diferente del resto, que nossirva para destacarlo.

A continuación pasaremos al código del formulario, declarando en primer lugar dos variables conámbito de clase, que usaremos como soporte para las operaciones de dibujo. La primera de estasvariables: nContadorDibujoItems, la usaremos para controlar el número de veces que el eventoDrawItem se ejecuta al ser abierta la lista desplegable del ComboBox, ya que en este caso loselementos serán dibujados sin usar el color de selección.

La otra variable: nSeleccionadoActual, determinará si el evento DrawItem que se ha producidocorresponde al elemento que acaba de perder su condición de seleccionado, o bien al que acaba deganarla.

Seguidamente, dentro del evento Load del formulario, configuraremos el ComboBox para realizar eldibujo personalizado de elementos; inicializaremos el contador de elementos dibujados en el eventoDropDown, que ocurre al desplegar la lista; y estableceremos las dimensiones de los elementos en elevento MeasureItem.

1 Todos los ejemplos del texto los puede bajar desde la dirección:http://www.elcampusdigital.com/FtpTextos/AplicacionWindowsAvanzado/Ejemplos.zipEj

emplo

de

lectu

ra

Page 46: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 46

Por último pasaremos al evento DrawItem, en el que crearemos los objetos de color y tipo de letra parael elemento de la lista a dibujar. Después calcularemos si debemos pintar el elemento comoseleccionado o no, para finalmente proceder a su dibujo.

Todas estas operaciones se muestran en el Código fuente 32.

Public Class Form1 ' variables complementarias Dim nContadorDibujoItems As Integer Dim nSeleccionadoActual As Integer = -1

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles MyBase.Load ' establecer la altura de la lista desplegable ' y que el dibujo del control se realizará ' manualmente Me.cboLista.DropDownHeight = 150

' especificar que el dibujo del control ' será realizado manualmente Me.cboLista.DrawMode = DrawMode.OwnerDrawVariable End Sub

Private Sub cboLista_DropDown(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles cboLista.DropDown ' inicializar el contador de elementos dibujados ' al ser abierta la lista desplegable del control Me.nContadorDibujoItems = 1 End Sub

' este evento se produce para asignar ' las dimensiones de los elementos del ComboBox Private Sub cboLista_MeasureItem(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.MeasureItemEventArgs) Handles cboLista.MeasureItem e.ItemWidth = 200 e.ItemHeight = 21 End Sub

' cada vez que se necesite dibujar un elemento ' de la lista se produce este evento Private Sub cboLista_DrawItem(ByVal sender As System.Object, ByVal e AsSystem.Windows.Forms.DrawItemEventArgs) Handles cboLista.DrawItem Dim oColorElemento As Color = Color.Empty Dim oColorLetra As Color = Color.Black Dim oTipoLetra As Font = New Font("Comic Sans MS", 8, FontStyle.Bold) Dim oRectElemento As Rectangle

' dibujar el fondo del elemento e.DrawBackground()

' seleccionar el color del elemento a dibujar Select Case e.Index Case 0 oColorElemento = Color.LightGreen

Case 1 oColorElemento = Color.Yellow

Case 2 oColorElemento = Color.LightSkyBlue End Select

' comprobar si se están dibujando los items ' debido a que la lista se acaba de desplegar ' o bien porque ya está desplegada y el usuario ' se desplaza por los mismos If Me.nContadorDibujoItems <= Me.cboLista.Items.Count Then ' la lista se está desplegando, ' dibujar el item con su colorEj

emplo

de

lectu

ra

Page 47: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 47

Me.nContadorDibujoItems += 1 Else ' la lista ya está desplegada If Me.cboLista.SelectedIndex <> -1 And Me.nSeleccionadoActual = -1 Then ' el elemento acaba de ser seleccionado oColorElemento = SystemColors.Highlight oColorLetra = SystemColors.HighlightText Me.nSeleccionadoActual = Me.cboLista.SelectedIndex Else ' el elemento ha dejado de estar seleccionado If Me.cboLista.SelectedIndex = Me.nSeleccionadoActual Then Me.nSeleccionadoActual = -1 End If End If End If

' dibujar el rectángulo del elemento de la lista ' con el color que le corresponda oRectElemento = New Rectangle(e.Bounds.Location, e.Bounds.Size) e.Graphics.FillRectangle(New SolidBrush(oColorElemento), oRectElemento)

' dibujar el texto del elemento de la lista e.Graphics.DrawString(Me.cboLista.Items(e.Index), _ oTipoLetra, _ New SolidBrush(oColorLetra), _ e.Bounds.Location) End SubEnd Class

public partial class Form1 : Form{ // variables complementarias int nContadorDibujoItems; int nSeleccionadoActual = -1; //.... private void Form1_Load(object sender, EventArgs e) { // establecer la altura de la lista desplegable // y que el dibujo del control se realizará // manualmente this.cboLista.DropDownHeight = 150;

// especif (icar que el dibujo del control // será realizado manualmente this.cboLista.DrawMode = DrawMode.OwnerDrawVariable; }

private void cboLista_DropDown(object sender, EventArgs e) { // inicializar el contador de elementos dibujados // al ser abierta la lista desplegable del control this.nContadorDibujoItems = 1; }

// este evento se produce para asignar // las dimensiones de los elementos del ComboBox private void cboLista_MeasureItem(object sender, MeasureItemEventArgs e) { e.ItemWidth = 200; e.ItemHeight = 21; }

// cada vez que se necesite dibujar un elemento // de la lista se produce este evento private void cboLista_DrawItem(object sender, DrawItemEventArgs e) { Color oColorElemento = Color.Empty; Color oColorLetra = Color.Black; Font oTipoLetra = new Font("Comic Sans MS", 8, FontStyle.Bold);Ej

emplo

de

lectu

ra

Page 48: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 48

Rectangle oRectElemento;

// dibujar el fondo del elemento e.DrawBackground();

// seleccionar el color del elemento a dibujar switch (e.Index) { case 0: oColorElemento = Color.LightGreen; break;

case 1: oColorElemento = Color.Yellow; break;

case 2: oColorElemento = Color.LightSkyBlue; break; }

// comprobar si se están dibujando los items // debido a que la lista se acaba de desplegar // o bien porque ya está desplegada y el usuario // se desplaza por los mismos if (this.nContadorDibujoItems <= this.cboLista.Items.Count) { // la lista se está desplegando, // dibujar el item con su color this.nContadorDibujoItems += 1; } else { // la lista ya está desplegada if (this.cboLista.SelectedIndex != -1 & this.nSeleccionadoActual == -1) { // el elemento acaba de ser seleccionado oColorElemento = SystemColors.Highlight; oColorLetra = SystemColors.HighlightText; this.nSeleccionadoActual = this.cboLista.SelectedIndex; } else { // el elemento ha dejado de estar seleccionado if (this.cboLista.SelectedIndex == this.nSeleccionadoActual) { this.nSeleccionadoActual = -1; } } }

// dibujar el rectángulo del elemento de la lista // con el color que le corresponda oRectElemento = new Rectangle(e.Bounds.Location, e.Bounds.Size); e.Graphics.FillRectangle(new SolidBrush(oColorElemento), oRectElemento);

// dibujar el texto del elemento de la lista e.Graphics.DrawString(this.cboLista.Items[e.Index].ToString(), oTipoLetra, new SolidBrush(oColorLetra), e.Bounds.Location); }}

Código fuente 32.

La Figura 21 muestra nuestro control con la lista desplegada en dos vistas diferentes: una de ellasmuestra los elementos con sus colores normales, y otra con uno seleccionado.Ej

emplo

de

lectu

ra

Page 49: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 49

Figura 21. ComboBox con dibujo personalizado de elementos.

La clase ToolStripRenderer.Aplicando GDI+ en lapresentación de la barra de herramientasEntre las diversas aplicaciones y usos que podemos dar a GDI+, se encuentra la capacidad de generarde forma personalizada determinadas características visuales de los objetos ToolStrip y sus clasesderivadas.

Cuando añadimos un componente ToolStrip a un formulario y abrimos su etiqueta inteligente,podemos ver que existe una propiedad con el nombre RenderMode. Esta propiedad se utiliza paraindicar al objeto cuál va a ser el estilo de presentación de sus elementos. Los valores disponibles paraesta propiedad son los siguientes:

· System. Visualiza el objeto con la apariencia básica del sistema operativo. Ver la Figura 22.

Figura 22. Control ToolStrip con apariencia básica.

· Professional. Visualiza el objeto utilizando la combinación visual mejorada de colores, letras,etc., correspondiente a WindowsXP. Ver la Figura 23.

Figura 23. Control ToolStrip con apariencia mejorada.

· ManagerRenderMode. Visualiza el objeto utilizando un objeto que contiene unapresentación personalizada creada por el programador. Inicialmente, el componente ToolStriptiene por defecto este valor, y la apariencia visual que proporciona será la misma que laEjem

plo d

e lec

tura

Page 50: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 50

correspondiente al valor Professional, hasta que el programador escriba su propio código devisualización, lo cual haremos seguidamente.

Para crear una presentación personalizada para los contenidos de un objeto ToolStrip, después deasegurarnos que su propiedad RenderMode tiene el valor ManagerRenderMode, crearemos una claseque herede de ToolStripRenderer, a la que podemos llamar GeneradorPersonalizado, yreemplazaremos aquellos métodos en los que vayamos a crear nuestra propia visualizaciónpersonalizada, teniendo en cuenta que en cada uno de los métodos que reemplacemos, deberemosllamar también a la implementación de ese mismo método en su clase base.

Si consultamos los métodos disponibles para esta clase en la documentación de la plataforma,comprobaremos que existe una buena cantidad de todos aquellos dedicados a las labores depresentación, y que además son los métodos llamados cuando se produce un evento de cambio enalgún aspecto visual del componente. Como convención, estos métodos llevan el nombreOnNombreMétodo().

De esta manera, si queremos por ejemplo que el objeto ToolStrip tenga un color de fondopersonalizado, basado en un fundido de dos colores, en la clase GeneradorPersonalizadoreemplazaremos el método OnRenderToolStripBackground y escribiremos el código mostrado en elCódigo fuente 33.

Imports System.Drawing.Drawing2D

Public Class GeneradorPersonalizado Inherits ToolStripRenderer

Protected Overrides Sub OnRenderToolStripBackground(ByVal e As _ ToolStripRenderEventArgs) ' obtener el objeto Graphics del parámetro del evento Dim oGraphics As Graphics = e.Graphics

' crear un color con degradado para situar como fondo del ToolStrip Dim oLGB As LinearGradientBrush = New LinearGradientBrush(e.AffectedBounds,_ Color.DarkOrange, Color.MediumSpringGreen, LinearGradientMode.Vertical)

oGraphics.FillRectangle(oLGB, e.AffectedBounds) oLGB.Dispose()

' ejecutar el código de este método ' que está en la clase base MyBase.OnRenderToolStripBackground(e) End Sub '....End Class

//....using System.Windows.Forms;using System.Drawing;using System.Drawing.Drawing2D;

class GeneradorPersonalizado : ToolStripRenderer{ protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) { // obtener el objeto Graphics del parámetro del evento Graphics oGraphics = e.Graphics;

// crear un color con degradado para situar como fondo del ToolStrip LinearGradientBrush oLGB = new LinearGradientBrush(e.AffectedBounds, Color.DarkOrange, Color.MediumSpringGreen, LinearGradientMode.Vertical);

oGraphics.FillRectangle(oLGB, e.AffectedBounds);Ejem

plo d

e lec

tura

Page 51: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 51

oLGB.Dispose();

// ejecutar el código de este método // que está en la clase base base.OnRenderToolStripBackground(e); }}

Código fuente 33.

Para que este efecto visual pueda ser aplicado al componente, necesitamos instanciar un objeto denuestra clase GeneradorPersonalizado y asignarlo a la propiedad ToolStrip.Renderer. Un buen lugarpara ello sería en el evento Load del formulario, como vemos en el Código fuente 34

Public Class Form1 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles MyBase.Load Me.tsBHerramientas.Renderer = New GeneradorPersonalizado() End SubEnd Class

public partial class Form1 : Form{ //.... private void Form1_Load(object sender, EventArgs e) { this.tsBHerramientas.Renderer = new GeneradorPersonalizado(); }}

Código fuente 34

Ahora sólo queda ejecutar el proyecto y observar el resultado, que debería parecerse al mostrado en laFigura 24.

Figura 24. Control ToolStrip con apariencia personalizada.

Como ya hemos mencionado, la profusión de métodos en la clase ToolStripRenderer permiteconfigurar los más variados aspectos visuales del objeto ToolStrip. A continuación vamos areemplazar algunos métodos adicionales, para completar el ejemplo que estamos desarrollando.

El método OnRenderItemText se utiliza cuando se va a dibujar el texto correspondiente a un elementodel ToolStrip. Por otra parte, el método OnRenderButtonBackground se emplea para pintar el color defondo de los controles de tipo botón de la barra de herramientas. Finalmente, el métodoOnRenderMenuItemBackground realiza la misma labor que el anterior, pero en este caso para lasopciones de menú, las cuales pueden usarse tanto desde un objeto ToolStrip como desde alguna de susclases derivadas: MenuStrip y StatusStrip. Todo ello lo vemos en el Código fuente 35.

Protected Overrides Sub OnRenderItemText(ByVal e As _ ToolStripItemTextRenderEventArgs) ' mostrar el texto de los elementos del ToolStripEj

emplo

de

lectu

ra

Page 52: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 52

' con un nuevo tipo de letra y color e.TextColor = Color.DarkViolet e.TextFont = New Font("Comic Sans MS", 7, FontStyle.Bold Or FontStyle.Italic)

MyBase.OnRenderItemText(e)End Sub

Protected Overrides Sub OnRenderButtonBackground(ByVal e As _ ToolStripItemRenderEventArgs) ' pintar el fondo de un botón del ToolStrip ' con un color degradado distinto ' en función de si está o no seleccionado MyBase.OnRenderButtonBackground(e)

Dim oGraphics As Graphics = e.Graphics Dim oRectangulo As Rectangle oRectangulo = New Rectangle(0, 0, e.Item.Bounds.Width, e.Item.Bounds.Height)

' si el botón está seleccionado... If e.Item.Selected Then ' ...si el botón está además pulsado If e.Item.Pressed Then Dim oLGB As LinearGradientBrush oLGB = New LinearGradientBrush(oRectangulo, _ Color.Crimson, Color.Gold, _ LinearGradientMode.BackwardDiagonal)

oGraphics.FillRectangle(oLGB, oRectangulo) Else ' si el botón no está pulsado (el ratón sólo está pasando encima) ' pintamos el color con una estructura Using ' para optimizar recursos del sistema Using oLGB As LinearGradientBrush = _ New LinearGradientBrush(oRectangulo, _ Color.SpringGreen, Color.Beige, LinearGradientMode.ForwardDiagonal) oGraphics.FillRectangle(oLGB, oRectangulo) End Using End If End IfEnd Sub

Protected Overrides Sub OnRenderMenuItemBackground(ByVal e AsSystem.Windows.Forms.ToolStripItemRenderEventArgs) ' pintar el fondo de una opción de menú ' con un color distinto en función de si la opción ' está o no seleccionada MyBase.OnRenderMenuItemBackground(e)

Dim oGraphics As Graphics = e.Graphics Dim oLGB As LinearGradientBrush Dim oRectangulo As Rectangle oRectangulo = New Rectangle(0, 0, e.Item.Bounds.Width, e.Item.Bounds.Height)

If e.Item.Selected Then If e.Item.Pressed Then oLGB = New LinearGradientBrush(New Rectangle(0, 0, _ e.Item.Bounds.Width, e.Item.Bounds.Height), _ Color.Maroon, Color.MediumAquamarine, _ LinearGradientMode.Horizontal) Else oLGB = New LinearGradientBrush(New Rectangle(0, 0, _ e.Item.Bounds.Width, e.Item.Bounds.Height), _ Color.Aqua, Color.Crimson, _ LinearGradientMode.ForwardDiagonal) End If Else oLGB = New LinearGradientBrush(New Rectangle(0, 0, _ e.Item.Bounds.Width, e.Item.Bounds.Height), _Ej

emplo

de

lectu

ra

Page 53: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 53

Color.Lime, Color.LavenderBlush, _ LinearGradientMode.Vertical) End If

oGraphics.FillRectangle(oLGB, New Rectangle(0, 0, _ e.Item.Bounds.Width, e.Item.Bounds.Height))End Sub

protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e){ // mostrar el texto de los elementos del ToolStrip // con un nuevo tipo de letra y color e.TextColor = Color.DarkViolet; e.TextFont = new Font("Comic Sans MS", 7, FontStyle.Bold | FontStyle.Italic); base.OnRenderItemText(e);}

protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e){ // pintar el fondo de un botón del ToolStrip // con un color degradado distinto // en función de si está o no seleccionado base.OnRenderButtonBackground(e);

Graphics oGraphics = e.Graphics; Rectangle oRectangulo; oRectangulo = new Rectangle(0, 0, e.Item.Bounds.Width, e.Item.Bounds.Height);

// si el botón está seleccionado... if (e.Item.Selected) { // ...si el botón está además pulsado if (e.Item.Pressed) { LinearGradientBrush oLGB; oLGB = new LinearGradientBrush(oRectangulo, Color.Crimson, Color.Gold, LinearGradientMode.BackwardDiagonal);

oGraphics.FillRectangle(oLGB, oRectangulo); } else { // si el botón no está pulsado (el ratón sólo está pasando encima) // pintamos otro color LinearGradientBrush oLGB = new LinearGradientBrush(oRectangulo, Color.SpringGreen, Color.Beige,LinearGradientMode.ForwardDiagonal);

oGraphics.FillRectangle(oLGB, oRectangulo); } }}

protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e){ // pintar el fondo de una opción de menú // con un color distinto en función de si la opción // está o no seleccionada base.OnRenderMenuItemBackground(e);

Graphics oGraphics = e.Graphics; LinearGradientBrush oLGB; Rectangle oRectangulo; oRectangulo = new Rectangle(0, 0, e.Item.Bounds.Width, e.Item.Bounds.Height);

if (e.Item.Selected) {Ej

emplo

de

lectu

ra

Page 54: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 54

if (e.Item.Pressed) { oLGB = new LinearGradientBrush(new Rectangle(0, 0, e.Item.Bounds.Width, e.Item.Bounds.Height), Color.Maroon, Color.MediumAquamarine, LinearGradientMode.Horizontal); } else { oLGB = new LinearGradientBrush(new Rectangle(0, 0, e.Item.Bounds.Width, e.Item.Bounds.Height), Color.Aqua, Color.Crimson, LinearGradientMode.ForwardDiagonal); } } else { oLGB = new LinearGradientBrush(new Rectangle(0, 0, e.Item.Bounds.Width, e.Item.Bounds.Height), Color.Lime, Color.LavenderBlush, LinearGradientMode.Vertical); }

oGraphics.FillRectangle(oLGB, new Rectangle(0, 0, e.Item.Bounds.Width, e.Item.Bounds.Height));}

Código fuente 35

Al volver a ejecutar el formulario, la presentación de los botones al ser pulsados, opciones de menú,etc., también se verá afectada por nuestra clase generadora de elementos visuales, como vemos en laFigura 25.

Figura 25. Efectos de presentación en los elementos del control ToolStrip.

La clase ToolStrip, además de utilizarse para la creación de barras de herramientas, actúa como clasebase de StatusStrip y MenuStrip, por lo que igualmente podemos alterar la presentación del menú y labarra de estado del formulario, escribiendo una clase derivada de ToolStripRenderer, y asignando unainstancia de la misma a la propiedad Renderer de estos objetos.

En el caso de que necesitemos que la apariencia personalizada de todos estos elementos delformulario: barras de herramientas, estado y menú, sea igual, podemos asignar el mismo objeto a lapropiedad Renderer, o lo que es preferible, utilizar la clase ToolStripManager, y asignar el objeto conla visualización personalizada a su método Renderer, como vemos en el Código fuente 36.

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles MyBase.Load ToolStripManager.Renderer = New GeneradorPersonalizado()End Sub

private void Form1_Load(object sender, EventArgs e)Ejem

plo d

e lec

tura

Page 55: Desarrollo de Aplicaciones Windows. Aspectos Avanzados (Ejemplo)

P á g i n a | 55

{ ToolStripManager.Renderer = new GeneradorPersonalizado();}

Código fuente 36

La Figura 26 muestra el resultado de aplicar el anterior código fuente

Figura 26. Efectos de presentación personalizados para todos los objetos.

Caso práctico. Creación y aplicación de un degradado decoloresUtilizando las características gráficas de GDI+ debemos crear y aplicar un efecto de degradado decolores a la superficie de un formulario.

Para seleccionar los colores que compondrán el degradado, emplearemos dos controles ListBox querellenaremos con los nombres de los colores disponibles en la plataforma. Por otra parte, para elegir elmodo de degradado a utilizar, añadiremos al formulario un control ComboBox, que igualmenterellenaremos con los nombres pertenecientes a este tipo de efecto.

Una vez seleccionados todos los valores, mediante la pulsación de un control Button, compondremosel degradado y lo aplicaremos al área cliente del formulario.

Ejem

plo d

e lec

tura