Como personalizar barras de ferramentas

A maioria dos aplicativos baseados no Windows usa controles de barra de ferramentas para fornecer aos usuários acesso conveniente à funcionalidade do programa. No entanto, as barras de ferramentas estáticas têm algumas deficiências, como pouco espaço para exibir efetivamente todas as ferramentas disponíveis. A solução para esse problema é tornar as barras de ferramentas do seu aplicativo personalizáveis pelo usuário. Em seguida, os usuários podem optar por exibir apenas as ferramentas de que precisam e podem organizá-las de uma maneira que se adapte ao seu estilo de trabalho pessoal.

Observação

As barras de ferramentas nas caixas de diálogo não podem ser personalizadas.

 

Para habilitar a personalização, inclua o sinalizador de estilo de controles CCS_ADJUSTABLE comum ao criar o controle da barra de ferramentas. Há duas abordagens básicas para a personalização:

  • A caixa de diálogo de personalização. Esta caixa de diálogo fornecida pelo sistema é a abordagem mais simples. Ele oferece aos usuários uma interface gráfica do usuário que permite adicionar, excluir ou mover ícones.
  • Ferramentas de arrastar e soltar. A implementação da funcionalidade de arrastar e soltar permite que os usuários movam ferramentas para outro local na barra de ferramentas ou as excluam arrastando-as para fora da barra de ferramentas. Ele fornece aos usuários uma maneira rápida e fácil de organizar sua barra de ferramentas, mas não permite que eles adicionem ferramentas.

Você pode implementar uma abordagem ou ambas, dependendo das necessidades do aplicativo. Nenhuma dessas duas abordagens de personalização fornece um mecanismo interno, como um botão Cancelar ou Desfazer, para retornar a barra de ferramentas ao seu estado anterior. Você deve usar explicitamente a API de controle da barra de ferramentas para armazenar o estado de pré-personalização da barra de ferramentas. Se necessário, você pode usar posteriormente essas informações armazenadas para restaurar a barra de ferramentas ao seu estado original.

O que você precisa saber

Tecnologias

Pré-requisitos

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

Instruções

Caixa de diálogo Personalização

A caixa de diálogo de personalização é fornecida pelo controle da barra de ferramentas para oferecer aos usuários uma maneira simples de adicionar, mover ou excluir ferramentas. Os usuários podem iniciá-lo clicando duas vezes na barra de ferramentas. Os aplicativos podem iniciar a caixa de diálogo de personalização programaticamente enviando ao controle da barra de ferramentas uma mensagem TB_CUSTOMIZE.

A ilustração a seguir mostra um exemplo da caixa de diálogo de personalização da barra de ferramentas.

screen shot of a window with a three-item toolbar, and a dialog box with lists of the available and current toolbar buttons

As ferramentas na caixa de listagem à direita são as que estão atualmente na barra de ferramentas. Inicialmente, essa lista conterá as ferramentas especificadas ao criar a barra de ferramentas. A caixa de listagem à esquerda contém as ferramentas que estão disponíveis para adicionar à barra de ferramentas. Seu aplicativo é responsável por preencher essa lista (exceto com o Separador, que aparece automaticamente).

O controle da barra de ferramentas notifica seu aplicativo de que ele está iniciando uma caixa de diálogo de personalização enviando à janela pai um código de notificação TBN_BEGINADJUST seguido por um código de notificação TBN_INITCUSTOMIZE. Na maioria dos casos, o aplicativo não precisa responder a esses códigos de notificação. No entanto, se você não quiser que a caixa de diálogo Personalizar barra de ferramentas exiba um botão Ajuda, manipule TBN_INITCUSTOMIZE retornando TBNRF_HIDEHELP.

O controle da barra de ferramentas, em seguida, coleta as informações necessárias para inicializar a caixa de diálogo enviando três séries de códigos de notificação, na seguinte ordem:

  • Um código de notificação TBN_QUERYINSERT para cada botão na barra de ferramentas para determinar onde os botões podem ser inseridos. Retornar FALSE para impedir que um botão seja inserido à esquerda do botão especificado na mensagem de notificação. Se você retornar FALSO para todos os códigos de notificação TBN_QUERYINSERT, a caixa de diálogo não será exibida.
  • Um código de notificação TBN_QUERYDELETE para cada ferramenta que está atualmente na barra de ferramentas. Retorne TRUE se uma ferramenta puder ser excluída ou FALSE se não.
  • Uma série de códigos de notificação TBN_GETBUTTONINFO para preencher a lista de botões disponíveis. Para adicionar um botão à lista, preencha a estrutura NMTOOLBAR que é passada com o código de notificação e retorne TRUE. Quando você não tiver mais ferramentas para adicionar, retorne FALSE. Observe que você pode retornar informações para botões que já estão na barra de ferramentas; Esses botões não serão adicionados à lista.

