Guía de migración y actualización a
 Visual Basic .NET

Conozca en su propio idioma todo lo que la plataforma .NET y VB tienen para Usted

Guía de migración y actualización a VB .Net 

 

Características gráficas de GDI+ en .NET framework (Erich Bühler, www.vblibros.com. Publicado en revista Sólo programadores, abril 2004)
Fuentes clic aquí

Introducción

En realidad admito que he conocido pocos programadores que a mano alzada tengan buenas cualidades para el dibujo. Los que son muy buenos para esta tarea en general se dedican al diseño gráfico aunque también hay buenas excepciones. Yo me incluyo dentro del primero grupo y debo aceptar para mi mala fortuna que nunca he podido trazar de forma atractiva ni siquiera la figura humana. Sin embargo pienso que con este artículo muchos de los que estamos en esta situación nos revindicaremos aunque sea de forma virtual mediante el estado del arte elevado a la potencia informática.

Como ya sabrá los sistemas operativos utilizan interfases gráficas compuestas de un conjunto de objetos como ser botones, ventanas, etc. Cada uno de los elementos contienen a su vez puntos, líneas, y texturas. Ellos se forman de píxeles de la pantalla en diferentes sitios apagados o prendidos en uno u otro color. En algunos casos se involucran elementos aún más complejos como ser imágenes, rellenos en degradé y hasta trazados vectoriales. Los objetos que integran la interfaz gráfica normalmente varían dinámicamente su apariencia mediante eventos iniciados por el usuario u otros dispositivos. Este proceso que pasa inadvertido para la mayor parte de las personas en realidad involucra cientos de miles de líneas de código interactuando entre sí para lograr un resultado consistente.

También existen técnicas especiales cuando lo que se desea por ejemplo construir un juego en donde más que líneas y rellenos se necesiten verdaderas construcciones tridimensionales, sonido estéreo, utilización de una red para múltiples participantes, etc. Es por ello que vamos a indagar sobre que opciones contamos hoy en día para trabajar en ambientes gráficos con el fin de poder evaluar las distintas chances y por supuesto sus beneficios. Veamos 4 tecnologías comúnmente utilizadas que creo habrá conocido en mayor o menor medida en algún momento o por lo menos escuchado su nombre:

-         DirectX

-         GDI

-         GDI+

-         OpenGL

Si bien en este artículo nos centraremos en GDI+ analizaremos la utilidad de las demás  técnicas ya que un buen programador debe siempre saber con que alternativas se cuenta a la hora de llevar adelante una tarea. Es fundamental comprender que Microsoft Windows es un sistema WYSIWYG (‘What you see is what you get’), lo que quiere decir que lo que se ve es lo que finalmente se obtiene. Esto ha sido así desde sus comienzos, y se ha hecho un esfuerzo mayúsculo para que lo que se ve en pantalla pueda ser reproducido o llevado a cualquier otro dispositivo (impresora, fax, etc.) sin demasiadas dificultades. La ventaja principal es que esta tarea es transparente para el desarrollador y finalmente para el usuario. Durante más de 5 años para trabajar en serio con la interfaz gráfica se debía recurrir a la biblioteca GDI (Grafic User Interface) del sistema. Ella incluye un conjunto de funciones para realizar trazados. El listado 1 muestra un ejemplo de cómo cambiar el tipo de fuente de un control.

 

CStatic *lpLabel=(CStatic *)GetDlgItem(IDC_STATIC1);

CFont LabelFont;

LabelFont.CreateFont(20,20,0,0,FW_BOLD,FALSE,FALSE,0,DEFAULT_CHARSET,  OUT_CHARACTER_PRECIS,CLIP_CHARACTER_PRECIS,DEFAULT_QUALITY, DEFAULT_PITCH,NULL);

lpLabel->SetFont(&LabelFont,TRUE);

Listado 1.

