Compartir a través de


Cambios en el modelo de programación

En las secciones siguientes se describen varias formas en que la programación con Windows GDI+ es diferente de la programación con la interfaz de dispositivo gráfico de Windows (GDI).

Contextos de dispositivo, identificadores y objetos gráficos

Si ha escrito programas con GDI (la interfaz de dispositivo gráfico incluida en versiones anteriores de Windows), estará familiarizado con la idea de un contexto de dispositivo (DC). Un contexto de dispositivo es una estructura que usa Windows para almacenar información sobre las funcionalidades de un dispositivo de pantalla y los atributos concretos que especifican cómo se dibujarán los elementos en ese dispositivo. Un contexto de dispositivo para una pantalla de vídeo también está asociado a una ventana determinada en la pantalla. En primer lugar, debe obtener un identificador para un contexto de dispositivo (HDC) y, a continuación, pasar ese identificador como argumento a las funciones de GDI que realmente realizan el dibujo. También se pasa el identificador como argumento a las funciones de GDI que obtienen o establecen los atributos del contexto del dispositivo.

Cuando se usa GDI+, no es necesario preocuparse por los identificadores y los contextos de dispositivo como lo hace al usar GDI. Simplemente cree un objeto Graphics y, a continuación, invoque sus métodos en el estilo conocido orientado a objetos: myGraphicsObject.DrawLine(parameters). El objeto Graphics se encuentra en el núcleo de GDI+, igual que el contexto del dispositivo está en el núcleo de GDI. El contexto del dispositivo y el objeto Graphics desempeñan roles similares, pero hay algunas diferencias fundamentales entre el modelo de programación basado en identificadores que se usa con contextos de dispositivo (GDI) y el modelo orientado a objetos que se usa con objetos Graphics (GDI+).

El objeto Graphics, como el contexto del dispositivo, está asociado a una ventana determinada en la pantalla y contiene atributos (por ejemplo, modo de suavizado y sugerencia de representación de texto) que especifican cómo se van a dibujar los elementos. Sin embargo, el objeto Graphics no está asociado a un lápiz, pincel, ruta, imagen o fuente como contexto del dispositivo. Por ejemplo, en GDI, para poder usar un contexto de dispositivo para dibujar una línea, debe llamar a SelectObject para asociar un objeto de lápiz con el contexto del dispositivo. Esto se conoce como seleccionar el lápiz en el contexto del dispositivo. Todas las líneas dibujadas en el contexto del dispositivo usarán ese lápiz hasta que seleccione un lápiz diferente. Con GDI+, se pasa un objeto Pen como argumento al método DrawLine de la clase Graphics. Puede usar un objeto Pen diferente en cada una de una serie de llamadas DrawLine sin tener que asociar un objeto Pen determinado con un objeto Graphics.

Dos maneras de dibujar una línea

Los dos ejemplos siguientes dibujan una línea roja de 3 de ancho desde la ubicación (20, 10) a la ubicación (200 100). El primer ejemplo llama a GDI y el segundo llama a GDI+ a través de la interfaz de clase de C++.

Dibujo de una línea con GDI

Para dibujar una línea con GDI, necesita dos objetos: un contexto de dispositivo y un lápiz. Para obtener un identificador de un contexto de dispositivo, llame a BeginPaint y para obtener un identificador para un lápiz llame a CreatePen. A continuación, llame a SelectObject para seleccionar el lápiz en el contexto del dispositivo. Establezca la posición del lápiz en (20, 10) llamando a MoveToEx y, a continuación, dibuje una línea de esa posición del lápiz a (200, 100) llamando a LineTo. Tenga en cuenta que MoveToEx y LineTo reciben hdc como argumento.

HDC          hdc;
PAINTSTRUCT  ps;
HPEN         hPen;
HPEN         hPenOld;
hdc = BeginPaint(hWnd, &ps);
   hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
   hPenOld = (HPEN)SelectObject(hdc, hPen);
   MoveToEx(hdc, 20, 10, NULL);
   LineTo(hdc, 200, 100);
   SelectObject(hdc, hPenOld);
   DeleteObject(hPen);