A caixa de diálogo é exibida e o usuário pode começar a personalizar a barra de ferramentas.

Quando a caixa de diálogo é aberta, seu aplicativo pode receber uma variedade de códigos de notificação, dependendo das ações do usuário:

  • TBN_QUERYINSERT. O usuário alterou o local de uma ferramenta na barra de ferramentas ou adicionou uma ferramenta. Retorne FALSE para impedir que a ferramenta seja inserida nesse local.
  • TBN_DELETINGBUTTON. O usuário está prestes a remover uma ferramenta da barra de ferramentas.
  • TBN_CUSTHELP. O usuário clicou no botão Ajuda.
  • TBN_TOOLBARCHANGE. O usuário adicionou, moveu ou excluiu uma ferramenta.
  • TBN_RESET. O usuário clicou no botão Redefinir.

Depois que a caixa de diálogo for destruída, seu aplicativo receberá um código de notificação TBN_ENDADJUST .

O exemplo de código a seguir mostra uma maneira de implementar a personalização da barra de ferramentas.

// The buttons are stored in an array of TBBUTTON structures. 
//
// Constants such as STD_FILENEW are identifiers for the 
// built-in bitmaps that have already been assigned as the toolbar's 
// image list.
//
// Constants such as IDM_NEW are application-defined command identifiers.

TBBUTTON allButtons[] = 
    {
        { MAKELONG(STD_FILENEW,  ImageListID), IDM_NEW,   TBSTATE_ENABLED, 0, {0}, 0, (INT_PTR)L"New" },
        { MAKELONG(STD_FILEOPEN, ImageListID), IDM_OPEN,  TBSTATE_ENABLED, 0, {0}, 0, (INT_PTR)L"Open"},
        { MAKELONG(STD_FILESAVE, ImageListID), IDM_SAVE,  TBSTATE_ENABLED, 0, {0}, 0, (INT_PTR)L"Save"},
        { MAKELONG(STD_CUT,      ImageListID), IDM_CUT,   TBSTATE_ENABLED, 0, {0}, 0, (INT_PTR)L"Cut" },
        { MAKELONG(STD_COPY,     ImageListID), IDM_COPY,  TBSTATE_ENABLED, 0, {0}, 0, (INT_PTR)L"Copy"},
        { MAKELONG(STD_PASTE,    ImageListID), IDM_PASTE, TBSTATE_ENABLED, 0, {0}, 0, (INT_PTR)L"Paste"}
    };

// The following appears in the window's message handler.

case WM_NOTIFY: 
    {
        switch (((LPNMHDR)lParam)->code) 
        {
        
        case TBN_GETBUTTONINFO:  
            {
                LPTBNOTIFY lpTbNotify = (LPTBNOTIFY)lParam;

                // Pass the next button from the array. There is no need to filter out buttons
                // that are already used—they will be ignored.
                
                int buttonCount = sizeof(allButtons) / sizeof(TBBUTTON);
                
                if (lpTbNotify->iItem < buttonCount)
                {
                    lpTbNotify->tbButton = allButtons[lpTbNotify->iItem];
                    return TRUE;
                }
                
                else
                
                {
                    return FALSE;  // No more buttons.
                }
            }
            
            break;

            case TBN_QUERYINSERT:
            
            case TBN_QUERYDELETE:
                return TRUE; 
        }
    }

Ferramentas de arrastar e soltar

Os usuários também podem reorganizar os botões em uma barra de ferramentas pressionando a tecla SHIFT e arrastando o botão para outro local. O processo de arrastar e soltar é manipulado automaticamente pelo controle da barra de ferramentas. Ele exibe uma imagem fantasma do botão à medida que ele é arrastado e reorganiza a barra de ferramentas depois que ele é solto. Os usuários não podem adicionar botões dessa maneira, mas podem excluir um botão deixando-o sair da barra de ferramentas.

Embora o controle da barra de ferramentas normalmente faça essa operação automaticamente, ele também envia ao aplicativo dois códigos de notificação: TBN_QUERYDELETE e TBN_QUERYINSERT. Para controlar o processo de arrastar e soltar, manipule esses códigos de notificação da seguinte maneira:

  • O código de notificação TBN_QUERYDELETE é enviado assim que o usuário tenta mover o botão, antes que o botão fantasma seja exibido. Retornar FALSO para impedir que o botão seja movido. Se você retornar TRUE, o usuário poderá mover a ferramenta ou excluí-la deixando-a sair da barra de ferramentas. Se uma ferramenta puder ser movida, ela poderá ser excluída. No entanto, se o usuário excluir uma ferramenta, o controle da barra de ferramentas enviará ao aplicativo um código de notificação TBN_DELETINGBUTTON , momento em que você poderá optar por reinserir o botão em seu local original, cancelando assim a exclusão.
  • O código de notificação TBN_QUERYINSERT é enviado quando o usuário tenta soltar o botão na barra de ferramentas. Para impedir que o botão que está sendo movido seja descartado à esquerda do botão especificado na notificação, retorne FALSE. Esse código de notificação não será enviado se o usuário soltar a ferramenta da barra de ferramentas.