A simple vista parece más fácil aprender los caracteres del alfabeto japonés (Kenji) que programar utilizando C++ y GDI. Afortunadamente podremos olvidarnos de esta biblioteca ya que más adelante aprenderemos sobre una opción disponible en .NET framework más fácil y flexible. La segunda opción es hacer uso de OpenGL  que es otra biblioteca para gráficos creada inicialmente por Silicon Graphics, actualmente de uso libre y multiplataforma, pero que sólo podremos sacarle provecho si lo que deseamos es crear ambientes tridimensionales. La contrapartida de Microsoft se llama DirectX y además de incluir características similares a las de OpenGL adiciona la gestión de audio, redes, y dispositivos de entrada (joysticks, etc). Digamos que estas últimas son ideales para la creación de juegos o todo aquello que requiera un manejo muy avanzado de la interfaz gráfica donde se requieran dibujos vectoriales en 3 dimensiones y un excelente rendimiento. La tabla 1 muestra una comparativa de las tecnologías detalladas anteriormente.

 

Tecnología

Uso desde .NET framework

Nativo en .NET framework

Trazados

Trazados 3D Reales

Audio

Red

Entrada (Joystick, etc.)

Dificultad
(1 al 5)

DirectX

ü

Ð (Requiere Kit de DirectX 9)

ü

ü

ü

ü

ü

5

GDI

ü (Mediante llamadas a API)

Ð

ü

Ð

Ð

Ð

Ð

4

GDI+

ü

ü

ü

Ð

Ð

Ð

Ð

2

OpenGL

ü

Ð (Requiere biblioteca de OPENGL)

ü

ü

Ð

Ð

Ð

5

Tabla 1.  Comparativa de diferentes tecnologías.

Existe un nicho para el que todavía no se cuenta con una alternativa fácil como es por ejemplo el de los gráficos estándares. ¿Qué debería utilizarse si lo que se necesita es crear un gráfico de tarta, modificar el aspecto de un control o de toda una aplicación? La respuesta en todos los casos GDI+. 

¿Qué es GDI+?

GDI+ es una de las tantas bibliotecas de clases que vienen incluidas de forma nativa en la infraestructura .NET o .NET framework como suele denominarle la gente de Microsoft. Se especializa en el trazado de dibujos y como muestra la tabla 1 su utilización es bastante sencilla. Como veremos más adelante los pasos son en su mayoría siempre los mismos, por lo que una vez entendidos los conceptos básicos es posible realizar en pocas líneas cualquier tipo de trazado sobre un enorme número de objetos de la interfaz gráfica, incluyendo la impresora, vistas preliminares, etc.  El ser parte intrínseca de la infraestructura nos asegurará que las versiones .NET de otros dispositivos (PocketPC, teléfonos inteligentes, etc.) o sistemas operativos (Linux,etc) contarán también con esta biblioteca. Sin embargo en dispositivos móviles de mano se emplea la versión compacta de la infraestructura .NET (.NET Compact Framework), la que brinda en realidad un sub-conjunto de la totalidad de las funcionalidades. Otra ventaja inminente es que la biblioteca puede ser utilizada desde cualquiera de los 25 lenguajes existentes que compilan para .NET y adicionalmente no se encontrará con problemas relacionados con incompatibilidad entre tipos de dato del lenguaje y las funciones de GDI+.

 

Figura 1. Juego de Pocket PC programado utilizando GDI+ de .NET Compact framework .

Vamos a comenzar viendo donde encontrar esta biblioteca ya que es siempre el primer paso para utilizar sus funcionalidades. La tabla 2 nos muestra la localización de las distintas características:

 

Espacio de nombres

Descripción

System.Drawing

Hace posible realizar trazados simples, aunque no por ello deja de resolver gran parte de los casos.

System.Drawing.Design

Ofrece clases para personalizar el cuadro de herramientas y el editor de clases. Se utiliza para extender la interfaz gráfica en tiempo de diseño.

System.Drawing.Drawing2D

Se utiliza para dibujar trazados avanzados en dos dimensiones y vectoriales.

System.Drawing.Imaging

Permite una gestión muy avanzada de imágenes y meta-archivos.

System.Drawing.Printing

Hace posible controlar todo lo relacionado con la impresión.

System.Drawing.Text

Si necesita crear, modificar, o utilizar fuentes entonces las clases de este espacio son las que deberá emplear.

Tabla 2. Resumen de espacios en GDI+.

 Como podrá apreciar GDI+ se ofrece a través de System.Drawing, o lo que es igual, System.Drawing es el espacio raíz. Aquellos que se encuentran destacados son los que normalmente se emplean y el resto se han incluido a modo informativo debido que raramente se usan.

Por donde comenzar…