EndPaint(hWnd, &ps);

Dibujo de una línea con GDI+ y la interfaz de clase de C++

Para dibujar una línea con GDI+ y la interfaz de clase de C++, necesita un objeto Graphics y un objeto Pen. Tenga en cuenta que no solicita a Windows identificadores para estos objetos. En su lugar, se usan constructores para crear una instancia de la clase Graphics (un objeto Graphics) y una instancia de la clase Pen (un objeto Pen). Dibujar una línea implica llamar al método Graphics::DrawLine de la clase Graphics. El primer parámetro del método Graphics::DrawLinees un puntero al objeto Pen. Se trata de un esquema más sencillo y flexible que seleccionar un lápiz en un contexto de dispositivo, como se muestra en el ejemplo de GDI anterior.

HDC          hdc;
PAINTSTRUCT  ps;
Pen*         myPen;
Graphics*    myGraphics;
hdc = BeginPaint(hWnd, &ps);
   myPen = new Pen(Color(255, 255, 0, 0), 3);
   myGraphics = new Graphics(hdc);
   myGraphics->DrawLine(myPen, 20, 10, 200, 100);
   delete myGraphics;
   delete myPen;
EndPaint(hWnd, &ps);

Lápices, pinceles, rutas, imágenes y fuentes como parámetros

Los ejemplos anteriores muestran que los objetos Pen se pueden crear y mantener independientemente del objeto Graphics, que proporciona los métodos de dibujo. Los objetos Brush, GraphicsPath, Image y Font también se pueden crear y mantener por separado del objeto Graphics. Muchos de los métodos de dibujo proporcionados por la clase Graphics reciben un objeto Brush, GraphicsPath, Image o Font como argumento. Por ejemplo, la dirección de un objeto Brush se pasa como argumento al método FillRectangle y la dirección de un objeto GraphicsPath se pasa como argumento al método Graphics::DrawPath. Del mismo modo, las direcciones de los objetos Image y Font se pasan a los métodos DrawImage y DrawString. Esto contrasta con GDI, donde selecciona un pincel, una ruta, una imagen o una fuente en el contexto del dispositivo y, a continuación, pasa un identificador al contexto del dispositivo como argumento para una función de dibujo.

Sobrecarga de métodos

Muchos de los métodos GDI+ están sobrecargados; es decir, varios métodos comparten el mismo nombre, pero tienen listas de parámetros diferentes. Por ejemplo, el método DrawLine de la clase Graphics tiene las siguientes formas:

Status DrawLine(IN const Pen* pen,
                IN REAL x1,
                IN REAL y1,
                IN REAL x2,
                IN REAL y2);
Status DrawLine(IN const Pen* pen,
                IN const PointF& pt1,
                IN const PointF& pt2);
Status DrawLine(IN const Pen* pen,
                IN INT x1,
                IN INT y1,
                IN INT x2,
                IN INT y2);
    
Status DrawLine(IN const Pen* pen,
                IN const Point& pt1,
                IN const Point& pt2);

Las cuatro variaciones de DrawLine anteriores reciben un puntero a un objeto Pen, las coordenadas del punto inicial y las coordenadas del punto final. Las dos primeras variaciones reciben las coordenadas como números de punto flotante y las dos últimas variaciones reciben las coordenadas como enteros. Las primeras y terceras variaciones reciben las coordenadas como una lista de cuatro números independientes, mientras que las variaciones segunda y cuarta reciben las coordenadas como un par de objetos Point (o PointF).

No más posición actual

