Tutorial 18: Controles Comunes

Aprenderemos qué son los contrioles comunes y cómo usarlos. Este tutorial sólo será una introducción rápida a ellos.

Bajar el ejemplo de código fuente aquí.

Teoría:

Windows 95 viene con varias ampliaciones de la interface de usuario sobre Windows 3.1x. Esas ampliaciones enriquecen la GUI. Algunas de ellas eran apliamente usadas antes de que Windows 95 llegara a los almacenes, tales como la barra de estado [status bar], barras de herramientas etc. Los programadores tenían que escribir el código para estas ampliaciones. Ahora Microsoft las ha incluido con Windows 9x y NT. Aprenderemos sobre ellas aquí.

Estos son los nuevos controles:

Ya que hay muchos, cargarlos todos en memoria y registrarlos sería un despilfarro de recursos. Todos ellos, con excepción del control "rich edit", están almacenados en comctl32.dll, desde donde las aplicaciones pueden cargarlas cuando se quiera usarlos. El control "rich edit" reside en su propia dll, richedXX.dll, porque es muy complicado y debido a que es más grande que su brethren.

Puedes cargar comctl32.dll incluyendo una llamada a InitCommonControls en tu programa.

InitCommonControls es una función en comctl32.dll, así que refiriéndola en cualquier parte del código de tu programa hará que el cargador de archivos PE cargue comctl32.dll cuando corra tu programa.No tienes que ejecutarla , sólo inclúyela en algún lugar de tu código. ¡Esta función no hace NADA! Su unica instrucción es "ret". Su único propósito es incluir la referencia a comctl32.dll en la sección de importación de manera que el caragador de archivos PE lla cargue cada vez que el programa sea caragado. El verdaero canalllo de batalla es el punto de entrada de la fucnión en la DLL que registra todas las clases de controles comunes cuando es cargada la dll. Los controles comunes son creados sobre la base de esas clases así como los controles de ventana hija tales como "edit", "listbox", etc.
El control "rich edit" es otra cosa. Si quieres usarlo, tienes que llamar a a LoadLibrary para caragarlo explícitamente y luego llamar a FreeLibrary para descargarlo.

Ahora aprenderemos cómo crearlos. Puedes usar un editor de recursos para incorporarlos en las cajas de diálogo o puedes crearlos túmismo. Casi todos los controles comunes se crean llamando a CreateWindowEx o a CreateWindow, pasando le el nombre de la clse del control. Algunos controles comunes tienen funciones de creación específicas, sin embargo, they are just wrappers around CreateWindowEx para facilitar la creación de esos controles. Las funciones de creación específicas existenetes son:

Con el fin de crear controles comunes, tienes que saber sus nombres de clase. Aparecen en la siguiente lista:
 

Nombre de la Clase

Control Común

ToolbarWindow32 Toolbar
tooltips_class32 Tooltip
msctls_statusbar32 Status bar
SysTreeView32 Tree view
SysListView32 List view
SysAnimate32 Animación
SysHeader32 Header
msctls_hotkey32 Hot-key
msctls_progress32 Progress bar
RICHEDIT Rich edit
msctls_updown32 Up-down
SysTabControl32 Tab

Los controles property sheets, property pages y image list tienen sus propias funciones de creación específicas. Los controles drag list son cajas de listas [listbox] potenciadas así que no tienen su propia clase. Los nombres de las clases de arriba pueden ser verificados chequeando el guión de recursos generado por el editor de recursos de Visual C++. Difieren de los nombres de clase que aparecen en la lista de la referencia de la api de Windows de Borland y de la lista del libro Programación en Windows 95 de Charles Petzold. La lista de arriba es la precisa.

Esos controles comunes pueden usar estilos de ventana generales tales como WS_CHILD, etc. También tienen sus estilos específicos tales como TVS_XXXXX para el control tree view, LVS_xxxx para el control list view, etc. La referencia de la api de Win32 es tu mejor amigo en este punto.

Ahora que sabemos cómo crear controles comunes, podemos ver el método de comunicación entre los controles comunes y sus padres. A diferencia de los controles de ventanas hija, los controles comunes no se comunican con el padre a través de WM_COMMAND. En vez de eso, ellos envían mensajes WM_NOTIFY a la ventana padre cuando algunos eventos interesantes ocurren con ellos. El padre puede controlar al hijo enviándoles mensajes. También hay muchos mensajes para los nuevos controles. Deberías consultar tu referencia de la api de win32 para más detalles.

Vamos ver los controles de barra de progreso [progress bar] y barra de estado [status bar] en el siguiente ejemplo.

Código muestra :

 .386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD

.const
IDC_PROGRESS equ 1            ; IDs de los controles
IDC_STATUS equ 2
IDC_TIMER  equ 3

.data
ClassName  db "CommonControlWinClass",0
AppName    db "Common Control Demo",0
ProgressClass  db "msctls_progress32",0       ; el nombre de la clase de la barra de progreso
Message  db "Finished!",0
TimerID  dd 0

