Variables a editar en los cuadros de diálogo

Quizás has notado que a nuestro programa le falta algo.

Efectivamente, podemos introducir y modificar texto en el cuadro de diálogo, pero no podemos asignar valores iniciales al control de edición ni tampoco podemos hacer que la aplicación tenga acceso al texto introducido por el usuario.

Lo primero que tenemos que tener es algún tipo de variable que puedan compartir los procedimientos de ventana de la aplicación y el del diálogo. En nuestro caso se trata sólo de una cadena, pero según se añadan más parámetros al cuadro de edición, estos datos pueden ser más complejos, así que usaremos un sistema que nos valdrá en todos los casos.

Se trata de crear una estructura con todos los datos que queremos que el procedimiento de diálogo comparta con el procedimiento de ventana:

typedef struct stDatos {
   char Texto[80];
} DATOS;

Lo más sencillo es que estos datos sean globales, pero no será buena idea ya que no es buena práctica el uso de variables globales.

Tampoco parece muy buena idea declarar los datos en el procedimiento de ventana, ya que este procedimiento se usa para todas las ventanas de la misma clase, y tendríamos que definir los datos como estáticos.

Pero recordemos que tenemos un modo de pasar parámetros al cuadro de diálogo, usando la función DialogBoxParam, a través del parámetro lParam.

Aunque esta opción parece que nos limita a valores enteros, y sólo permite pasar valores al procedimiento de diálogo, en realidad se puede usar para pasar valores en ambos sentidos, bastará con enviar un puntero en lugar de un entero.

Para ello haremos un casting del puntero al tipo LPARAM. Dentro del procedimiento de diálogo haremos otro casting de LPARAM al puntero.

Esto nos permite declarar la variable que contiene los datos dentro del procedimiento de ventana, en este caso, de forma estática.

static DATOS Datos;
...
   DialogBoxParam(hInstance, "DialogoPrueba", hwnd, DlgProc, (LPARAM)&Datos);

En el caso del procedimiento de diálogo:

    static DATOS *Datos;
...
        case WM_INITDIALOG:
           Datos = (DATOS *)lParam;

Daremos valores iniciales a las variables de la aplicación, dentro del procedimiento de ventana, al procesar el mensaje WM_CREATE:

        case WM_CREATE:
           /* Inicialización de los datos de la aplicación */
           strcpy(Datos.Texto, "Inicial");

Iniciar controles edit

Ahora tenemos que hacer que se actualice el contenido del control edit al abrir el cuadro de diálogo.

El lugar adecuado para hacer esto es en el proceso del mensaje WM_INITDIALOG:

        case WM_INITDIALOG:
           SendDlgItemMessage(hDlg, ID_TEXTO, EM_LIMITTEXT, 80, 0L);
           Datos = (DATOS *)lParam;
           SetDlgItemText(hDlg, ID_TEXTO, Datos->Texto);
           SetFocus(GetDlgItem(hDlg, ID_TEXTO));
           return FALSE;

Hemos añadido dos llamadas a dos nuevas funciones del API. La primera es a SendDlgItemMessage, que envía un mensaje a un control. En este caso se trata de un mensaje EM_LIMITTEXT, que sirve para limitar la longitud del texto que se puede almacenar y editar en el control edit. Es necesario que hagamos esto, ya que el texto que puede almacenar nuestra estructura de datos está limitado a 80 caracteres.

También hemos añadido una llamada a la función SetDlgItemText, que hace exactamente lo que pretendemos: cambiar el contenido del texto en el interior de un control edit.

Devolver valores a la aplicación

También queremos que cuando el usuario esté satisfecho con los datos que ha introducido, y pulse el botón de aceptar, el dato de nuestra aplicación se actualice con el texto que hay en el control edit.

Esto lo podemos hacer de varios modos. Como veremos en capítulos más avanzados, podemos responder a mensajes que provengan del control cada vez que cambia su contenido.

