Introducción a la Programación en Windows
Por: Demian C. Panello demianpanello@yahoo.com.ar
Capítulo III
Indice rápido del capítulo 3:
Una aplicación Windows, desde ya no es sólo una ventana, sino que también puede contar con otros objetos como botones, listas, cuadros de edición, marcos, etc. Pero a pesar de esto, estos objetos en realidad son VENTANAS, un tanto particulares, pero ventanas al fin y al cabo.
Para poder crear un botón, se usa la misma función, (CreateWindow()), que para una ventana, la diferencia para que el botón se vea como un botón y no como una ventana está en los parámetros pasados.
Ahora veremos un ejemplo de una ventana cuyo fondo será blanco, (para variar un poco), que tendrá dos botones y un texto centrado en la misma.
Como siempre vaya al menú FILE - NEW, de la solapa PROJECTS selecciones Win32 Application y Project Name. escriba "win3".
Luego de aceptar este proyecto hay que crear un nuevo archivo .cpp, entonces vaya otra vez a FILE - NEW y de la solapa FILES seleccione C++ Source File y escriba en File Name "win3".
La base de la aplicación va a ser muy parecida a la usada en los dos capítulos anteriores, o sea, crear la ventana, el bucle de mensajes, etc. Las líneas agregadas o modificadas o nuevas para este ejemplo están resaltadas en amarillo.
Escriba entonces:
//Creación de una ventana sencilla con dos botones
//que al pulsarlos muestran un mensaje
//Autor: Demian Panello.
#include <stdio.h>
#include <windows.h>
#define NOMBRE_CLASE
"Ventana1" //Nombre de la
clase ventana a crear
#define TITULO "Ventana con
botones" //El título que mostrará la ventana
//Prototipos de funciones.
static BOOL Inicializa(HINSTANCE hInstance, int nShowCmd);
long FAR PASCAL WindowProc(HWND hwnd, UINT message, WPARAM
wParam,LPARAM lParam);
void CentraTexto(HWND ventana);
HWND hwnd;
//Variable HWND, (window handler), global que será el manejador de la ventana.
//Función principal.
int PASCAL WinMain(HINSTANCE hInstance,HINSTANCE
hPrevInstance,
LPSTR lpCmdLine, int nShowCmd)
{
MSG msg; //Variable
para los mensajes.
//Si no se puede crear la ventana retorno FALSE
if (!Inicializa(hInstance, nShowCmd))
return FALSE;
//Bucle de mensajes.
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//Esta función se encarga de definir los atributos, (propiedades),
de la ventana y además la muestra.
static BOOL Inicializa(HINSTANCE hInstance, int nShowCmd)
{
//Variable para registrar la ventana.
WNDCLASS wc;
//Propiedades de la ventana.
wc.style = CS_HREDRAW|CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra =0;
wc.cbWndExtra =0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground = (HBRUSH)
GetStockObject(WHITE_BRUSH); //Se establece un
fondo de color blanco
wc.lpszMenuName = NULL;
wc.lpszClassName =NOMBRE_CLASE; //Nombre de
la clase
RegisterClass(&wc);
//Se crea la ventana de clase "Ventana1"
hwnd=CreateWindowEx(WS_EX_APPWINDOW,NOMBRE_CLASE,
TITULO, WS_CAPTION|WS_MINIMIZEBOX|WS_SYSMENU,CW_USEDEFAULT,CW_USEDEFAULT, 300,200,
NULL,NULL,hInstance,NULL);
if (!hwnd) //Si
no se pudo crear, retorno FALSE
return FALSE;
ShowWindow(hwnd,nShowCmd); //Muestro la ventana
UpdateWindow(hwnd);
//Se dibuja la ventana,(mensaje WM_PAINT)
return TRUE;
}
//Esta función es la encargada de
procesar los mensajes que van llegando.
long FAR PASCAL WindowProc(HWND hwnd, UINT message, WPARAM
wParam,LPARAM lParam)
{
static HWND hwndBoton;
static HWND hwndBoton2;
int p;
//Verifico que mensaje llegó(estos son sólo algunos mensajes)
switch(message)
{
case
WM_PAINT: //Cuando
se pinta la ventana.
CentraTexto(hwnd);
break;
case WM_CREATE: //Se
creó la ventana
hwndBoton=
CreateWindow("button", "Botón 1",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
10,120,80,40,hwnd,0,((LPCREATESTRUCT)lParam)->hInstance ,NULL);
hwndBoton2=
CreateWindow("button", "Botón 2",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
200,120,80,40,hwnd,0,((LPCREATESTRUCT)lParam)->hInstance ,NULL);
break;
case
WM_KEYDOWN: //Se
pulsó una tecla
break;
case WM_COMMAND:
if
(hwndBoton==(HWND)lParam)
MessageBox(hwnd,"Soy el botón 1","Mensaje",MB_OK+MB_ICONINFORMATION);
else
if ((hwndBoton2==(HWND)lParam))
{
MessageBox(hwnd, "Soy el botón 2", "Mensaje",
MB_OK+MB_ICONINFORMATION);
p = MessageBox(hwnd,"¿Sale de la
aplicación?","Salir",MB_YESNO+MB_ICONQUESTION);
if (p==IDYES)
PostQuitMessage(0);
}
break;
case WM_DESTROY: //Al destruirse la ventana invoco la salida
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd,message,wParam,lParam);
}
//Función CentraTexto().
void CentraTexto(HWND ventana)
{
HDC miDC;
PAINTSTRUCT ps;
char Mensa[]="Soy un ventana con botones";
miDC=GetDC(ventana);
miDC=BeginPaint(ventana,&ps);
SetTextColor(miDC,RGB(0,0,255));
TextOut(miDC,60,75 ,Mensa,sizeof(Mensa)-1);
EndPaint(ventana,&ps);
}
Para este ejemplo se agregó una función llamada CentraTexto(),
(encima de esta línea).
Esta función es llamada desde el mensaje WM_PAINT para que muestre un texto. La función TextOut() al igual que DrawText() permite imprimir un texto en el contexto de dispositivo; los parámetros de esta función son:
BOOL TextOut(
HDC hdc, // variable
del contexto de dispositivo.
int nXStart, //
coordenada x del punto de inicio.
int nYStart, //
coordenada y del punto de inicio.
LPCTSTR lpString, //
puntero a una cadena.
int cbString // cantidad de
caracteres );
Otra modificación para la ventana de este ejemplo es en la creación de la misma, en los parámetro pasados en la función CreateWindowEx(). El parámetro de estilo extendido en este ejemplo es: WS_CAPTION|WS_MINIMIZEBOX|WS_SYSMENU, para lograr una ventana con título pero con el botón de maximizar desactivado. Y además los valores para el ancho y también se fijaron a 300x200.
Bien, ahora lo que respecta a los botones.
Como los botones deberían mostrarse bien se crea la ventana, los mismos son creados precisamente al ocurrir el mensaje WM_CREATE de la ventana. En la función WindowProc() primero se definen dos variables de tipo static HWND, (static hace que la variable pueda ser accedida desde otros procedimientos), que serán los manejadores de los botones.
Y en WM_CREATE se crean los botones usando la función CreateWindow().:
hwndBoton=
CreateWindow("button", "Botón 1",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
10,120,80,40,hwnd,0, ((LPCREATESTRUCT)lParam)->hInstance ,NULL);
hwndBoton2=
CreateWindow("button", "Botón 2",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
200,120,80,40,hwnd,0, ((LPCREATESTRUCT)lParam)->hInstance ,NULL);
Los parámetros de esta función son del mismo tipo que si la usáramos para crear ventanas comunes, la variante son los valores. Los parámetros de esta función son:
lpClassName: Es el nombre de la clase. En caso de usar esta función para crear botones u otros objetos se deben usar los siguientes valores: button, combobox, edit, listbox, mdiclient, richedit, richedit_class, scrollbar, static.
lpWindowName: Es el título o texto de la ventana, boton, edit, etc.
dwStyle: Para cada objeto hay estilos muy especificos. Para un botón puden ser: BS_PUSHBUTTON, BS_RADIOBUTTON, BS_BITMAP, BS_GROUPBOX, etc, (ver la ayuda para más estilos de botones y otros estilos para otros objetos).0
x: Coordenada x del punto superior izquierdo.
y: Coordenada y del punto superior izquierdo.
nWidth: Ancho.
nHeight: Alto.
hWndParent: Un botón sería la ventana filial de la ventana donde se encuentra ubicado, aquí se especifica cual es esa ventana, en nuestro ejemplo, hwnd.
hMenu: Este valor es 0 ya que no tiene un menú un botón.
hInstance: Es un manejador a la instancia actual la cual se almacena en lParam, pero debemos convertirla a LPCREATESTRUCT que es el tipo puntero esperado por este parámetro, por eso: ((LPCREATESTRUCT)lParam)->hInstance.
lParam: Para un control este valor es NULL.
Como creamos los botones en el mensaje WM_CREATE, podemos verlos inmediatamente, ahora falta escribir el código que actúe en caso de pulsar uno de ellos.
Existe un mensaje de la ventana que precisamente permite manipular la pulsaciones de los diferentes controles que posee, este mensaje es WM_COMMAND. El mismo es enviado por Windows cada vez que pulsamos un botón, un ítem de un menú o cualquier otro objeto.
Una vez que llega este mensaje hay que detectar que objeto lo produjo. En nuestro caso no son muchos los objeto a analizar, tenemos sólo dos botones que nos interesan y la información sobre cual de los dos a sido pulsado viene guardad en la variable lParam que es un parámetro de la función WindowProc(). Entonces en este función primero chequeamos si se trata de un mensaje WM_COMMAND, en caso de ser así se verifica que botón se pulsó comparando el contenido de lParam con los manejadores de los botones, hwndBoton y hwndBoton2.
case WM_COMMAND:
if
(hwndBoton==(HWND)lParam)
//Se verifica si es el botón que dice "Botón 1"
MessageBox(hwnd,"Soy el botón 1","Mensaje",MB_OK+MB_ICONINFORMATION);
else
if ((hwndBoton2==(HWND)lParam)) //o si es el botón que dice "Botón 2"
{
MessageBox(hwnd, "Soy el botón 2", "Mensaje",
MB_OK+MB_ICONINFORMATION);
p = MessageBox(hwnd,"¿Sale de la
aplicación?","Salir",MB_YESNO+MB_ICONQUESTION);
if (p==IDYES)
PostQuitMessage(0);
}
break;
Se convierte lParam a HWND porque los manejadores son de tipo HWND.
Si se pulsó el botón que dice "Botón 1" se muestra el mensaje "Soy el botón 1" y si se pulsó el botón que dice "Botón 2" se muestra el mensaje "Soy el botón 2" y se le pregunta al usuario si quiere salir de la aplicación, por medio de la misma función MessageBox() pero en este caso con los botones "Si" y "No", y de acuerdo a la opción elegida se envía el mensaje de salida con PostQuitMessage().
Lo único que nos falta es escribir el código de la función que muestra el texto "Soy una ventana con botones" en el centro de la ventana. A esta función se la llama únicamente en el mensaje WM_PAINT, cuando la aplicación tiene que dibujarse, o bien porque acaba de crearse o bien porque acaba de descubrirse la ventana.
En esta función se declaran tres variables una de tipo HDC que será nuestro contexto de dispositivo, (ver capitulo 2), otro de tipo PAINTSTRUCT, (ver capítulo 2) y la tercera de tipo char que será el mensaje a mostrar.
Luego se toma el contexto de dispositivo de nuestra ventana y se llama a la función BeginPaint().
Lo nuevo de esta función es el uso de la función TextOut(), en lugar de DrawText(), ver definición.