Si se quiere pintar sobre un objeto lo primero que hay que tener es una buena dosis de pintura, un poco de inventiva y por supuesto acceso al objeto que se desea colorear. Un buen ejemplo de todo esto en conjunto es el exhibido en la figura 2:

 

Figura 2: Para poder pintar sobre un objeto hay que tener acceso a este.

Al igual que en la vida real la regla de oro en GDI+ es que siempre que se quiera pintar sobre algo primero hay que ganar su acceso. Esto se logra en todos los casos mediante el Objeto de contexto gráfico, punto de comienzo para todo viaje a emprender hacia GDI+. La mayor parte de los controles (botones, ventanas, etc.) cuentan con uno, lo que posibilita su acceso gráfico y posterior trazado sobre el mismo. Si de un elemento no se puede obtener el objeto de contexto gráfico entonces no se podrá modificar su aspecto.

“El objeto de contexto gráfico es
el punto de comienzo para todo viaje
a emprender hacia GDI+”

Veamos entonces como obtener el Objeto de contexto gráfico de un formulario y su posterior almacenamiento en una variable: 

Graphics Superficie = this.CreateGraphics();

La variable Superficie es del tipo Graphics quien es en realidad el tipo del objeto de contexto gráfico. El método CreateGraphics del formulario crea y guarda la referencia al área de dibujo haciendo que se comience nuestra travesía por GDI+. Estas líneas siempre serán constantes así se quiera dibujar sobre otros controles de la interfaz gráfica. Por supuesto que para esto último deberá reemplazar la palabra this por el nombre del elemento deseado.

 

Dibujos simples

Para llevar adelante un dibujo es necesario contar al menos con un lápiz o una brocha. Esta aproximación también es la elegida por esta biblioteca ya que se tiene una estructura llamada Pen que define este tipo de elemento. El color, el ancho, y hasta la textura es información factible de indicar en el constructor predeterminado de la clase. El siguiente ejemplo muestra como crear una brocha de 10 píxeles de ancho con textura sólida de color rojo. 

Pen Lápiz = new Pen(Color.Red, 10);

La estructura Color ofrece varias opciones predeterminadas aunque por supuesto también se pueden definir colores personalizados. Como podrá imaginar Pen se usa para llevar adelante el trazado de diversos dibujos, por lo que es muy importante comprender su funcionamiento. El próximo paso es utilizar alguno de los métodos disponibles para plasmar el dibujo deseado (figura geométrica, relleno, etc). Veamos las opciones con las que se cuenta en la tabla 3.

Método

Utilidad

DrawArc

Plasma una línea en forma de arco.

DrawBezier, DrawBeziers, DrawCurve

Dibuja líneas curvas o de Bezier (esto último lo veremos más adelante).

DrawEllipse

Traza un círculo o elipse.

DrawImage

Plasma una imagen.

DrawLine

Dibuja una línea.

DrawPath

Almacena la definición de líneas, curvas y rellenos.

DrawPie

Dibuja una sección de un gráfico de tarta.

DrawPolygon

Traza un polígono.

DrawRectangle

Dibuja un rectángulo.

DrawString

Plasma una cadena de caracteres.

Tabla 3. Resumen de métodos de dibujo.

Ha llegado el momento de juntar todo lo aprendido para ver como plasmar finalmente el resultado de tanto esfuerzo. He elegido un rectángulo aunque le recomiendo que experimente con los demás métodos expuestos, como ser círculos, arcos, etc. 

private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
       //Obtiene el contexto gráfico.
       Graphics Superficie = e.Graphics;

       //Crea el lápiz a utilizar.
      
Pen Lápiz = new Pen(Color.Red,10); 

       //Rectángulo que comienza en la posición 30,30
       //y tendrá un ancho y alto de 100, 100 píxeles
       Superficie.DrawRectangle(Lápiz,30,30,100,100);
}

Listado 2.

Figura 3: Resultado de DrawRectangle.

Como podrá apreciar se ha incluido este código dentro del evento Paint y esto es así para que se repinte automáticamente el dibujo. La única diferencia palpable es que el objeto de contexto gráfico se obtiene de la propiedad Graphics del argumento e. Esto hará que automáticamente .NET framework ejecute este código cada vez que sea necesario repintar la pantalla.

 

Estructuras de uso común

Antes de continuar con las diferentes características de GDI+ es bueno que conozca 3 estructuras de utilidad mayúscula que se emplean en gran parte de las funciones de trazado:

-         Point

-         Size

-         Rectangle

Cuando se quiere especificar un punto en pantalla algunas funciones aceptan que esto se haga mediante un parámetro de horizontal X y otro de vertical Y. Sin embargo en otros casos se requiere una estructura de tipo Point. Ésta además de contener algunos  métodos de conversión brinda una propiedad llamada X y otra Y que almacenan las coordenadas de un plano bidimensional. El siguiente ejemplo exhibe la utilización de Point para crear una línea de Bezier.

 

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

       //Obtiene el contecto gráfico.
       Graphics Superficie = e.Graphics;                    

       //Crea el lápiz a utilizar.
      
Pen Lápiz = new Pen(Color.Red,10); 

       //Estructura con Puntos de la línea Bezier.
       Point Punto1 = new Point(100,100);
       Point Punto2 = new Point(150,50);
       Point Punto3 = new Point(200,100);
       Point Punto4 = new Point(250,50); 

       //Dibuja la línea Bezier utilizando los puntos especificados.
       Superficie.DrawBezier(Lápiz, Punto1, Punto2, Punto3, Punto4); 

}

Listado 3. 

Figura 4: Trazado de línea de Bezier.

El trazado de Bezier es una forma bastante divertida ya que los puntos de comienzo y fin son parte de la línea mientras que los demás actúan como imanes atrayendo la línea hacia ellos.

Por su parte Size tiene un significado similar pero para indicar el ancho y alto de un objeto como se muestra a continuación.

Size Tamaño = new Size(200,200);

La estructura de rectángulo o Rectangle constituye la columna vertebral de muchas otras formas entre las cuales se destacan las elipses, gráficos de tarta, etc. Esto es debido a que la misma representa también una región rectangular del área de contexto gráfico en vez de considerarse una mera forma geométrica. Existe también una estructura llamada Region la que ofrece mayor control y flexibilidad para trabajar con regiones de la pantalla, pero dejaremos esta última de lado con el fin de no extendernos en demasía. El siguiente ejemplo declara una estructura de tipo Rectangle para construir un círculo. Obviaremos de aquí en más la obtención del contexto y creación del lápiz para centrarnos en lo que nos interesa. 

       //Locación y tamaño del dibujo.
       Point ÁnguloSuperior = new Point(50,20);
       Size Tamaño = new Size(200,200); 

       //Estructura de rectángulo a contener el círculo.
       Rectangle Rectángulo = new Rectangle(ÁnguloSuperior, Tamaño); 

       //Dibuja finalmente el círculo.
       Superficie.DrawEllipse(Lápiz, Rectángulo);

 

Figura 5: Resultado de DrawEllipse.

 

Utilización de rellenos

Una de las particularidades más interesantes del trabajo con objetos geométricos es la utilización de rellenos. Afortunadamente la forma de lograr esto es muy sencilla ya que GDI+ se encarga de los cálculos del área a cubrir. Como es posible ver en la tabla 4 cada trazado tiene una contrapartida de relleno y basta invocar a la adecuada para que se obtenga la configuración resultante.

Método

Utilidad

DrawLine

La línea es a su vez relleno, por lo que no se requiere un método específico.

FillEllipse

Rellena una elipse definida dentro de un rectángulo.

FillPath

Rellena el interior de un conjunto de líneas y curvas conectadas. Si el dibujo no se encuentra cerrado entonces este método cerrará el dibujo automáticamente mediante un segmente entre el primer punto y el último.

FillPie

Rellena el interior de un trozo de una tarta.

FillPolygon

Rellena el interior de un polígono resultante de una matriz de puntos.

FillRectangle

Rellena el interior de un rectángulo.

FillRegion

Rellena el interior de una región.

Tabla 4. Resumen de métodos de relleno.

 El siguiente ejemplo muestra la utilización de un relleno sólido y otro con textura mediante una estructura HatchBrush.

 Nota: Para utilizar HatchBrush deberá importar al comienzo del módulo el espacio de nombres System.Drawing.Drawing2D. 

       //Crea la brochas a utilizar.
       HatchBrush Brocha1 = new HatchBrush(HatchStyle.BackwardDiagonal, Color.Red, Color.White);
      
