Share via


Cambios en el modelo de programación

En las secciones siguientes se describen varias maneras en que la programación con GDI+ de Windows 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), está familiarizado con la idea de un contexto de dispositivo (DC). Un contexto de dispositivo es una estructura usada por Windows para almacenar información sobre las funcionalidades de un dispositivo de visualización y 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, se obtiene un identificador para un contexto de dispositivo (HDC) y, a continuación, se pasa 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 GDI que obtienen o establecen los atributos del contexto del dispositivo.

Al usar GDI+, no es necesario preocuparse por los identificadores y contextos de dispositivo como lo hace al usar GDI. Simplemente se crea un objeto Graphics y, a continuación, se invocan sus métodos en el conocido estilo orientado a objetos: myGraphicsObject.DrawLine(parameters). El objeto Graphics está 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 usado con contextos de dispositivo (GDI) y el modelo orientado a objetos usado con objetos Gráficos (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 dibujarán los elementos. Sin embargo, el objeto Graphics no está asociado a un lápiz, pincel, ruta de acceso, 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 a un objeto Graphics .

Dos maneras de dibujar una línea

En los dos ejemplos siguientes se dibuja una línea roja de ancho 3 de 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++.

Dibujar 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 a un contexto de dispositivo, llame a BeginPaint y un identificador a un lápiz llamando a CreatePen. A continuación, llama a SelectObject para seleccionar el lápiz en el contexto del dispositivo. Para establecer la posición del lápiz en (20, 10), llame a MoveToEx y, a continuación, dibuje una línea de esa posición de 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);

Dibujar 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::D rawLine de la clase Graphics . El primer parámetro del método Graphics::D rawLine es 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 de acceso, imágenes y fuentes como parámetros

Los ejemplos anteriores muestran que los objetos Pen se pueden crear y mantener por separado 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::D rawPath . De forma similar, las direcciones de los objetos Image y Font se pasan a los métodos DrawImage y DrawString . Esto contrasta con GDI, donde se selecciona un pincel, una ruta de acceso, una imagen o una fuente en el contexto del dispositivo y, a continuación, se pasa un identificador al contexto del dispositivo como argumento a 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 la segunda y cuarta variación reciben las coordenadas como un par de objetos Point (o PointF).

No hay 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 en el que se llama a MoveToEx para establecer la posición actual del lápiz seguida de LineTo para dibujar una línea a partir de (x1, y1) y terminar en (x2, y2). GDI+ en su conjunto ha abandonado la noción de posición actual.

Métodos independientes para Draw y Fill

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 todo 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 de GDI+ reciben argumentos que especifican el borde izquierdo del rectángulo, la parte superior, el ancho y el alto. Esto contrasta con la funciónRectangle de GDI, que toma argumentos que especifican el borde izquierdo del rectángulo, el borde derecho, el superior e inferior. 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, puntos suspensivos, rectángulos redondeados y polígonos como argumentos, pero eso no es el caso. 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, a continuación, pasando la dirección de ese objeto GraphicsPath a un constructor Region .

GDI+ facilita la forma de áreas complejas mediante la combinación de formas y rutas de acceso. La clase Region tiene métodos Union e Intersect que puede usar para aumentar una región existente con una ruta de acceso 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 de acceso a una región con la función PathToRegion , pero la ruta de acceso 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 de acceso determinada como bloque de creación para varias regiones independientes. Esto 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);