Pero ahora nos limitaremos a leer ese contenido cuando procesemos el comando generado al pulsar el botón de "Aceptar".

        case WM_COMMAND:
           if(LOWORD(wParam) == IDOK) 
           {
              GetDlgItemText(hDlg, ID_TEXTO, Datos->Texto, 80);
              EndDialog(hDlg, FALSE);
           }
           return TRUE;

Para eso hemos añadido la llamada a la función GetDlgItemText, que es simétrica a SetDlgItemText.

Ahora puedes comprobar lo que pasa cuando abres varias veces seguidas el cuadro de diálogo modificando el texto cada vez.

Con esto parece que ya controlamos lo básico de los controles edit, pero aún hay algo más.

Añadir la opción de cancelar

Es costumbre dar al usuario la oportunidad de arrepentirse si ha modificado algo en un cuadro de diálogo y, por la razón que sea, cambia de idea.

Para eso se suele añadir un segundo botón de "Cancelar".

Empecemos por añadir dicho botón en el fichero de recursos:

DialogoPrueba DIALOG 0, 0, 118, 48
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION
CAPTION "Diálogo de prueba"
FONT 8, "Helv"
{
 CONTROL "Texto:", -1, "static", 
    SS_LEFT | WS_CHILD | WS_VISIBLE, 
    8, 9, 28, 8
 CONTROL "", ID_TEXTO, "EDIT", 
    ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 
    36, 9, 76, 12
 CONTROL "Aceptar", IDOK, "BUTTON", 
    BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 
    8, 26, 45, 14
 CONTROL "Cancelar", IDCANCEL, "BUTTON", 
    BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 
    61, 26, 45, 14
}

Hemos cambiado las coordenadas de los botones, para que el de "Aceptar" aparezca a la izquierda. Además, el botón de "Aceptar" lo hemos convertido en el botón por defecto, añadiendo el estilo BS_DEFPUSHBUTTON. Haciendo eso, podemos simular la pulsación del botón de aceptar pulsando la tecla de "intro".

El identificador del botón de "Cancelar" es IDCANCEL, y está definido en Windows.h.

Ahora tenemos que hacer que nuestro procedimiento de diálogo manipule el mensaje del botón de "Cancelar".

        case WM_COMMAND:
           switch(LOWORD(wParam)) {
              case IDOK: 
                 GetDlgItemText(hDlg, ID_TEXTO, Datos->Texto, 80);
                 EndDialog(hDlg, FALSE);
                 break;
              case IDCANCEL:
                 EndDialog(hDlg, FALSE);
                 break;
           }
           return TRUE;

Como puedes ver, sólo leemos el contenido del control edit si se ha pulsado el botón de "Aceptar".

Ejemplo 5


  Nombre Fichero Fecha Tamaño Contador Descarga
D Ejemplo 5 win005.zip 2004-05-17 3048 bytes 1008

Comentarios de los usuarios (5)

Joose
2012-10-09 08:45:44

Con razon a pocos les gusta el c++ porque esta bastante complicado, parece muy potente pero el visual c++ es mucho mas sencillo

Borja
2013-02-09 13:48:02

No tengo muy claro por qué en la función EndDialog usamos FALSE como segundo parámetro. ¿Existe alguna razón para hacer esto o daría igual pasar el valor TRUE ya que no procesamos el valor de retorno?

Salvador Pozo
2013-02-25 16:21:36

Hola Borja:

En este caso da exactamente igual usar el valor TRUE o FALSE, ya que como dices, no usamos el valor de retorno.

En general, se suele retornar FALSE cuando se cierra con "Cancelar" o por un error, y TRUE cuando se cierra con "Aceptar", o sencillamente, cuando los datos del diálogo son válidos.

Hasta pronto.

Pascual
2014-10-01 21:14:55

Gracias de nuevo por el curso es superior. Un saludo.

Milton Parra
2017-08-30 19:58:48

En algunos programas veo que se pueden redimensionar objetos con click sostenido. Si quisiera redimensionar un control (Edit por ejemplo) que propiedades de esta ventana debo tener en cuenta para hacerlo?. De antemano gracias por este servicio.