[ contenidos | #winprog ]

Diálogos de Modelamiento

Ejemplo: dlg_two

[images/dlg_two.gif]

Ahora echémosle un vistazo a CreateDialog( ), la hermana de DialogBox( ). La diferencia es que mientras DialogBox() implementa su propio bucle de mensajes y no retorna hasta que el diálogo sea cerrado, CreateDialog() actúa de manera mas parecida a una ventana creada con CreateWindowEx(), la cual retorna inmediatamente y depende de tu bucle de mensajes enviar los mensajes a tu ventana principal. Esto es llamado Modelamiento mientras que DialogBox() crea diálogos Modales.

Podemos crear el recurso diálogo de la misma manera que lo hicimos en el ejemplo anterior, también debemos asignar el estilo extendido "Tool Window" (ventana de herramientas) para darle a la barra de titulo el típico título pequeño de las barras de herramientas. El recurso diálogo que he creado es el siguiente:

IDD_TOOLBAR DIALOGEX 0, 0, 98, 52
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
EXSTYLE WS_EX_TOOLWINDOW
CAPTION "My Dialog Toolbar"
FONT 8, "MS Sans Serif"
BEGIN
    PUSHBUTTON      "&Press This Button",IDC_PRESS,7,7,84,14
    PUSHBUTTON      "&Or This One",IDC_OTHER,7,31,84,14
END

Habrás observado que el editor de recursos ha reemplazado DIALOG con DIALOGEX indicando que queremos asignar un EXSTYLE en nuestro diálogo.

A continuación, queremos crear el diálogo cuando el programa se ejecuta y ademas queremos que el diálogo sea visible, por lo tanto hacemos esto en WM_CREATE. También queremos declarar una varialbe global para almacenar el handle retornado por CreateDialog( ), que usaremos luego. DialogBox( ) no retorna un handle debido a que cuando éste retorna, el diálogo ha sido destruido.

HWND g_hToolbar = NULL;
    case WM_CREATE:
        g_hToolbar = CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_TOOLBAR),
            hwnd, ToolDlgProc);
        if(g_hToolbar != NULL)
        {
            ShowWindow(g_hToolbar, SW_SHOW);
        }
        else
        {
            MessageBox(hwnd, "CreateDialog returned NULL", "Warning!",  
                MB_OK | MB_ICONINFORMATION);
        }
    break;

Es una buena idea chequear siempre los valores de retorno, y si son válidos (distintos de NULL) mostramos la ventana usando ShowWindow(), con DialogBox( ) esto no es necesario debido a que el sistema llama a ShowWindow() por nosotros.

Ahora necesitamos un Dialog Procedure para nuestra barra de herramientas.

BOOL CALLBACK ToolDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
    switch(Message)
    {
        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case IDC_PRESS:
                    MessageBox(hwnd, "Hi!", "This is a message", 
                        MB_OK | MB_ICONEXCLAMATION);
                break;
                case IDC_OTHER:
                    MessageBox(hwnd, "Bye!", "This is also a message", 
                        MB_OK | MB_ICONEXCLAMATION);
                break;
            }
        break;
        default:
            return FALSE;
    }
    return TRUE;
}

La mayoría de las reglas de procesamiento de mensajes se aplican a los diálogos creados con CreateDialog( ) y al igual que con DialogBox( ) no llamamos a DefWindowProc( ), retornamos FALSE en los mensajes que no procesamos y TRUE en los que si lo hacemos.

Un cambio es que, en los diálogos de modelamiento, no llamamos a EndDialog( ), simplemente usamos DestroyWindow( ) como lo hacíamos con las ventanas regulares. En este caso, destruímos el diálogo cuando la ventana principal es destruída. En el WndProc() de la ventana principal...

    case WM_DESTROY:
        DestroyWindow(g_hToolbar);
        PostQuitMessage(0);
    break;

Por último, pero no menos importante, queremos ser capaz de mostrar y ocultar nuestra barra de herramientas cada vez que el usuario lo desee, por lo tanto agregamos dos comandos al menú y los procesamos así:

    case WM_COMMAND:
        switch(LOWORD(wParam))
        {   
            case ID_DIALOG_SHOW:
                ShowWindow(g_hToolbar, SW_SHOW);
            break;
            case ID_DIALOG_HIDE:
                ShowWindow(g_hToolbar, SW_HIDE);
            break;
            //... other command handlers
        }
    break;

Hasta aquí, debes ser capaz de crear tu propio menú usando el editor de recursos o manualmente, si no es así (como pasa siempre) puedes observar el ejemplo dlg_two que viene junto a este tutorial.

Cuando ejecutes el programa, debes ser capaz de acceder a la ventana de diálogo y a la ventana pricipal al mismo tiempo.

Si ejecutaste el programa e intentaste correr el foco entre los dos botones usando la tecla tab, probablemente habrás observado que esto no funciona, como asi tampoco funciona activar los botones presionando Alt-O o Alt-P. ¿Por que no? DialogBox() implementa su propio bucle de mensajes y procesa estos eventos por default, pero CreateDialog( ) no lo hace. Podemos hacerlo nosotros, llamando a IsDialogMessage( ) en nuestro bucle de mensajes, el cual hará los procesos por default en lugar nuestro.

    while(GetMessage(&Msg, NULL, 0, 0))
    {
        if(!IsDialogMessage(g_hToolbar, &Msg))
        {
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
        }
    }

Aqui, primero pasamos el mensaje a IsDialogMessage( ), si el mensaje está destinado a nuestra barra de herramientas (indicado por el handle que le pasamos) el sistema realizará los procesamientos por default y retornará TRUE. Si es el caso en que los mensajes ya han sido procesados, entonces no llamamos a TransalateMessage( ) o DispatchMessage( ) y si el mensaje es para otra ventana, lo procesamos de la manera usual.

Es importante notar que IsDialogMessaje( ) también puede ser usado con ventanas que no son diálogos, con la intención de darles el comportamiento de un diálogo. Recuerda que un diálogo es una ventana y la mayoría (por no decir todas) de las API's para diálogos funcionarán correctamente en cualquier ventana.

Un aspecto para destacar es ¿que pasa si tienes mas de una barra de herramientas? Bien, una posible solución es tener una lista (o un arreglo) y ciclar en nuestro bucle de mensajes pasando cada handle a IsDialogMessage( ) hasta que encontremos el correcto y si no lo es, hacemos el procesamiento regular. Este es un problema de programación genérico y no uno que esté relacionado con Win32, por lo tanto se deja como ejercicio para el lector.


Copyright © 1998-2003, Brook Miles (theForger). All rights reserved.

Versión en Español: Federico Pizarro - 2003