Apunte de Visual C++
Por: Demian Panello demianpanello@yahoo.com.ar
Capítulo IX
Indice rápido del capítulo 9:
Existe en
Windows un concepto denominado Contexto de dispositivo. Un contexto de dispositivo
proporciona el medio sobre el cual se dibujan todos los puntos, líneas, figuras, fuentes
y todo lo que se ve. La palabra “dispositivo” se refiere al lugar donde se
dibujará, o sea, la pantalla, impresora, plotter o cualquier otro dispositivo de
cualquier fabricante, sin necesidad de tener un excesivo conocimiento del modelo del
mismo.
Algo
positivo que ha hecho Microsoft para la industria del software ha sido estandarizar el
soporte para todos los dispositivos en el sistema Windows.
La MFC (Microsoft Foundation Classes) proporciona clases que simplifican la interacción con los GDI. La clase del contexto de dispositivo global es la CDC. Esta clase proporciona funciones de dibujo, de correspondencia entre coordenadas y otras para implementar la visualización de gráficos.
Todas las demás clases de contexto de dispositivo, más especializadas, se basan en ésta y la extienden.
Para ejemplificar el uso de CDC
vamos a crear una aplicación que “pinte” toda la pantalla, para esto entonces,
cree un proyecto MFC basado en diálogos, con
el nombre “graf1”.
Reduzca
las dimensiones del diálogo un poco y al botón por defecto “Cancelar”
cámbiele el ID por IDC_DIBUJAR y Caption= Dibujar.
Tiene que
quedar un diálogo con sólo dos botones: Aceptar y Dibujar.
Pulse dos veces sobre el botón Dibujar para crear el manejador de
mensajes OnDibujar() allí escriba:
{
// TODO: Add your control notification handler code
here
CDC* pDC;
(1)
CWnd*
pEscritorio; (2)
int x, y;
pDC= pEscritorio->GetWindowDC (); (4)
for(x=0; x<800; x++)
for(y=0;
y<600; y++)
pDC->SetPixel (x, y, RGB(x*y,0,0)); (5)
}
En la
primer línea se declara una variable puntero, pDC, a CDC, por lo general siempre se
declara el futuro objeto CDC como un puntero porque el retorno de la función que los crea
es un puntero.
Como se
desea utilizar toda la pantalla y no sólo la región ocupada por nuestro diálogo
deberemos relacionar el contexto de dispositivo con el escritorio, cosa que en Windows
equivale a toda la pantalla, entonces en la línea (2) se declara
un puntero a la clase CWnd y en (3) se toma el
manejador, (puntero CWnd), del escritorio por medio de la función global
GetDesktopWindow().
Una vez
que tengo el manejador, que sería la “salida” de mi contexto de dispositivo,
creo el objeto CDC en la línea (4) por medio
de la función GetWindowDC() perteneciente a la clase CWnd, que en nuestro caso está
apuntando a la “ventana” que representa el escritorio.
Una vez
que uno tiene referenciado el objeto CDC, en nuestro caso por medio del puntero pDC, se
pueden acceder a todas las funciones GDI disponibles haciendo pDC->función, (note que
se utiliza el operados -> y no el punto ya que la variable es un puntero).
El dibujo
que vamos a hacer será recorrer todo el escritorio dibujando de un color en particular
cada píxel, por eso se usan dos ciclos variar para el ancho y el alto, (mi resolución de
video es de 800 x 600, en caso de tener una resolución diferente hay que cambiar estos
valores). En la línea (5) se usa la
función GDI SetPixel() cuyos parámetros son los puntos x e y donde se dibujará el
píxel y el otro parámetro es el color del mismo. Para el color usamos la función RGB()
donde los parámetros representan el grado de rojo, verde y azul, entre 0 y 255. La
intensidad de verde y azul la dejamos en 0 y el rojo será el producto de las coordenadas
x e y lo que da un efecto interesante llamado tramas moiré.
La última
línea libera la referencia al objeto CDC del
escritorio, esto es importante hacerlo ya que ocupa memoria.
Antes de ejecutar la aplicación, hay que hacer otra cosa importante,
que es “limpiar” la pantalla cuando la aplicación termine, porque sino todo el
escritorio quedará dibujado de forma permanente hasta que se pulse F5 que se encarga en
Windows de re-dibujar. Entonces utilizaremos el evento, (mensaje), WM_DESTROY que se
ejecuta cuando la aplicación termina, para re-dibujar el escritorio y dejar todo como
antes.
Hay que
agregar el mensaje WM_DESTROY, para esto pulse CTRL.+ W para acceder a ClassWizard. Allí
en Objects Ids seleccione CGraf1Dlg y en messages WM_DESTROY, luego pulse Add Function y
luego Edit Code, se editará el mensaje OnDestroy(), allí escriba:
void
CGraf1Dlg::OnDestroy()
{
CDialog::OnDestroy();
CWnd* pEscritorio;
pEscritorio=GetDesktopWindow();
pEscritorio->RedrawWindow(NULL,NULL,
RDW_ERASE+RDW_INVALIDATE+RDW_ALLCHILDREN+RDW_ERASENOW);
Nuevamente
se toma el manejador del escritorio con un puntero CWnd y luego se usa la función
RedrawWindow(). Los dos primer parámetros hacen referencia
a la región a re-dibujar, como en nuestro caso pretendemos re-dibujar todo el
escritorio estos parámetros serán NULL y el tercer parámetro son combinaciones de
constantes que indican como se re-dibujará, RDW_ERASE va
junto con RDW_INVALIDATE donde está última si el segundo parámetro es NULL,
(nuestro caso), indica que se re-dibujará todo incluyendo las aplicaciones descendientes
tal como lo especifica RDW_ALLCHILDREN.
Pruebe
escribiendo este código en el mensaje WM_MOUSEMOVE y obtendrá un prototipo de salva
pantalla.

Para cerrar la aplicación tendrá que adivinar donde está el botón Aceptar detrás del dibujo.
En caso de
querer usar como objeto de “salida” el diálogo lea el siguiente ejemplo de este
capítulo.
Resumiendo:
Las funciones gráficas de Windows forman parte de una librería llamada GDI, a la cual se puede acceder desde Visual C++ haciendo uso de la clase CDC que es la clase principal para el uso de contextos de dispositivos (pantalla, impresora, casco de realidad virtual, etc).
No hay que
olvidarse de re-dibujar el escritorio por ejemplo en el evento WN_DESTROY para así tener
todo lindo como antes.
Descargar archivos fuentes del ejemplo: graf1.zip
Dibujar
en un diálogo cliente:
En el
ejemplo anterior dibujamos sobre el escritorio utilizando la clase principal para definir
un contexto de dispositivo, CDC. Usando esta misma clase también se puede establecer como
contexto de dispositivo un diálogo, al cual se le llama “cliente”. En este caso
no haría falta obtener ningún manejador de ventana por medio de una función, pues el
puntero a un diálogo activo ya se encuentra referenciado con la palabra clave this, por lo que sólo haría falta definir el puntero CDC y
obtenerlo desde el cliente:
De esta forma puede uno, luego, usar la función SetPixel() o cualquier otra del puntero pDC y el efecto se producirá en toda la ventana del diálogo, incluyendo la barra donde está el título de la ventana y los botones de cerrar, maximizar y minimizar, (para que el dibujo se realice sólo dentro del diálogo; en lugar de GetWindowDC() habría que utilizar GetDC()).
Bien, VC++ nos da otra posibilidad más de hacer lo mismo usando una
clase descendiente de CDC que se llama CClientDC y se dedica pura y exclusivamente a
definir como DC el diálogo cliente (sin incluir la barra de título).
Para
probar esto con un ejemplo cree un proyecto MFC basado en diálogo con el nombre
“graf2” y realice los cambios en el diálogo de manera tal que quede exactamente
igual que el del ejemplo anterior, o sea con un botón Aceptar y el otro Dibujar, con
ID=IDC_DIBUJAR.
{
// TODO: Add your control notification handler code
here
//Se
construye un DC cliente a partir de la ventana de diálogo
CClientDC
dlgDC(this); (1)
int x,y;
for(y=0;
y<=200; y++)
dlgDC.SetPixel (x,y,RGB(x*y,0,0)); (2)
¡Listo!,
simplemente se crea el DC para el diálogo según la sentencia (1), (se define y se
crea todo de un saque). Luego con la variable dlgDC tengo acceso a todas las funciones
gráficas (2) y el efecto se produce sobre el diálogo.
Aquí no es necesario liberar la memoria utilizada usando ReleaseDC(), ni limpiar nada, pues al descargar el diálogo se soluciona el tema.
Ejecute el
programita y obtendrá una salida como la siguiente:

Verá que el dibujo está limitado al interior del diálogo, en caso de querer dibujar sobre la barra de título hay que usar la clase CDC tal como se explicó al principio de este ejemplo.
Resumiendo:
Se puede dibujar sobre un diálogo, (cliente), usando la clase CClientDC, construyendo el objeto DC simplemente al pasarle el puntero del diálogo cliente (this).
La clase CDC es la clase principal para crear un contexto de dispositivo.