Partilhar via


Como criar assistentes

Um assistente é um tipo de folha de propriedades que fornece uma maneira simples e poderosa de guiar os usuários através de um procedimento.

Os assistentes são uma das chaves para simplificar a experiência do usuário. Eles permitem que você faça uma operação complexa, como a configuração de um aplicativo, e a divida em uma série de etapas simples. Em cada ponto do processo, você pode fornecer uma explicação do que é necessário e exibir controles que permitem ao usuário fazer seleções e inserir texto.

Um assistente é, na verdade, um tipo de ficha de propriedades. Uma folha de propriedades é essencialmente um contêiner para uma coleção de páginas , onde cada página é uma caixa de diálogo separada. Enquanto as folhas de propriedades regulares permitem que o usuário acesse qualquer página a qualquer momento, os assistentes apresentam páginas em sequência. Em vez de guias, os botões são usados para navegar para frente e para trás. A ordem em que as páginas são exibidas é controlada pelo aplicativo e pode ser modificada com base na entrada do usuário.

Há dois estilos principais de assistente: o estilo Wizard97 mais antigo e o estilo Aero introduzido no Windows Vista. Para obter ilustrações, consulte Sobre fichas de propriedades. (Um terceiro estilo, usando apenas o sinalizador PSH_WIZARD ou PSH_WIZARD_LITE, apresenta uma sequência simples de folhas de propriedades sem cabeçalhos ou marcas d'água.)

Observação

Uma "marca d'água" no contexto de assistentes é um bitmap que aparece na margem esquerda de algumas páginas.

 

A discussão na maioria deste documento pressupõe que você esteja implementando um assistente para um sistema com versão 5.80 ou posterior dos controles comuns. Se você tentar usar o estilo Wizard97 com versões anteriores dos controles comuns, seu aplicativo pode compilar, mas não será exibido corretamente. Para obter uma discussão sobre como criar um assistente compatível com Wizard97 em sistemas anteriores, consulte Assistentes compatíveis com versões anteriores mais adiante neste tópico.

O que precisa de saber

Tecnologias

Pré-requisitos

  • C/C++
  • Programação da interface do usuário do Windows

Instruções

Implementação do assistente

A implementação de um assistente parece-se com a implementação de uma ficha de propriedades normal. No nível mais básico, é uma questão de definir um dos seguintes sinalizadores ou combinações de sinalizadores na estrutura PROPSHEETHEADER que define a folha de propriedades.

Bandeira Estilo
PSH_WIZARD Um assistente simples sem cabeçalhos ou bitmaps.
PSH_WIZARD_LITE Semelhante ao PSH_WIZARD, com algumas pequenas diferenças na aparência; Por exemplo, o divisor acima dos botões é definido para a largura total da janela.
PSH_WIZARD97 Um assistente do Wizard97 com cabeçalhos (opcionais), bitmaps de cabeçalho e marcas d'água.
PSH_WIZARD | PSH_AEROWIZARD Um Assistente Aero. Os Aero Wizards não utilizam marcas d'água nem bitmaps de cabeçalho. Eles exigem o modelo STA (Single-Threaded Apartment).

 

O procedimento básico para implementar um assistente é o seguinte:

  1. Crie um modelo de caixa de diálogo para cada página.
  2. Defina as páginas criando uma PROPSHEETPAGE estrutura para cada página. Essa estrutura define a página e contém ponteiros para o modelo de caixa de diálogo e quaisquer bitmaps ou outros recursos.
  3. Passe a estrutura PROPSHEETPAGE que foi criada na etapa anterior para a função CreatePropertySheetPage para criar o identificador HPROPSHEETPAGE da página.
  4. Defina o assistente criando uma estrutura de PROPSHEETHEADER para ele.
  5. Passe a estrutura PROPSHEETHEADER para a função PropertySheet para exibir o assistente.
  6. Implemente procedimentos de caixa de diálogo para cada página para lidar com mensagens de notificação dos controles da página e dos botões do assistente e para processar outras mensagens do Windows.

Criar os modelos de caixa de diálogo

Existem dois tipos básicos de página do assistente: exterior e interior. As páginas exteriores são as páginas de introdução (boas-vindas) e de conclusão. Todas as outras são páginas interiores.

Modelos de Caixa de Diálogo de Página Exterior

O layout básico das páginas de introdução e conclusão é idêntico. A seguinte ilustração mostra uma página de introdução de exemplo do Wizard97, com uma marca d'água de marcador de posição.

captura de ecrã mostrando uma página do assistente com um gráfico à esquerda, título e corpo de texto à direita e botões Voltar, Avançar e Cancelar na parte inferior

Para páginas externas do Wizard97, o modelo de caixa de diálogo é de 317x193 unidades de diálogo. Ele preenche todo o assistente, exceto a legenda e a faixa na parte inferior que contém os botões Voltar, Próximoe Cancelar. O lado esquerdo do modelo, que é reservado para um bitmap de "marca d'água", não deve conter nenhum controle. A marca d'água é especificada na estrutura PROPSHEETHEADER do assistente e é adicionada à página automaticamente. Você deve permitir espaço para ele ao projetar o modelo de recurso.

Ao criar o bitmap da marca d'água, lembre-se de que a caixa de diálogo pode aumentar de tamanho se, por exemplo, o usuário escolher uma fonte grande do sistema. Idiomas diferentes também tendem a ter métricas de fonte diferentes. Quando a página cresce, a área reservada para a marca d'água fica proporcionalmente maior. No entanto, você não pode alterar o bitmap da marca d'água, nem o bitmap será ampliado para preencher a área maior. Em vez disso, o bitmap é deixado em seu tamanho original na parte superior esquerda da área reservada. A parte maior da área reservada que não é coberta pela marca d'água é preenchida automaticamente com a cor do pixel no canto superior esquerdo do bitmap.

Se precisar de bitmaps de marca d'água de tamanhos diferentes para diferentes métricas de fonte, duas soluções possíveis são:

  • Obtenha as métricas de fonte antes de criar o assistente e especifique um bitmap de marca d'água de tamanho apropriado.
  • Não especifique um bitmap de marca d'água ao criar o assistente. O Wizard97 deixará a área da marca d'água em branco. Em seguida, desenhe um bitmap de tamanho apropriado na área reservada para a marca d'água.

Você pode colocar controles na área à direita da marca d'água tal como faria para uma caixa de diálogo normal. A cor de fundo desta área é determinada pelo sistema e não requer nenhuma ação da sua parte. Normalmente, você coloca dois controles estáticos nessa área. A parte superior contém o título e usa uma fonte grande em negrito (12 pontos Verdana Bold para Wizard97). O outro, que é para texto explicativo, usa a fonte padrão da caixa de diálogo.

A principal diferença entre as páginas de introdução e conclusão são os botões do assistente e o texto nos controles estáticos. As páginas de introdução normalmente têm um botão Seguinte e um botão Voltar, com apenas o botão Seguinte ativado. As páginas de conclusão têm o botão Voltar ativado e o botão Próxima é substituído por um botão Concluir.

Observação

No Aero Wizards, o botão Voltar é substituído por um botão de seta na barra de legenda.

 

Você pode modificar o texto no botão Concluir enviando uma mensagem PSM_SETFINISHTEXT ao assistente. Por padrão, o botão Concluir não inclui um acelerador de teclado. Para definir um acelerador de teclado, inclua um ampersand na cadeia de texto que se passa para PSM_SETFINISHTEXT. Por exemplo, "&Finish" define "F" como o acelerador do teclado.

Modelos de Caixa de Diálogo da Página Interior

As páginas interiores têm um aspeto um pouco diferente das páginas exteriores. A ilustração a seguir mostra um exemplo de página interna do Wizard97, com um bitmap de espaço reservado para cabeçalho.

captura de tela de uma página do assistente com texto de título e subtítulo e um gráfico na parte superior, texto no meio e botões na parte inferior

A área do cabeçalho na parte superior da página é manipulada pela folha de propriedades, portanto, não está incluída no modelo. O conteúdo do cabeçalho é especificado na estrutura PROPSHEETPAGE da página e na estrutura PROPSHEETHEADER do assistente. Como a página interior precisa caber entre o cabeçalho e os botões, o modelo de caixa de diálogo do Wizard97 tem 317x143 unidades de diálogo, ligeiramente menor do que o modelo para páginas externas.

A ilustração a seguir mostra um Aero Wizard que foi criado a partir do mesmo modelo.

captura de tela que difere da anterior por ter uma área de título na parte superior e apenas os botões Avançar e Cancelar na parte inferior

Definir as páginas do assistente

Depois de criar os modelos de caixa de diálogo e recursos relacionados, como bitmaps e tabelas de cadeia de caracteres, você pode criar as páginas da folha de propriedades. O procedimento é semelhante ao das folhas de propriedades padrão. Primeiro, preencha os membros apropriados de uma estrutura PROPSHEETPAGE. (Alguns membros são específicos para assistentes.) Em seguida, chame a função CreatePropertySheetPage para criar o identificador HPROPSHEETPAGE da página.

Os seguintes sinalizadores relacionados ao assistente podem ser definidos no membro dwFlags da estrutura PROPSHEETPAGE.

Bandeira Descrição
PSP_HIDEHEADER Defina este sinalizador para páginas externas no Wizard97. O cabeçalho não é mostrado e uma marca d'água pode ser exibida.
PSP_USEHEADERTITLE Defina esse sinalizador para páginas internas para colocar um título na área de cabeçalho no Wizard97 ou na parte superior da área do cliente em um Aero Wizard.
PSP_USEHEADERSUBTITLE Defina esse sinalizador para páginas internas para colocar uma legenda na área de cabeçalho no Wizard97.

 

Se definiu PSP_USEHEADERTITLE ou PSP_USEHEADERSUBTITLE, atribua o texto do título e do subtítulo aos membros pszHeaderTitle e pszHeaderSubtitle, respectivamente. Quando atribui cadeias de caracteres de texto aos membros das estruturas PROPSHEETPAGE e PROPSHEETHEADER, pode atribuir um ponteiro para a cadeia de caracteres ou usar a macro MAKEINTRESOURCE para atribuir um valor de um recurso de cadeia de caracteres. O recurso de cadeia de caracteres é carregado a partir do módulo especificado no hInstance membro da estrutura PROPSHEETHEADER do assistente.

Ao chamar CreatePropertySheetPage para criar uma página, atribua o resultado a um elemento de uma matriz de identificadores HPROPSHEETPAGE. Essa matriz é usada ao criar a folha de propriedades. O índice do array do handle de uma página determina a ordem padrão na qual a página é exibida. Depois de criar o identificador HPROPSHEETPAGE de uma página, você pode reutilizar a mesma estruturaPROPSHEETPAGE para criar a próxima página atribuindo novos valores aos membros relevantes.

Uma maneira alternativa de criar páginas é usar estruturas PROPSHEETPAGE separadas para cada página e criar uma matriz de estruturas. Esta matriz é utilizada ao invés de uma matriz de identificadores HPROPSHEETPAGE ao criar a folha de propriedades. Usar estruturas PROPSHEETPAGE separadas elimina a necessidade de chamar CreatePropertySheetPage, mas usa mais memória. Caso contrário, não há diferença significativa entre as duas abordagens.

O exemplo a seguir define uma página interior do Wizard97 ao atribuir valores a uma estrutura PROPSHEETPAGE. Neste exemplo, o título, o subtítulo e o modelo de caixa de diálogo da página são todos identificados por suas IDs de recurso. A funçãoCreatePropertySheetPage é então chamada para criar o identificador HPROPSHEETPAGE da página. Como será a segunda página a ser exibida, o identificador é atribuído à matriz de referências, ahpsp, com um í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);