Se o usuário tentar arrastar um botão sem também pressionar a tecla SHIFT, o controle da barra de ferramentas não manipulará a operação de arrastar e soltar. No entanto, ele enviará ao aplicativo um código de notificação TBN_BEGINDRAG para indicar o início de uma operação de arrastar e um código de notificação TBN_ENDDRAG para indicar o fim. Se você quiser habilitar essa forma de arrastar e soltar, seu aplicativo deverá manipular esses códigos de notificação, fornecer a interface de usuário necessária e modificar a barra de ferramentas para refletir quaisquer alterações.

Salvando e restaurando barras de ferramentas

No processo de personalização de uma barra de ferramentas, seu aplicativo pode precisar salvar informações para que você possa restaurar a barra de ferramentas para seu estado original. Para iniciar o salvamento ou a restauração de um estado da barra de ferramentas, envie ao controle da barra de ferramentas uma mensagem TB_SAVERESTORE com o lParam definido como TRUE. O valor lParam desta mensagem especifica se você está solicitando uma operação de salvamento ou restauração. Depois que a mensagem é enviada, há duas maneiras de manipular a operação de salvamento/restauração:

  • Com controles comuns versão 4.72 e anteriores, você deve implementar um manipulador de TBN_GETBUTTONINFO . O controle da barra de ferramentas envia esse código de notificação para solicitar informações sobre cada botão à medida que ele é restaurado.
  • A versão 5.80 inclui uma opção de salvar/restaurar. No início do processo, e à medida que cada botão é salvo ou restaurado, seu aplicativo receberá um código de notificação TBN_SAVE ou TBN_RESTORE . Para usar essa opção, você deve implementar manipuladores de notificação para fornecer as informações de bitmap e estado necessárias para salvar ou restaurar com êxito o estado da barra de ferramentas.

Os estados da barra de ferramentas são salvos em um fluxo de dados que consiste em blocos de dados definidos pelo Shell alternados com blocos de dados definidos pelo aplicativo. Um bloco de dados de cada tipo é armazenado para cada botão, juntamente com um bloco opcional de dados globais que os aplicativos podem colocar no início do fluxo. Durante o processo de salvamento, o manipulador de TBN_SAVE adiciona os blocos definidos pelo aplicativo ao fluxo de dados. Durante o processo de restauração, o manipulador de TBN_RESTORE lê cada bloco e fornece ao Shell as informações necessárias para reconstruir a barra de ferramentas.

Como lidar com uma notificação de TBN_SAVE

O primeiro código de notificação TBN_SAVE é enviado no início do processo de salvamento. Antes de qualquer botão ser salvo, os membros da estrutura NMTBSAVE são definidos conforme mostrado na tabela a seguir.

Membro Configuração
iItem –1
cbDados A quantidade de memória necessária para dados definidos pelo Shell.
cBotões O número de botões.
pData A quantidade calculada de memória necessária para dados definidos pelo aplicativo. Normalmente, você inclui alguns dados globais, além de dados para cada botão. Adicione esse valor ao cbData e aloque memória suficiente para pData para armazenar tudo.
pCorrente O primeiro byte não utilizado no fluxo de dados. Se você não precisar de informações da barra de ferramentas global, defina pCurrent = pData para que ele aponte para o início do fluxo de dados. Se você precisar de informações globais da barra de ferramentas, armazene-as em pData e defina pCurrent como o início da parte não utilizada do fluxo de dados antes de retornar.

 

Se você quiser adicionar algumas informações da barra de ferramentas global, coloque-as no início do fluxo de dados. Avançar pCurrent para o final dos dados globais para que ele aponte para o início da parte não utilizada do fluxo de dados e retorne.

Depois que você retornar, o Shell começa a salvar as informações do botão. Ele adiciona os dados definidos pelo Shell para o primeiro botão em pCurrent e, em seguida, avança pCurrent para o início da parte não utilizada.

Depois que cada botão é salvo, um código de notificação TBN_SAVE é enviado e NMTBSAVE é retornado com esses membros definidos da seguinte maneira.