.data?
hInstance  HINSTANCE ?
hwndProgress dd ?
hwndStatus dd ?
CurrentStep dd ?
.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
    invoke ExitProcess,eax
    invoke InitCommonControls

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND

    mov   wc.cbSize,SIZEOF WNDCLASSEX
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInst
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_APPWORKSPACE
    mov   wc.lpszMenuName,NULL
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
           CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
           hInst,NULL
    mov   hwnd,eax
    .while TRUE
         invoke GetMessage, ADDR msg,NULL,0,0
        .BREAK .IF (!eax)
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
    .endw
    mov eax,msg.wParam
    ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .if uMsg==WM_CREATE
         invoke CreateWindowEx,NULL,ADDR ProgressClass,NULL,\
            WS_CHILD+WS_VISIBLE,100,\
            200,300,20,hWnd,IDC_PROGRESS,\
            hInstance,NULL
        mov hwndProgress,eax
        mov eax,1000               ; the lParam of PBM_SETRANGE message contains the range
        mov CurrentStep,eax
        shl eax,16                   ; the high range is in the high word
        invoke SendMessage,hwndProgress,PBM_SETRANGE,0,eax
        invoke SendMessage,hwndProgress,PBM_SETSTEP,10,0
        invoke CreateStatusWindow,WS_CHILD+WS_VISIBLE,NULL,hWnd,IDC_STATUS
        mov hwndStatus,eax
        invoke SetTimer,hWnd,IDC_TIMER,100,NULL        ; crear un temporizador
        mov TimerID,eax
    .elseif uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
        .if TimerID!=0
            invoke KillTimer,hWnd,TimerID
        .endif
    .elseif uMsg==WM_TIMER                                                              ; cuando ocurre un evento de temporizador
        invoke SendMessage,hwndProgress,PBM_STEPIT,0,0 ; incrementar el progreso en la barra de progreso
        sub CurrentStep,10
        .if CurrentStep==0
            invoke KillTimer,hWnd,TimerID
            mov TimerID,0
            invoke SendMessage,hwndStatus,SB_SETTEXT,0,addr Message
            invoke MessageBox,hWnd,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
            invoke SendMessage,hwndStatus,SB_SETTEXT,0,0
            invoke SendMessage,hwndProgress,PBM_SETPOS,0,0
        .endif
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret
WndProc endp
end start

Análisis:

Deliberadamente pongo InitCommonControls después de ExitProcess para demostrar que InitCommonControls está justo ahí para poner una referencia a comctl32.dll en la sección de importación. Como puedes ver, los controles comunes trabajan incluso si InitCommonControls no se ejecuta.

Aquí es donde creamos el control común. Nota que esta llamada a CreateWindowEx contiene hWnd como manejador de la ventana padre. También especifica un ID de control para identificar este control. Sin embargo, como tenemos un manejador del control de ventana, este ID no es usado. Todos los controles de ventana hija debe tener el estilo WS_CHILD.

Después de que es creada la barra de progreso, podemos establecer su rango. El rango por defecto es desde 0 a 100. Si no estás satisfecho con esto, puedes especificar tu propio rango con el mensaje PBM_SETRANGE. lParam de este parámetro contiene el rango, el máximo rango está en la palabra alta y el mínimo está en la palabra baja. Puedes especificar cuánto toma un paso [how much a step takes] usando el mensaje PBM_SETSTEP. El ejemplo lo establece a 10 lo cual significa que cuando tú envías un mensaje PBM_STEPIT a la barra de progreso, el indicador de progreso se incrementará por 10. También puedes poner tu indicador de posición enviando mensajes PBM_SETPOS. Este mensaje te da un control más estricto sobre la barra de progreso.

Luego, creamos la barra de estado llamando a CreateStatusWindow. Esta llamada es fácil de entender, así que no la comentaré. Después de que es creada la ventana de estado, creamos un temporizador. En este ejemplo, actualizaremos la barra de progreso en un intervalo regular de 100 ms así que debemos crear un control de teporización. Abajo está el prototipo de la función SetTimer.

hWnd : Manejador de la ventana padre
TimerID : un identificador del temporizador, nunca igual a cero. Puedes crear tu propio identificador.
TimerInterval : el intervalo del temporizador en milisegundos que deben pasar antes de que el temporizador llame al proceso del temporizador o envía un mensaje WM_TIMER
lpTimerProc : la dirección de la función del temporizador que será llamada cuando el intervalo de tiempo expire. Si este parámetro es NULL, el temporizador enviará más bien el mensaje WM_TIMER a la ventana padre.

Si esta llamada tiene éxito, regresará el TimerID. Si falla, regresa 0. Esto se debe a que el valor del ID del temporizador debe ser diferente a cero.

Cuando expira el intervalo de tiempo especificado, el temporizador envía un mensaje WM_TIMER. Aquí pondrás el código que será ejecutado. En este ejemplo, actualizamos la barra de progreso y luego chequeamos si ha sido alcanzado el límite máximo. Si ha sido alcanzado, mataremos el temporizador y luego pondremos el texto en la ventana de estado con el mensaje SB_SETTEXT. Se depliega una caja de mensaje y cuando el usuario hace click sobre OK, limpiamos el texto en la barra de estado y en la barra de progreso.


Índice

Siguiente

[Iczelion's Win32 Assembly Homepage]

n u M I T_o r's   Programming Page

Este tutorial, original de Iczelion, ha sido traducido por:   n u M I T_o r

www.000webhost.com