<//!--tabla2-->
|
<//!--tabla1-->
<//!--fin de tabla1--> |
|
|
<//!--fin tabla2--> |
Programación
básica en Windows (III)
A lo largo del artículo
se explicará detenidamente la forma de programar uno de los
recursos más usados en Windows: las barras de desplazamiento,
las cuales, además de fáciles de crear, son un elemento
muy importante a la hora de realizar aplicaciones capaces de manejar
gran cantidad de información.
En el anterior capítulo de
este pequeño curso de C quedó explicado a fondo el código
fuente de un sencillo ejemplo de programa en C para Windows que poseía
todos los elementos básicos. Ahora que se supone el lector ha
quedado ya familiarizado con la forma en que se programa bajo este
sistema operativo, se va a pasar a detallar cómo funcionan y cómo
se programan las llamadas barras de desplazamiento, las cuales, son un
recurso muy usado en este sistema operativo y que como se verá,
son relativamente fáciles de entender y de aplicar. De esta
forma se intentarán asentar los conocimientos adquiridos por el
lector en los artículos anteriores.
Las barras de desplazamiento son uno
de los recursos más usados de Windows
BARRAS DE DESPLAZAMIENTO
Como casi todos lo usuarios de
aplicaciones saben por experiencia propia, son uno de los recursos más
utilizados en Windows. Su utilidad reside en que ofrecen al usuario la
posibilidad de acceder a gran cantidad de más información
de la que cabe en la ventana de la aplicación. Es decir, cuando
los datos sobrepasan el límite físico de las dimensiones
de la ventana, las barras de desplazamiento permiten el desplazamiento
por toda la información almacenada.
Como apreciaremos más
adelante, las aplicaciones se pueden programar para que usen de forma
individual las barras verticales, las horizontales o hacer que estén
presentes ambas simultáneamente.
Como concepto relacionado, decir
también que el cuadro de desplazamiento de una barra es el rectángulo
que aparece dentro de la propia barra de desplazamiento, y se usa para
que el usuario conozca la posición exacta de la ventana virtual
que se está viendo respecto a la ventana real imaginaria que
contiene toda la información realmente disponible.
CONSTANTE Y COMANDOS
Los comandos y constantes que se
utilizan para manejar las barras se encuentran definidos en el fichero
winuser.h. Mediante ellos resulta más intuitivo y fácil
el desarrollo de aplicaciones, evitando el manejo de valores sin
relación alguna con lo que representan. En la tabla 1 se
muestra una lista de dichos valores.
RANGO DE DESPLAZAMIENTO
Ccon este término nos estamos
refriendo a las posiciones tanto mínima como máxima que
son válidas para un cuadro de desplazamiento. El programa es
capaz de definirlos mediante enteros, aunque por defecto ya posee los
valores predeterminados 0 y 100 respectivamente
TIPOS DE BARRAS
Las barras de desplazamiento se
pueden dividir en dos clases: por un lado tenemos las barras de
desplazamiento propiamente dichas, y por otro los denominados
controles de barra. Normalmente los controles de Windows se comportan
como ventanas hijas y separadas que aparecen asociadas a la ventana
padre y que además, poseen la propiedad de ser totalmente
programables, pudiéndose colocar y dimensionar según la
necesidad del desarrollador. La barra de desplazamiento por su parte,
tiene un tamaño y posición en la ventana predeterminados
según sea el tamaño y ubicación del área
cliente de la ventana en la que se utiliza.
FUNCIONES DE LAS BARRAS
Las funciones GetScrollPos() y
SetScrollPos() se utilizan para poner u obtener el valor de la posición
dentro de una barra de desplazamiento. Ambas envían o reciben
un valor de 16 bits.
Existen dos clases de barras: los
controles y las propias barras de desplazamiento
Además de las dos
mencionadas, en Windows de 32 bits (95/98/NT) existen dos funciones
adicionales: GetScrollInfo() y SetScrollInfo(). La primera utiliza
tres parámetros: un handle de la ventana, el flag fnBar y un
puntero a una estructura. El flag fnBar puede poseer tres valores:
SB_CTL (para un control), SB_HORZ (barra de desplazamiento horizontal)
y SB_VERT (barra de desplazamiento vertical). La estructura mencionada
a la que apunta el tercer parámetro es la siguiente: typedef struct tagSCROLLINFO
{
UINT cbSize
UINT fMask;
int nMin;
int nMax;
UINT nPage;
int nPos;
int nTrackPos;
} SCROLLINFO, FAR *LPSCROLLINFO
typedef SCROLLINFO CONST FAR *LPSCROLLINFO;
Respecto a la estructura anterior, sólo
debemos mencionar que cbSize se refiere al tamaño de la propia
estructura, y fMask es una combinación de los valores: SIF_ALL,
SIF_DISABLENOSCROLL, SIF_PAGE, SIF_POS y SIF_RANGE.
Por otro lado disponemos de la función
SetScrollInfo() donde se requieren cuatro parámetros: los tres
de la anterior más un valor de tipo Boolean (TRUE o FALSE). Si
se indica el primero de estos valores se dibuja el botón del
cuadro de desplazamiento. En caso contrario dicho cuadro no hará
su aparición.
EJEMPLO DE UN PROGRAMA
Dentro del listado 1 se ha incluido
el código fuente de un programa C para Windows que hace uso de
las barras de desplazamiento. De esta manera el lector podrá
apreciar cómo se aplican todas las instrucciones relacionadas
con esta materia en un ejemplo genérico de su uso. #include <windows.h>
#include <winuser.h>
#include <stdio.h>
#include <string.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
Char szProgName[]= "ProgName";
Int WINAPI WinMain(HINSTANCE hInst,
HINSTANCE hPreIns, LPSTR. LpszCmdLine, int nCmdShow)
{
HWND hWnd;
MSG lpMsg;
WNDCLASS wcApp;
WcApp.lpszClassName = szProgName;
WcApp.hInstance = hInst;
WcApp.lpfnWndProc = WndProc;
WcApp.hCursor = LoadCursor(NULL, IDC_ARROW);
WcApp.hIcon = 0;
WcApp.lpszMenuName = 0;
WcApp.hbrBackground = GetStockObject
(LTGRAY_BRUSH);
WcApp.style = CS_HREDRAW | CS_VREDRAW;
WcApp.cbClsExtra = 0;
WcApp.cbWndExtra = 0;
If(!RegisterClass (&wcApp))
Return 0;
hWnd= CreateWindow(szProgName,
"Ejemplo 1. Un programa que usa
barras de desplazamiento", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, (HWND) NULL, (HMENU) NULL,
(HANDLE) hInst, (LPSTR) NULL);
ShowWindow(hWnd, nCmDShow);
UpdateWindow(hWnd);
While (GetMessage(&lpMsg, NULL, 0, 0))
{
TranslateMessage(&lpMsg);
DispatchMessage(&lpMsg);
}
return(lpMsg.wParam);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT messg,
WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm;
char szbuffer[160] = " ";
static int charwt, charht, HOldPos,
HNewPos, VOldPos, VNewPos, nDelta;
FILE *fp;
int i,t,ch;
SCROLLINFO si;
switch (messg)
{
case WM_CREATE:
hdc= GetDC (hWnd);
GetTextMetrics( hdc, &tm);
charwt = tm.tmAveCharWidth;
charht = tm.tmHeight + tm.
tmExternalLeading;
ReleaseDC( hWnd, hdc);
break;
case WM_HSCROLL;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = 100;
si.npage = 10;
si.nPos = HNewPos;
SetScrollInfo(hWnd, SB_HORZ, &si, FALSE);
HOldPos = si.nPos;
switch (LOWORD(wParam))
{
case SB_LINERIGHT:
nDelta = 1;
break;
case SB_LINELEFT:
nDelta = -1;
break;
case SB_PAGERIGHT:
nDelta = 15;
break;
case SB_PAGELEFT:
nDelta = -15;
break;
case SB_THUMBPOSITION
nDelta = ( HIWORD(wParam)) -
HOldPos;
break;
default:
nDelta = 0;
break;
}
if(nDelta)
{
HNewPos = HOldPos + nDelta;
if(HOldPos < si.nMin)
{
nDelta - = HOldPos - si.nMin;
HNewPos = si.nMin;
}
if (HOldPos > si.Max)
{
nDelta = - HOldPos - si.nMax;
HNewPos = si.nMax;
}
}
if(nDelta)
{
SetScrollPos(hWnd, SB_HORZ, HNewPos, TRUE);
si.nPos = HNewPos;
SetScrollInfo( hWnd, SB_HORZ; &si, FALSE);
}
InvalidateRect(hWnd, NULL, TRUE);
break;
case WM_VSCROLL:
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = 100;
si.nPage = 10;
si.nPos = VNewPos;
SetScrollInfo( hWnd, SB_VERT,
&si, FALSE);
VOldPos = si.nPos;
switch( LOWORD( wParam))
{
case SB_LINEDOWN:
nDelta = 1;
break;
case SB_LINEUP:
nDelta = -1;
break;
case SB_PAGEDOWN:
nDelta = 15;
break;
case SB_PAGEUP:
nDelta = -15;
break;
case SB_THUMBPOSITION:
nDelta = (HIWORD( wParam)) - VOldPos;
break ;
default:
nDelta = 0;
break;
}
if(nDelta)
{
VNewPos = VOldPos + nDelta;
if(VOlPos < si.nMin)
{
nDelta - = VOldPos - si.nMin;
VNewPos = si.nMin;
}
if(VOlPos > si.nMax)
{
nDelta - = VOldPos - si.nMax;
VNewPos = si.nMax;
}
}
if(nDelta)
{
SetScrollPos( hWnd, SB_VERT, VNewPos,
TRUE);
si.nPos = VNewPos;
SetScrollInfo( hWnd, SB_VERT, &si,
FALSE);
}
InvalidateRect( hWnd, NULL, TRUE);
break;
case WM_PAINT:
hdc = BeginPaint ( hWnd, &ps);
// Funciones CGI
t = 0;
if (( fp= fopen("c:/texto.doc", "r"))
!= NULL)
{
while(!feof(fp))
{
ch = fgetc(fp);
i = 0;
while((ch!='\n') && (ch!= EOF))
{
szBuffer[i] = (char)ch;
ch = fgetc(fp);
i++;
}
textOut( hdc, -charwt *(HNewPos),
charht *(t - VNewPos),
szBuffer,i);
t++;
}
}
fclose( fp);
// Fuciones de CGI antes:
ValidateRect( hWnd, NULL);
EndPaint( hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return( DefWindowProc( hWnd, messg,
wParam, lParam));
}
return(0);
}
ANÁLISIS DEL EJEMPLO
Como se puede observar en el código
fuente con el que estamos trabajando, este ejemplo necesita para
funcionar correctamente un fichero de texto denominado texto.doc que
el programa se encargará de abrir y cuyo contenido podrá
ser visualizado mediante las barras de desplazamiento que previamente
han sido programadas para la ventana.
El código correspondiente a
la carga del fichero no supone ningún problema de entender ya
que no incluye ninguno de los conceptos específicos de la
programación bajo Windows y básicamente es código
C típico del que cualquier programador medio ya ha usado en
alguna ocasión.
La apertura del fichero de texto se
hace de forma normal, o sea, se indica el path u filename del mismo,
de forma que se obtiene un handle al mismo. A partir de aquí
podemos ya leer su contenido.
Un control normalmente aparece como
una ventana hija dentro de una ventana padre
Para poder acceder a su contenido se
ha creado un bucle bidimensional donde se leen los caracteres del
fichero por líneas (al final de cada una de las cuales ésta
se escribe en el búffer de pantalla) y dura infinitamente hasta
que se lea el carácter de fin de fichero (OEF), momento en que
el bucle se da por finalizado.
Realizada la lectura del fichero, se
cierra.
Otro aspecto que se puede observar
consiste en la aparición de una función nueva denominada
GetTextmetrix() que no había sido explicada hasta ahora. Se
utiliza para consultar y manejar todos los valores relacionados con
los tipos de fuentes empleados por el programa, lo cual, debido a la
extensión del tema, se tratará en el próximo artículo
con mayor profundidad.
WM_HSCROLL y WM_VSCROLL se utilizan
en el programa para que se puedan procesar los mensajes generados por
Windows relacionados con las barras de desplazamiento. Como se puede
comprobar, los dos segmentos de código que tratan las dos
barras, tanto la horizontal como la vertical, son muy parecidos.
El código necesario para
gestionar la barra horizontal y vertical es prácticamente el
mismo
Una vez que se ejecuta el programa,
si el usuario pulsa sobre una de las flechas de desplazamiento de la
barra vertical, Windows mandará a la aplicación un
mensaje SB_LINEUP o SB_LINEDOWN. Por otro lado, si el usuario pulsa en
algún punto de la barra que no sea el cuadro de desplazamiento,
se generará un mensaje de la clase SB_PAGEDOWN o SB_PAGEUP, que
el programa ejecutará como un desplazamiento de 15 líneas
del texto ya sea hacia abajo o hacia arriba.
En todos los casos de desplazamiento
sobre alguna de las barras, el mensaje se interpreta, se cambia el
valor de nDelta y se actualizan todas aquellas posiciones de los
cuadros de desplazamiento.
MÁS PRUEBAS
Como se dijo al final del artículo
anterior de esta serie, una vez se comprende el funcionamiento de cada
parte del código fuente dado de ejemplo, el usuario puede
empezar a practicar la inserción de líneas de código
para practicar funciones gráficas. Se caracterizan por su
sencillez de uso, aunque es necesario disponer de la información
técnica sobre el número y función de los parámetros
utilizados en cada una de ellas.
Finalizamos con algunos ejemplos con
los que podreis practicar y que detallamos a lo largo del listado 2. 1. Dibujar un rectángulo:
Rectangle(hdc, 25,300,130,375);
TextOut(hdc, 50, 325, "Rectángulo", 16);
2. Dibujar un círculo:
Ellipse(hdc, 375, 75, 525, 225);
TextOut(hdc, 435, 190, "Círculo", 16);
3. Dibujar una elipse:
Ellipse(hdc, 275, 300, 200, 250);
TextOut(hdc, 450,190, "Elipse",16);
4.Dibujar una línea diagonal y un texto:
MoveToEx(hdc, 20, 20 ,NULL);
LineTo(hdc, 80, 80);
TextOut (hdc, 50, 25, "<-línea diagonal",16);
|