Dados de página personalizados

Ao criar uma página, pode-se atribuir-lhe dados personalizados utilizando o membro lParam da estruturaPROPSHEETPAGE, normalmente atribuindo-lhe um ponteiro para uma estrutura definida pelo utilizador.

Quando a página é selecionada pela primeira vez, seu procedimento de caixa de diálogo recebe uma mensagem WM_INITDIALOG. O valor lParam da mensagem aponta para uma cópia da estruturaPROPSHEETPAGEda página, a partir da qual você pode recuperar os dados personalizados. Em seguida, você pode armazenar esses dados para uso em mensagens subsequentes usando SetWindowLongPtr com GWL_USERDATA como parâmetro index. Várias páginas podem ter um ponteiro para os mesmos dados, e qualquer alteração nos dados feita por uma página fica disponível para as outras páginas em seus procedimentos de diálogo.

Definir a folha de propriedades do assistente

Assim como acontece com as folhas de propriedades comuns, você define a folha de propriedades do assistente preenchendo membros de uma PROPSHEETHEADER estrutura. Essa estrutura permite especificar as páginas que compõem o assistente e a ordem padrão em que elas são exibidas, juntamente com vários parâmetros relacionados. Em seguida, inicie o assistente chamando a função PropertySheet.

No estilo Wizard97, o membro pszCaption da estrutura PROPSHEETHEADER é ignorado. Em vez disso, o assistente exibe a legenda especificada no modelo de caixa de diálogo da página atual. Se o modelo não tiver uma legenda, a legenda da página anterior será exibida. Assim, para exibir a mesma legenda em todas as páginas, especifique a legenda no modelo da página introdutória.