SolidBrush Brocha2 = new SolidBrush(Color.Blue);

        //Locación y tamaño de cada dibujo.
       Point ÁnguloSuperior1 = new Point(20,20);
       Point ÁnguloSuperior2 = new Point(120,120);
      
Size Tamaño = new Size(90,90); 

       //Estructura de rectángulos.
       Rectangle Rectángulo1 = new Rectangle(ÁnguloSuperior1, Tamaño);
       Rectangle Rectángulo2 = new Rectangle(ÁnguloSuperior2, Tamaño); 

       //Dibuja y rellena los rectángulos.
       Superficie.FillRectangle(Brocha1, Rectángulo1);
       Superficie.FillRectangle(Brocha2, Rectángulo2);       

Listado 5. 
 

Figura 6: Rectángulos con diferentes tipos de relleno. 

La clase HatchBrush del espacio Drawing2D ofrece más de 50 tramas por lo que no es mala idea que experimente con los distintos tipos para lograr efectos diferentes. Por supuesto que el relleno también puede ser una imagen o incluso un degradé estándar o personalizado. Este último tema es bastante extenso ya que se pueden crear texturas donde se indique el ángulo de entrada de el haz de luz de color y como se deberá efectuar la mezcla o transición de los mismos para plasmar el degradé final. Para no ampliar en demasía este artículo solamente se mostrará como utilizar un degradé estándar y un relleno de imagen. Si le interesa profundizar más sobre este tema le recomiendo que lea en la ayuda la clase ColorBlend y la propiedad InterpolationColors.

       //Crea el objeto y carga la imagen.
       Bitmap Imagen = new Bitmap("imagen.bmp"); 

       //Crea la brochas a utilizar.
       TextureBrush Brocha1 = new TextureBrush(Imagen, WrapMode.TileFlipY); 
       LinearGradientBrush Brocha2 = new  LinearGradientBrush(Rectángulo1,
      
Color.Blue, Color.White, LinearGradientMode.Vertical);      

       //Dibuja los rectángulos.
       Superficie.FillRectangle(Brocha2, Rectángulo1);
       Superficie.FillEllipse(Brocha1, Rectángulo2);

Listado 6.

Figura 7: Diferentes tipos de rellenos incluso con imágenes.

Nuevamente se ha omitido la creación de los rectángulos y coordenadas bidimensionales para centrarnos en lo que nos interesa, no obstante encontrará el listado completo en el CDROM que se adjunta con la revista.

Primeramente se carga la imagen en una variable de tipo Bitmap y luego se crean las brochas para el mapa de bits y el degradé. La estructura LinearGradientBrush es quien lleva adelante la tarea de pintado de este último haciendo uso del parámetro Vertical que indica que la caída del haz de luz se deberá configurar en este sentido. Por su parte la primera brocha emplea una imagen como relleno y usa la clase TextureBrush con el contenido del archivo indicado anteriormente. El parámetro TileFlipY especifica que se desea que se repita la misma en cascada horizontal.

Existe también la posibilidad de aplicar un relleno a un conjunto de caracteres. Para escribir un texto sobre un dispositivo se debe apelar al método DrawString del objeto de contexto gráfico. Como paso previo se tiene que indicar la información de la fuente deseada. El siguiente ejemplo muestra como exhibir el texto Sólo Programadores sobre un formulario, rellenando las letras con un degradé de color azul hacia blanco.

Font Fuente = new Font("Arial",22);

LinearGradientBrush Brocha = new  LinearGradientBrush(Rectángulo, Color.Blue, Color.White, LinearGradientMode.ForwardDiagonal);

 Superficie.DrawString("Sólo programadores", Fuente ,Brocha, 10, 20);

Listado 7. 

Figura 8: Ahora es fácil rellenar cualquier cosa utilizando texturas en degradé.

 

Trazado de gráfico de tarta 3D

Mediante las clases de trazado de líneas, rectángulos y rellenos que vimos hasta el momento podría construirse de forma sencilla un gráfico de barras. Sin embargo existe un tipo aún más llamativo que consiste  en exhibir la información pero utilizando un gráfico de tarta. Este tipo de dibujos hace uso de un círculo dividido en secciones y cada una de ellas representa un porcentaje de la totalidad de la muestra. Afortunadamente mediante GDI+ es posible crear estos dibujos y hasta se puede crear un efecto tridimensional jugando con algunas de las propiedades.