Membro Configuração
iItem O índice baseado em zero do número do botão.
pCorrente Um ponteiro para o primeiro byte não utilizado no fluxo de dados. Se você quiser armazenar informações adicionais sobre o botão, armazene-o no local apontado por pCurrent e atualize pCurrent para apontar para a primeira parte não utilizada do fluxo de dados depois disso.
Botão TBBUTTON Uma estrutura TBBUTTON que descreve o botão que está sendo salvo.

 

Ao receber o código de notificação, você deve extrair qualquer informação específica do botão que você precisa do TBBUTTON. Lembre-se de que, ao adicionar um botão, você pode usar o membro dwData do TBBUTTON para armazenar dados específicos do aplicativo. Carregue seus dados no fluxo de dados no pCurrent. Avançar pCurrent para o final de seus dados, apontando novamente para o início da parte não utilizada do fluxo, e retornar.

O Shell então vai para o próximo botão, adiciona suas informações ao pData, avança pCurrent, carrega TBBUTTON e envia outro código de notificação TBN_SAVE. Esse processo continua até que todos os botões sejam salvos.

Restaurando barras de ferramentas salvas

O processo de restauração basicamente reverte o processo de salvamento. No início, seu aplicativo receberá um código de notificação TBN_RESTORE com o membro iItem da estrutura NMTBRESTORE definido como –1. O membro cbData é definido como o tamanho de pData e cButtons é definido como o número de botões.

Seu manipulador de notificação deve extrair as informações globais que foram colocadas no início de pData durante o salvamento e avançar pCurrent para o início do primeiro bloco de dados definidos pelo Shell. Defina cBytesPerRecord para o tamanho dos blocos de dados usados para salvar os dados do botão. Defina cButtons para o número de botões e retorne.

O próximo NMTBRESTORE é para o primeiro botão. O membro pCurrent aponta para o início do primeiro bloco de dados do botão e o iItem é definido como o índice do botão. Extraia esses dados e avance pCurrent. Carregue os dados no TBBUTTON e retorne. Para omitir um botão da barra de ferramentas restaurada, defina o membro idCommand de TBBUTTON como zero. O Shell repetirá o processo para os botões restantes. Além das mensagens NMTBSAVE e NMTBRESTORE, você também pode usar mensagens como TBN_RESET para salvar e restaurar uma barra de ferramentas.

O exemplo de código a seguir salva uma barra de ferramentas antes que ela seja personalizada e a restaura se o aplicativo receber uma mensagem TBN_RESET .

int               i;
LPNMHDR           lpnmhdr;
static int        nResetCount;
static LPTBBUTTON lpSaveButtons;
LPARAM            lParam;

switch( lpnmhdr->code)
{
    case TBN_BEGINADJUST: // Begin customizing the toolbar.
    {
        LPTBNOTIFY  lpTB = (LPTBNOTIFY)lparam;
       
        // Allocate memory for the button information.
        
        nResetCount   = SendMessage(lpTB->hdr.hwndFrom, TB_BUTTONCOUNT, 0, 0);
        lpSaveButtons = (LPTBBUTTON)GlobalAlloc(GPTR, sizeof(TBBUTTON) * nResetCount);
      
        // In case the user presses reset, save the current configuration 
        // so the original toolbar can be restored.
        
        for(i = 0; i < nResetCount; i++)
        {
            SendMessage(lpTB->hdr.hwndFrom, 
                        TB_GETBUTTON, i, 
                        (LPARAM)(lpSaveButtons + i));
        }
    }
    
    return TRUE;
   
    case TBN_RESET:
    {
        LPTBNOTIFY lpTB = (LPTBNOTIFY)lparam;
        
        int nCount, i;
    
        // Remove all of the existing buttons, starting with the last one.
        
        nCount = SendMessage(lpTB->hdr.hwndFrom, TB_BUTTONCOUNT, 0, 0);
        
        for(i = nCount - 1; i >= 0; i--)
        {
            SendMessage(lpTB->hdr.hwndFrom, TB_DELETEBUTTON, i, 0);
        }
      
        SendMessage(lpTB->hdr.hwndFrom,      // Restore the saved buttons.
                    TB_ADDBUTTONS, 
                    (WPARAM)nResetCount, 
                    (LPARAM)lpSaveButtons);
    }
    
    return TRUE;
   
    case TBN_ENDADJUST:                // Free up the memory you allocated.
        GlobalFree((HGLOBAL)lpSaveButtons);
        
        return TRUE;
}

Usando controles da barra de ferramentas

Valores de índice de imagem do botão padrão da barra de ferramentas

Demonstração de controles comuns do Windows (CppWindowsCommonControls)