No estilo Aero Wizard, a legenda da caixa de diálogo é retirada de pszCaption.

Se você criou uma matriz de identificadores HPROPSHEETPAGE para suas páginas, atribua a matriz ao membro phpage. Se, em vez disso, criou uma matriz de estruturas PROPSHEETPAGE, atribua a matriz ao membro ppsp e defina o sinalizador PSH_PROPSHEETPAGE no membro dwFlags.

O exemplo a seguir atribui valores a psh, uma estrutura PROPSHEETHEADER, e chama a função PropertySheet para lançar o assistente. O assistente no estilo Wizard97 tem gráficos de marca d'água e cabeçalho, especificados por suas IDs de recurso. A matriz ahpsp contém todos os identificadores HPROPSHEETPAGE e define a ordem padrão na qual eles são exibidos.

// 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);

O procedimento da caixa de diálogo

Cada página do assistente requer um procedimento de caixa de diálogo para processar mensagens do Windows, particularmente notificações de seus controles e do assistente. As três mensagens que quase todos os programadores devem ser capazes de lidar são WM_INITDIALOG, WM_DESTROYe WM_NOTIFY.

A mensagem WM_NOTIFY é recebida antes da página ser exibida e quando qualquer um dos botões do assistente é clicado. O parâmetro lParam da mensagem é um ponteiro para uma estrutura NMHDR de cabeçalho. O ID da notificação está contido no membro do código da estrutura. As quatro notificações que a maioria dos assistentes precisa manipular são as seguintes.