Tenga en cuenta que en los métodos DrawLine mostrados anteriormente, tanto el punto inicial como el punto final de la línea se reciben como argumentos. Se trata de una salida del esquema GDI donde se llama a MoveToEx para establecer la posición actual del lápiz seguida de LineTo para dibujar una línea que empieza en (x1, y1) y termina en (x2, y2). GDI+ en su conjunto ha abandonado la noción de posición actual.

Métodos independientes para dibujar y rellenar

GDI+ es más flexible que GDI cuando se trata de dibujar los contornos y rellenar los interiores de formas como rectángulos. GDI tiene una función Rectangle que dibuja el contorno y rellena el interior de un rectángulo en un solo paso. El contorno se dibuja con el lápiz seleccionado actualmente y el interior se rellena con el pincel seleccionado actualmente.

hBrush = CreateHatchBrush(HS_CROSS, RGB(0, 0, 255));
hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
SelectObject(hdc, hBrush);
SelectObject(hdc, hPen);
Rectangle(hdc, 100, 50, 200, 80);

GDI+ tiene métodos independientes para dibujar el contorno y rellenar el interior de un rectángulo. El método DrawRectangle de la clase Graphics tiene la dirección de un objeto Pen como uno de sus parámetros y el método FillRectangle tiene la dirección de un objeto Brush como uno de sus parámetros.

HatchBrush* myHatchBrush = new HatchBrush(
   HatchStyleCross,
   Color(255, 0, 255, 0),
   Color(255, 0, 0, 255));
Pen* myPen = new Pen(Color(255, 255, 0, 0), 3);
myGraphics.FillRectangle(myHatchBrush, 100, 50, 100, 30);
myGraphics.DrawRectangle(myPen, 100, 50, 100, 30);

Tenga en cuenta que los métodos FillRectangle y DrawRectangle en GDI+ reciben argumentos que especifican el borde izquierdo, la parte superior, la anchura y la altura del rectángulo. Esto contrasta con la función Rectangle de GDI, que toma argumentos que especifican el borde izquierdo, el borde derecho, la parte superior y la parte inferior del rectángulo. Tenga en cuenta también que el constructor de la clase Color en GDI+ tiene cuatro parámetros. Los tres últimos parámetros son los valores de color rojo, verde y azul habituales; el primer parámetro es el valor alfa, que especifica la medida en que el color que se dibuja se combina con el color de fondo.

Construcción de regiones

GDI proporciona varias funciones para crear regiones: CreateRectRgn, CreateEllpticRgn, CreateRoundRectRgn, CreatePolygonRgn y CreatePolyPolygonRgn. Es posible que la clase Region de GDI+ tenga constructores análogos que toman rectángulos, elipses, rectángulos redondeados y polígonos como argumentos, pero no es así. La clase Region de GDI+ proporciona un constructor que recibe una referencia de objeto Rect y otro constructor que recibe la dirección de un objeto GraphicsPath. Si desea construir una región basada en una elipse, rectángulo redondeado o polígono, puede hacerlo fácilmente creando un objeto GraphicsPath (que contiene una elipse, por ejemplo) y pasando la dirección de ese objeto GraphicsPath a un constructor Region.

GDI+ facilita la creación de regiones complejas mediante la combinación de formas y rutas. La clase Region tiene métodos Union y Intersect que puede usar para aumentar una región existente con una ruta u otra región. Una buena característica del esquema GDI+ es que un objeto GraphicsPath no se destruye cuando se pasa como argumento a un constructor Region. En GDI, puede convertir una ruta a una región con la función PathToRegion, pero la ruta se destruye en el proceso. Además, un objeto GraphicsPath no se destruye cuando su dirección se pasa como argumento a un método Union o Intersect, por lo que puede usar una ruta determinada como bloque de creación para varias regiones independientes. Esta implementación se muestra en el ejemplo siguiente. Supongamos que onePath es un puntero a un objeto GraphicsPath (simple o complejo) que ya se ha inicializado.

Region  region1(rect1);
Region  region2(rect2);
region1.Union(onePath);
region2.Intersect(onePath);