Creación de asistentes
Un asistente es un tipo de hoja de propiedades que proporciona una manera sencilla y eficaz de guiar a los usuarios a través de un procedimiento.
Los asistentes son una de las claves para simplificar la experiencia del usuario. Permiten realizar una operación compleja, como la configuración de una aplicación, y dividirla en una serie de pasos sencillos. En cada punto del proceso, puede proporcionar una explicación de lo que se necesita y mostrar los controles que permiten al usuario realizar selecciones y escribir texto.
Un asistente es realmente un tipo de hoja de propiedades. Una hoja de propiedades es fundamentalmente un contenedor para una colección de páginas, donde cada página es un cuadro de diálogo independiente. Mientras que las hojas de propiedades normales permiten al usuario acceder a cualquier página en cualquier momento, los asistentes presentan las páginas en una secuencia ordenada. En lugar de pestañas, se usan botones para navegar hacia delante y hacia atrás. El orden en el que se muestran las páginas se controla mediante la aplicación y se puede modificar en función de las entradas del usuario.
Hay dos estilos principales de asistente: el estilo anterior Wizard97 y el estilo Aero presentado en Windows Vista. Para obtener ilustraciones, consulte Acerca de las hojas de propiedades. (Un tercer estilo, con solo la marca PSH_WIZARD o PSH_WIZARD_LITE, presenta una secuencia simple de hojas de propiedades sin encabezados ni marcas de agua).
Nota
Una "marca de agua" en el contexto de los asistentes es un mapa de bits que aparece en el margen izquierdo de algunas páginas.
En la mayor parte de este documento se supone que está implementando un asistente para un sistema con la versión 5.80 o posterior de los controles comunes. Si intenta usar el estilo Wizard97 con versiones anteriores de los controles comunes, la aplicación puede crearse, pero no se mostrará correctamente. Para obtener información sobre cómo crear un asistente compatible con Wizard97 en sistemas anteriores, consulte Asistentes compatibles con versiones anteriores más adelante en este tema.
- C/C++
- Programación de la interfaz de usuario de Windows
La implementación de un asistente es similar a la de una hoja de propiedades normal. En el nivel más básico, es cuestión de establecer una de las siguientes marcas o combinaciones de marcas en la estructura PROPSHEETHEADER que define la hoja de propiedades.
Marca | Estilo |
---|---|
PSH_WIZARD | Un asistente sencillo sin encabezados ni mapas de bits. |
PSH_WIZARD_LITE | Es parecido a PSH_WIZARD, con algunas diferencias menores en el aspecto. Por ejemplo, el divisor situado encima de los botones se establece en el ancho completo de la ventana. |
PSH_WIZARD97 | Un asistente Wizard97 con encabezados (opcionales), mapas de bits de encabezado y marcas de agua. |
PSH_WIZARD | PSH_AEROWIZARD | Un asistente para Aero. Los asistentes para Aero no usan marcas de agua ni mapas de bits de encabezado. Requieren el modelo de contenedor uniproceso (STA). |
El procedimiento básico para implementar un asistente es el siguiente:
- Cree una plantilla de cuadro de diálogo para cada página.
- Defina las páginas mediante la creación de una estructura PROPSHEETPAGE para cada página. Esta estructura define la página y contiene punteros a la plantilla del cuadro de diálogo y a los mapas de bits u otros recursos.
- Pase la estructura PROPSHEETPAGE que se creó en el paso anterior a la función CreatePropertySheetPage para crear el identificador HPROPSHEETPAGE de la página.
- Defina el asistente mediante la creación de una estructura PROPSHEETHEADER para él.
- Pase la estructura PROPSHEETHEADER a la función PropertySheet para mostrar el asistente.
- Implemente procedimientos de cuadro de diálogo para cada página para controlar los mensajes de notificación de los controles de la página y los botones del asistente, y para procesar otros mensajes de Windows.
Hay dos tipos básicos de páginas del asistente: exterior e interior. Las páginas exteriores son las páginas de presentación (bienvenida) y finalización. Todas las demás son páginas interiores.
Plantillas de cuadros de diálogo de páginas exteriores
El diseño básico de las páginas de introducción y finalización es idéntico. En la ilustración siguiente se muestra una página de introducción de Wizard97, con una marca de agua de marcador de posición.
Para las páginas exteriores de Wizard97, la plantilla del cuadro de diálogo es de 317 x 193 unidades de cuadro diálogo. Ocupa todo el asistente, excepto la descripción y la banda de la parte inferior que contiene los botones Atrás, Siguiente y Cancelar. El lado izquierdo de la plantilla, que está reservado para un mapa de bits de "marca de agua", no debe contener ningún control. La marca de agua se especifica en la estructura PROPSHEETHEADER del asistente y se agrega automáticamente a la página. Debe permitir espacio para ella al diseñar la plantilla de recursos.
Al crear el mapa de bits de marca de agua, tenga en cuenta que el cuadro de diálogo puede aumentar el tamaño si, por ejemplo, el usuario elige una fuente del sistema grande. Los distintos idiomas también tienden a tener diferentes métricas de fuente. Cuando crece la página, el área reservada para la marca de agua se vuelve proporcionalmente mayor. Sin embargo, no puede cambiar el mapa de bits de marca de agua ni el mapa de bits extendido para rellenar el área más grande. En su lugar, el mapa de bits se deja en su tamaño original en la parte superior izquierda del área reservada. La parte del área reservada más grande que no está cubierta por la marca de agua se rellena automáticamente con el color del píxel superior izquierdo del mapa de bits.
Si necesita tener mapas de bits de marca de agua de distintos tamaños para métricas de fuente diferentes hay dos soluciones posibles:
- Obtenga las métricas de fuente antes de crear el asistente y especifique un mapa de bits de marca de agua de tamaño adecuado.
- No lo especifique al crear el asistente. Wizard97 dejará el área de la marca de agua en blanco. A continuación, dibuje un mapa de bits de tamaño adecuado en el área reservada para la marca de agua.
Puede colocar controles en el área situada a la derecha de la marca de agua como lo haría con un cuadro de diálogo normal. El sistema determina el color de fondo de esta área y no se requiere ninguna acción por su parte. Normalmente, se ponen dos controles estáticos en esta área. La parte superior contiene el título y usa una fuente en negrita grande (Verdana Bold de 12 puntos para Wizard97). La otra, que es para texto explicativo, usa la fuente del cuadro de diálogo estándar.
La principal diferencia entre las páginas de presentación y finalización son los botones del asistente y el texto de los controles estáticos. Normalmente, las páginas de presentación tienen un botón Siguiente y un botón Atrás, con solo el botón Siguiente habilitado. Las páginas de finalización tienen habilitado el botón Atrás y el botón Siguiente se ha reemplazado por un botón Finalizar.
Nota
En los asistentes para Aero, el botón Atrás se reemplaza por un botón de flecha en la barra de subtítulos.
Para modificar el texto del botón Finalizar, puede enviar al asistente un mensaje PSM_SETFINISHTEXT. De forma predeterminada, el botón Finalizar no incluye un acelerador de teclado. Para definir un acelerador de teclado, incluya el carácter "Y" comercial (&) en la cadena de texto que pase a PSM_SETFINISHTEXT. Por ejemplo, "&Finish" define "F" como acelerador de teclado.
Plantillas de cuadros de diálogo de páginas interiores
Las páginas interiores tienen un aspecto algo diferente del de las páginas exteriores. En la ilustración siguiente se muestra una página interior de Wizard97, con un mapa de bits de marcador de posición.
El área de encabezado de la parte superior de la página se controla mediante la hoja de propiedades, por lo que no se incluye en la plantilla. El contenido del encabezado se especifica en la estructura PROPSHEETPAGE de la página y en la estructura PROPSHEETHEADER del asistente. Dado que la página interior debe ajustarse entre el encabezado y los botones, la plantilla del cuadro de diálogo Wizard97 es de 317 x 143 unidades de cuadro de diálogo, algo más pequeña que la plantilla para las páginas exteriores.
En la ilustración siguiente se muestra un asistente para Aero que se creó a partir de la misma plantilla.
Después de crear las plantillas del cuadro de diálogo y los recursos relacionados, como mapas de bits y tablas de cadenas, puede crear las páginas de la hoja de propiedades. El procedimiento es similar al de las hojas de propiedades estándar. En primer lugar, rellene los miembros adecuados de una estructura PROPSHEETPAGE. (Algunos miembros son específicos de los asistentes). A continuación, llame a la función CreatePropertySheetPage para crear el identificador HPROPSHEETPAGE de la página.
Las siguientes marcas relacionadas con el asistente se pueden establecer en el miembro dwFlags de la estructura PROPSHEETPAGE.
Marca | Descripción |
---|---|
PSP_HIDEHEADER | Establezca esta marca para páginas exteriores en Wizard97. No se muestra el encabezado y se puede mostrar una marca de agua. |
PSP_USEHEADERTITLE | Establezca esta marca para las páginas interiores para colocar un título en el área de encabezado de Wizard97 o en la parte superior del área de cliente en un asistente para Aero. |
PSP_USEHEADERSUBTITLE | Establezca esta marca para las páginas interiores para colocar un subtítulo en el área de encabezado en Wizard97. |
Si ha establecido PSP_USEHEADERTITLE o PSP_USEHEADERSUBTITLE, asigne el texto del título y el subtítulo a los miembros pszHeaderTitle y pszHeaderSubtitle, respectivamente. Al asignar cadenas de texto a los miembros de las estructuras PROPSHEETPAGE y PROPSHEETHEADER, puede asignar un puntero de cadena o usar la macro MAKEINTRESOURCE para asignar un valor desde un recurso de cadena. El recurso de cadena se carga desde el módulo especificado en el miembro hInstance de la estructura PROPSHEETHEADER del asistente.
Al llamar a CreatePropertySheetPage para crear una página, asigne el resultado a un elemento de una matriz de identificadores HPROPSHEETPAGE. Esta matriz se usa al crear la hoja de propiedades. El índice de la matriz del identificador de una página determina el orden predeterminado en el que se muestra. Después de crear el identificador HPROPSHEETPAGE de una página, puede reutilizar la misma estructura PROPSHEETPAGE para crear la página siguiente asignando nuevos valores a los miembros pertinentes.
Una manera alternativa de crear páginas es usar estructuras PROPSHEETPAGE independientes para cada página y crear una matriz de estructuras. Esta matriz se usa en lugar de una matriz de identificadores HPROPSHEETPAGE al crear la hoja de propiedades. El uso de estructuras PROPSHEETPAGE independientes elimina la necesidad de llamar a CreatePropertySheetPage, pero usa más memoria. De lo contrario, no hay ninguna diferencia significativa entre los dos enfoques.
En el ejemplo siguiente se define una página interior de Wizard97 asignando valores a una estructura PROPSHEETPAGE. En este ejemplo, todos los identificadores de recursos identifican el título, el subtítulo y la plantilla del cuadro de diálogo de la página. A continuación, se llama a la función CreatePropertySheetPage para crear el identificador HPROPSHEETPAGE de la página. Dado que será la segunda página que aparecerá, el identificador se asigna a la matriz de identificadores, ahpsp, con un índice de 1.
// g_hInstance is the global HINSTANCE of the application.
// IntPage1DlgProc is the dialog procedure for this page.
// ahpsp is an array of HPROPSHEETPAGE handles.
PROPSHEETPAGE psp = { sizeof(psp) };
psp.hInstance = g_hInstance;
psp.dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
psp.lParam = (LPARAM) &wizdata;
psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_TITLE1);
psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_SUBTITLE1);
psp.pszTemplate = MAKEINTRESOURCE(IDD_INTERIOR1);
psp.pfnDlgProc = IntPage1DlgProc;
ahpsp[1] = CreatePropertySheetPage(&psp);
Al crear una página, puede asignarle datos personalizados mediante el miembro lParam de la estructura PROPSHEETPAGE, normalmente asignando un puntero a una estructura definida por el usuario.
Cuando se selecciona la página por primera vez, su procedimiento de cuadro de diálogo recibe un mensaje WM_INITDIALOG. El valor lParam del mensaje apunta a una copia de la estructura PROPSHEETPAGE de la página, desde la que puede recuperar los datos personalizados. A continuación, puede almacenar estos datos para usarlos en los mensajes posteriores mediante SetWindowLongPtr con GWL_USERDATA como parámetro de índice. Varias páginas pueden tener un puntero a los mismos datos y cualquier cambio en los datos realizados por una página estará disponible para las otras páginas en sus procedimientos de diálogo.
Al igual que con las hojas de propiedades normales, debe definir la hoja de propiedades del asistente rellenando los miembros de una estructura PROPSHEETHEADER. Esta estructura permite especificar las páginas que componen el asistente y el orden predeterminado en el que se muestran, junto con varios parámetros relacionados. A continuación, para iniciar el asistente llame a la función PropertySheet.
En el estilo Wizard97, se omite el miembro pszCaption de la estructura PROPSHEETHEADER. En su lugar, el asistente muestra el subtítulo que se especifica en la plantilla del cuadro de diálogo de la página actual. Si la plantilla no tiene un subtítulo, se muestra el subtítulo de la página anterior. Por lo tanto, para mostrar el mismo subtítulo en todas las páginas, especifique el subtítulo de la plantilla para la página introductoria.
En el estilo del asistente para Aero, el subtítulo del cuadro de diálogo se toma de pszCaption.
Si ha creado una matriz de identificadores HPROPSHEETPAGE para las páginas, asigne la matriz al miembro phpage. Si en vez de eso ha creado una matriz de estructuras PROPSHEETPAGE, asigne la matriz al miembro ppsp y establezca la marca PSH_PROPSHEETPAGE en el miembro dwFlags.
En el ejemplo siguiente se asignan valores a psh, una estructura PROPSHEETHEADER y se llama a la función PropertySheet para iniciar el asistente. El asistente de estilo Wizard97 tiene tanto gráficos de marca de agua y de encabezado, especificados mediante sus identificadores de recursos. La matriz ahpsp contiene todos los identificadores HPROPSHEETPAGE y define el orden predeterminado en el que se muestran.
// g_hInstance is the global HINSTANCE of the application.
// ahpsp is an array of HPROPSHEETPAGE handles.
PROPSHEETHEADER psh = { sizeof(psh) };
psh.hInstance = g_hInstance;
psh.hwndParent = NULL;
psh.phpage = ahpsp;
psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK | PSH_HEADER;
psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER);
psh.nStartPage = 0;
psh.nPages = 4;
PropertySheet(&psh);
Cada página del asistente necesita un procedimiento de cuadro de diálogo para procesar los mensajes de Windows, especialmente las notificaciones de sus controles y el asistente. Los tres mensajes que casi todos los asistentes deben poder controlar son WM_INITDIALOG, WM_DESTROY y WM_NOTIFY.
El mensaje WM_NOTIFY se recibe antes de que aparezca la página y cuando se hace clic en cualquiera de los botones del asistente. El parámetro lParam del mensaje es un puntero a una estructura de encabezado NMHDR. El identificador de la notificación está incluido en el miembro de código de la estructura. Las cuatro notificaciones que la mayoría de los asistentes necesitan controlar son las siguientes.
Código | Descripción |
---|---|
PSN_SETACTIVE | Se envía antes de que se muestre la página. |
PSN_WIZBACK | Se envía cuando se hace clic en el botón Atrás. |
PSN_WIZNEXT | Se envía cuando se hace clic en el botón Siguiente. |
PSN_WIZFINISH | Se envía cuando se hace clic en el botón Finalizar. |
Cuando una página está a punto de aparecer por primera vez, su procedimiento de cuadro de diálogo recibe un mensaje WM_INITDIALOG. Controlar este mensaje permite al asistente realizar las tareas de inicialización necesarias, como almacenar datos personalizados o establecer fuentes.
Si se destruye la hoja de propiedades, recibirá un mensaje WM_DESTROY. El sistema destruye automáticamente el asistente, pero controlar este mensaje le permite realizar cualquier limpieza necesaria.
El código de notificación PSN_SETACTIVE se envía cada vez que una página está a punto de hacerse visible. La primera vez que se visita una página, PSN_SETACTIVE sigue al mensaje WM_INITDIALOG. Si la página se vuelve a visitar posteriormente, solo recibe una notificación PSN_SETACTIVE. Normalmente, esta notificación se controla para inicializar los datos de la página y habilitar los botones adecuados.
De forma predeterminada, el asistente muestra los botones Atrás, Siguiente y Cancelar, con todos los botones habilitados. Para deshabilitar un botón o que aparezca Finalizar en lugar de Siguiente, debe enviar un mensaje PSM_SETWIZBUTTONS. Una vez enviado este mensaje, el estado de los botones se conserva hasta que se modifica mediante otro mensaje PSM_SETWIZBUTTONS, incluso si se selecciona una página nueva. Normalmente, todos los controladores PSN_SETACTIVE envían este mensaje para asegurarse de que cada página tiene el estado correcto del botón.
Puede cambiar el estado del botón con este mensaje en cualquier momento. Por ejemplo, puede que desee que el botón Siguiente esté deshabilitado inicialmente. Una vez que un usuario haya escrito toda la información necesaria, puede enviar otro mensaje PSM_SETWIZBUTTONS para habilitar el botón Siguiente y permitir que el usuario continúe en la página siguiente.
El fragmento de código siguiente usa la macro PropSheet_SetWizButtons para habilitar los botones Atrás y Siguiente en una página interior antes de que se muestre.
case WM_NOTIFY :
{
LPNMHDR pnmh = (LPNMHDR)lParam;
switch(pnmh->code)
{
...
case PSN_SETACTIVE :
...
// This is an interior page.
PropSheet_SetWizButtons(hwnd, PSWIZB_NEXT | PSWIZB_BACK);
...
}
...
}
Si se hace clic en un botón Siguiente o Atrás, recibirá un código de notificación PSN_WIZNEXT o PSN_WIZBACK. De forma predeterminada, el asistente va automáticamente a la página siguiente o anterior en el orden que se define cuando se crea la hoja de propiedades. Un motivo común para controlar estas notificaciones es impedir que el usuario cambie de página o invalide el orden de página predeterminado.
Para evitar que el usuario cambie de páginas, controle la notificación del botón, llame a la función SetWindowLong con el valor DWL_MSGRESULT establecido en –1 y devuelva TRUE. Por ejemplo:
case PSN_WIZNEXT :
...
// Do not go to the next page yet.
SetWindowLong(hwnd, DWL_MSGRESULT, -1);
return TRUE;
...
Para invalidar el orden estándar e ir a una página determinada, llame a SetWindowLong con el valor DWL_MSGRESULT establecido en el identificador de recurso del cuadro de diálogo de la página y devuelva TRUE. Por ejemplo:
case PSN_WIZNEXT :
...
// Go straight to the completion page.
SetWindowLong(hwnd, DWL_MSGRESULT, IDD_FINISH);
return TRUE;
...
Si se hace clic en el botón Finalizar o Cancelar, recibirá un código de notificación PSN_WIZFINISH o PSN_RESET, respectivamente. Si se hace clic en cualquiera de estos botones, el sistema destruye automáticamente el asistente. Sin embargo, puede controlar estas notificaciones si necesita realizar tareas de limpieza antes de que se destruya el asistente. Para evitar que el asistente se destruya cuando reciba una notificación PSN_WIZFINISH, llame a SetWindowLong con el valor de DWL_MSGRESULT establecido en TRUE y devuelva TRUE. Por ejemplo:
case PSN_WIZFINISH :
...
// Not finished yet.
SetWindowLong(hwnd, DWL_MSGRESULT, TRUE);
return TRUE;
...
En la sección anterior se supone que está implementando un asistente para un sistema con la versión 5 o posterior de los controles comunes.
Si va a escribir un asistente para sistemas con versiones anteriores de los controles comunes, muchas de las características descritas en la sección anterior no estarán disponibles. Varios de los miembros de las estructuras PROPSHEETHEADER y PROPSHEETPAGE que usa el estilo Wizard97 solo son compatibles con los controles comunes de la versión 5 y posteriores. Sin embargo, se puede implementar un asistente compatible con versiones anteriores con una apariencia similar a la del estilo Wizard97. Para ello, debe implementar explícitamente lo siguiente:
- Agregue el gráfico de marca de agua a la plantilla del cuadro de diálogo para las páginas de presentación y finalización.
- Haga que todas las plantillas sean del mismo tamaño. No hay ningún área de encabezado independiente definida por el sistema para las páginas interiores.
- Cree el área de encabezado de la página interior explícitamente en las plantillas.
- No use un gráfico de encabezado porque puede entrar en conflicto con el título o el subtítulo si el asistente cambia el tamaño.
Para más información sobre los asistentes compatibles con versiones anteriores, consulte Asistente Wizard97 compatible con versiones anteriores.
Para obtener una explicación completa de los problemas de diseño de Wizard97, consulte la especificación Wizard97, en otra parte del SDK de Windows. En este documento se proporcionan instrucciones para elementos como las dimensiones de los cuadros de diálogo, dimensiones y colores del mapa de bits y colocación de controles.