Código Descrição
PSN_SETACTIVE Enviado antes da página ser exibida.
PSN_WIZBACK Enviado quando o botão Voltar é clicado.
PSN_WIZNEXT Enviado quando o botão Próximo é clicado.
PSN_WIZFINISH Enviado quando o botão Concluir é clicado.

 

Tratar de WM_INITDIALOG e WM_DESTROY

Quando uma página está prestes a ser exibida pela primeira vez, seu procedimento de caixa de diálogo recebe uma mensagem WM_INITDIALOG. A manipulação dessa mensagem permite que o assistente execute todas as tarefas de inicialização necessárias, como armazenar dados personalizados ou definir fontes.

Quando a folha de propriedades é destruída, você recebe uma mensagem WM_DESTROY. O assistente é automaticamente destruído pelo sistema, mas o processamento desta mensagem permite fazer qualquer limpeza necessária.

Gerir PSN_SETACTIVE

O código de notificação PSN_SETACTIVE é enviado sempre que uma página está prestes a ser tornada visível. A primeira vez que uma página é visitada, PSN_SETACTIVE segue a mensagem WM_INITDIALOG. Se a página for revisitada posteriormente, receberá apenas uma notificação PSN_SETACTIVE. Essa notificação geralmente é tratada para inicializar dados para a página e habilitar os botões apropriados.

Por padrão, o assistente exibe os botões Voltar, Avançare Cancelar, com todos os botões ativados. Para desativar um botão ou exibir Concluir em vez de Próxima, você deve enviar uma mensagem PSM_SETWIZBUTTONS. Depois que essa mensagem for enviada, o estado dos botões será preservado até ser modificado por outra mensagem PSM_SETWIZBUTTONS, mesmo que uma nova página seja selecionada. Normalmente, todos os manipuladores de PSN_SETACTIVE enviam essa mensagem para garantir que cada página tenha o estado correto do botão.