superficie.DrawPie(Lápiz, Rectángulo, 0, 90) 

El método DrawPie es quien se encarga de dibujar un trozo de la tarta y en este caso se plasmará el barrido desde la horizontal (0 grados) hasta su vertical (90 grados). Será siempre en sentido horario si el valor es positivo o anti-horario si es negativo. Basta luego con dibujar los restantes trozos dentro de un rectángulo indicando su locación y ángulo para lograr finalmente el gráfico deseado. Incluso si se desplaza el rectángulo de alguno de los pedazos hacia los lados se obtendrá un efecto óptico de que el elemento ha sido extraído de la tarta. El dibujo final  (se incluye el listado completo en el CD) utiliza varios de los conceptos vistos anteriormente para lograr que el gráfico se vea en tres dimensiones.

 

Figura 9: Con un poco de ingenio también es posible construir dibujos 3D.

Como podemos apreciar no es muy difícil lograr un efecto de este tipo ya que simplemente se necesita dibujar debajo del gráfico de tarta una elipse y un rectángulo, lo que dará la sensación de espesor. Incluso pueden lograrse mejores efectos con un poco más de tiempo e ingenio.
 

Transformaciones

De acuerdo con nuestros conocimientos actuales cuando se realiza un dibujo el mismo debe ser plasmado directamente mediante la utilización de los  métodos del objeto de contexto gráfico. Lógicamente cada dispositivo hace uso de su propio conjunto de recursos y coordenadas para llevar adelante dicha acción. En este proceso existen muchos factores aunque finalmente la información sea trasladada al dispositivo de forma transparente para el desarrollador. En los ejemplos anteriores aprendimos como efectuar un dibujo mediante una estructura y posteriormente transferirla al dispositivo deseado. Sin embargo, esto implica que cada vez que se quiera llevar adelante un trazado se debe crear la estructura adecuada de coordenadas y tamaño y posteriormente trasladar la misma al dispositivo. Ahora bien, en algunas ocasiones se necesita que un dibujo cuente con distintos rellenos y se repita  a lo largo de una superficie, o se mueva en la pantalla (esto podría ser el caso de un juego). Evidentemente las técnicas aprendidas son muy claras pero a la hora de lograr un excelente rendimiento no son una opción a tener en cuenta. Para enmendar esta situación se ofrece una característica llamada Sistema de coordenadas universales (World coordinate Space) la que permite dibujar uno o más trazados, aplicarle rellenos, etc. y finalmente trasladarlos en su conjunto a uno o más  dispositivos. Como característica adicional es posible efectuarle transformaciones a las formas para que representen una nueva versión del dibujo, quizá para exponerse en otra zona de la pantalla o con otro color. El siguiente ejemplo crea un rectángulo en el espacio de coordenadas universales y luego aplica una transformación utilizando una estructura llamada Matrix para ‘alterar su realidad’ y que el objeto inicial se plasme en una zona diferente de la pantalla. 

Rectangle Rectángulo = new Rectangle(10, 10, 30, 50);
GraphicsPath Espacio = new GraphicsPath();
Matrix Transformación; 

//Agrega el rectángulo original al espacio.
Espacio.AddRectangle(Rectángulo);

//Realiza la transformación trasladando una copia a la posición 50,50.
Transformación = new System.Drawing.Drawing2D.Matrix();
Transformación.Translate(50, 50);
Espacio.Transform(Transformación);             

//Agrega el rectángulo copia al espacio.
Espacio.AddRectangle(Rectángulo); 

//Transfiere al dispositivo el resultado.
superficie.DrawPath(Pens.Red, Espacio);

Listado 8. 

En realidad es un solo rectángulo que se duplica y se alteran algunas de sus propiedades. Finalmente se mueve al dispositivo gráfico el espacio en conjunto donde se obtiene el siguiente resultado:

 

Figura 10: Transformaciones en GDI+.

Hemos aprendido varias de las características de GDI+ aunque todavía es posible sacar mucho más jugo más a esta biblioteca. No obstante espero que lo aquí expuesto pueda servirle de guía para comenzar a moverse dentro del mundo gráfico de .NET framework.

 

Nota de copyright: Todas las marcas y/o tecnologías aquí citadas, son marcas registradas de las respectivas empresas y/o dueños.

© 2004 InetWork
All right reserved