Você pode alterar o estado do botão com esta mensagem a qualquer momento. Por exemplo, você pode querer que o botão Avançar seja inicialmente desativado. Depois que um usuário inserir todas as informações necessárias, você pode enviar outra mensagem PSM_SETWIZBUTTONS para ativar o botão Próxima e permitir que o usuário prossiga para a próxima página.

O fragmento de código a seguir usa a macro PropSheet_SetWizButtons para habilitar os botões Voltar e Avançar em uma página interna antes que ela seja exibida.

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);
            
            ...
        }
    ...
    
    }

Manipule PSN_WIZNEXT, PSNWIZBACK e PSN_WIZFINISH

Quando um botão Avançar ou Voltar é clicado, você recebe um código de notificação PSN_WIZNEXT ou PSN_WIZBACK. Por padrão, o assistente vai automaticamente para a página seguinte ou anterior na ordem definida quando a folha de propriedades é criada. Um motivo comum para lidar com essas notificações é impedir que o usuário alterne de página ou substitua a ordem de página padrão.

Para impedir que o utilizador mude de página, manipule a notificação do botão, chame a funçãoSetWindowLong docom o valor DWL_MSGRESULT definido como –1 e retorne TRUE. Por exemplo:

case PSN_WIZNEXT :

        ...
        
        // Do not go to the next page yet.
        SetWindowLong(hwnd, DWL_MSGRESULT, -1);
        
        return TRUE;
        
        ...

Para substituir a ordem padrão e ir para uma página específica, chame SetWindowLong com o valor DWL_MSGRESULT definido como ID de recurso da caixa de diálogo da página e retorne TRUE. Por exemplo:

case PSN_WIZNEXT :

        ...
        
        // Go straight to the completion page.
        SetWindowLong(hwnd, DWL_MSGRESULT, IDD_FINISH);
        
        return TRUE;
        
        ...

Quando o botão Concluir ou Cancelar é clicado, você recebe um código de notificação PSN_WIZFINISH ou PSN_RESET, respectivamente. Quando um desses botões é clicado, o assistente é automaticamente destruído pelo sistema. No entanto, você pode lidar com essas notificações se precisar executar tarefas de limpeza antes que o assistente seja destruído. Para evitar que o assistente seja destruído ao receber uma notificação de PSN_WIZFINISH, chame SetWindowLong com o valor DWL_MSGRESULT definido como TRUEe retorne TRUE. Por exemplo:

case PSN_WIZFINISH :

        ...
        
        // Not finished yet.
        SetWindowLong(hwnd, DWL_MSGRESULT, TRUE);
        
        return TRUE;
        
        ...

Assistentes compatíveis com versões anteriores

A seção anterior pressupõe que você esteja implementando um assistente para um sistema com versão 5 ou posterior dos controles comuns.

Se você estiver escrevendo um assistente para sistemas com versões anteriores dos controles comuns, muitos dos recursos discutidos na seção anterior não estarão disponíveis. Vários dos membros doPROPSHEETHEADERe estruturas de PROPSHEETPAGE que são usadas pelo estilo Wizard97 são suportados apenas por controles comuns versão 5 e posterior. No entanto, ainda é possível implementar um assistente compatível com versões anteriores de com uma aparência semelhante à do estilo Wizard97. Para fazer isso, você deve implementar explicitamente o seguinte:

  • Adicione o gráfico de marca d'água ao modelo de caixa de diálogo para suas páginas de introdução e conclusão.
  • Faça todos os seus modelos do mesmo tamanho. Não existe uma área de cabeçalho separada definida pelo sistema para páginas interiores.
  • Crie a área de cabeçalho da página interior explicitamente nos seus modelos.
  • Não use um gráfico de cabeçalho porque ele pode entrar em conflito com o título ou subtítulo se o assistente alterar de tamanho.

Para obter mais informações sobre assistentes compatíveis com versões anteriores, consulte Assistente compatível com versões anteriores 97.

Comentários

Para obter uma discussão completa dos problemas de design do Wizard97, consulte o Wizard97 Specification, em outro lugar no SDK do Windows. Este documento tem diretrizes para coisas como as dimensões das caixas de diálogo, dimensões e cores de bitmap e o posicionamento de controles.

Usando fichas de propriedades

demonstração dos controlos comuns do Windows (CppWindowsCommonControls)