Partilhar via


Como: Manipular o evento ContextMenuOpening

O evento ContextMenuOpening pode ser manipulado em um aplicativo para ajustar um menu de contexto existente antes da exibição ou para suprimir o menu que, de outra forma, seria exibido definindo a propriedade Handled para true nos dados do evento. A razão típica para definir Handled para true nos dados do evento é substituir o menu inteiramente por um novo objeto ContextMenu, que às vezes requer o cancelamento da operação e iniciar uma nova abertura. Se você escrever manipuladores para o evento ContextMenuOpening, deverá estar ciente dos problemas de tempo entre um controle de ContextMenu e o serviço responsável por abrir e posicionar menus de contexto para controles em geral. Este tópico ilustra algumas das técnicas de código para vários cenários de abertura de menu de contexto e ilustra um caso em que o problema de tempo entra em jogo.

Há vários cenários para lidar com o evento ContextMenuOpening:

  • Ajustar os itens de menu antes da exibição.

  • Substituindo todo o menu antes da exibição.

  • Suprimir completamente qualquer menu de contexto existente e não exibir nenhum menu de contexto.

Exemplo

Ajustando os itens de menu antes da exibição

Ajustar os itens de menu existentes é bastante simples e é provavelmente o cenário mais comum. Você pode fazer isso para adicionar ou subtrair opções de menu de contexto em resposta a informações de estado atual em seu aplicativo ou informações de estado específicas que estão disponíveis como uma propriedade no objeto onde o menu de contexto é solicitado.

A técnica geral é obter a origem do evento, que é o controle específico que foi clicado com o botão direito do mouse, e obter a propriedade ContextMenu dele. Normalmente, você deseja verificar a coleção de Items para ver quais itens de menu de contexto já existem no menu e, em seguida, adicionar ou remover novos itens de MenuItem apropriados para ou da coleção.

void AddItemToCM(object sender, ContextMenuEventArgs e)
{
    //check if Item4 is already there, this will probably run more than once
    FrameworkElement fe = e.Source as FrameworkElement;
    ContextMenu cm = fe.ContextMenu;
    foreach (MenuItem mi in cm.Items)
    {
        if ((String)mi.Header == "Item4") return;
    }
    MenuItem mi4 = new MenuItem();
    mi4.Header = "Item4";
    fe.ContextMenu.Items.Add(mi4);
}

Substituindo todo o menu antes da exibição

Um cenário alternativo é se você quiser substituir todo o menu de contexto. Você também pode, é claro, usar uma variação do código anterior, para remover todos os itens de um menu de contexto existente e adicionar novos começando com o item zero. Mas a abordagem mais intuitiva para substituir todos os itens no menu de contexto é criar um novo ContextMenu, preenchê-lo com itens e, em seguida, definir a propriedade FrameworkElement.ContextMenu de um controle como sendo o novo ContextMenu.

A seguir está o código manipulador simples para substituir um ContextMenu. O código faz referência a um método BuildMenu personalizado, que é separado porque é chamado por mais de um dos manipuladores de exemplo.

void HandlerForCMO(object sender, ContextMenuEventArgs e)
{
    FrameworkElement fe = e.Source as FrameworkElement;
    fe.ContextMenu = BuildMenu();
}
ContextMenu BuildMenu()
{
    ContextMenu theMenu = new ContextMenu();
    MenuItem mia = new MenuItem();
    mia.Header = "Item1";
    MenuItem mib = new MenuItem();
    mib.Header = "Item2";
    MenuItem mic = new MenuItem();
    mic.Header = "Item3";
    theMenu.Items.Add(mia);
    theMenu.Items.Add(mib);
    theMenu.Items.Add(mic);
    return theMenu;
}

No entanto, se você usar esse estilo de manipulador para ContextMenuOpening, poderá expor um problema de tempo se o objeto onde você está definindo a ContextMenu não tiver um menu de contexto preexistente. Quando um usuário clica com o botão direito do mouse em um controle, ContextMenuOpening é gerado mesmo se o ContextMenu existente estiver vazio ou nulo. Mas, neste caso, qualquer novo ContextMenu definido no elemento de origem chega tarde demais para ser exibido. Além disso, se o usuário clicar com o botão direito do mouse uma segunda vez, desta vez seu novo ContextMenu aparecerá, o valor não será nulo e seu manipulador substituirá e exibirá corretamente o menu quando o manipulador for executado uma segunda vez. Isso sugere duas soluções possíveis:

  1. Certifique-se de que os manipuladores ContextMenuOpening executem sempre em controlos que tenham pelo menos um placeholder ContextMenu disponível, que se pretende substituir pelo código do manipulador. Nesse caso, você ainda pode usar o manipulador mostrado no exemplo anterior, mas normalmente deseja atribuir um espaço reservado ContextMenu na marcação inicial:

    <StackPanel>
      <Rectangle Fill="Yellow" Width="200" Height="100" ContextMenuOpening="HandlerForCMO">
        <Rectangle.ContextMenu>
          <ContextMenu>
            <MenuItem>Initial menu; this will be replaced ...</MenuItem>
          </ContextMenu>
        </Rectangle.ContextMenu>
      </Rectangle>
      <TextBlock>Right-click the rectangle above, context menu gets replaced</TextBlock>
    </StackPanel>
    
  2. Suponha que o valor ContextMenu inicial pode ser nulo, com base em alguma lógica preliminar. Você pode verificar ContextMenu para nulo ou usar um sinalizador em seu código para verificar se o manipulador foi executado pelo menos uma vez. Como você assume que o ContextMenu está prestes a ser exibido, o manipulador define Handled para true nos dados do evento. Para o ContextMenuService responsável pela exibição do menu de contexto, um valor de true para Handled nos dados do evento representa uma solicitação para cancelar a exibição para a combinação de menu de contexto / controle que gerou o evento.

Agora que você suprimiu o menu de contexto potencialmente suspeito, a próxima etapa é fornecer um novo e, em seguida, exibi-lo. Definir o novo é basicamente o mesmo que o manipulador anterior: você cria um novo ContextMenu e define a propriedade FrameworkElement.ContextMenu da fonte de controle com ele. A etapa adicional é que agora você deve forçar a exibição do menu de contexto, porque você suprimiu a primeira tentativa. Para forçar a exibição, defina a propriedade Popup.IsOpen como true dentro do manipulador. Tenha cuidado ao fazer isso, porque abrir o menu de contexto no manipulador gera o evento ContextMenuOpening novamente. Se você reentrar no manipulador, ele se tornará infinitamente recursivo. É por isso que é sempre necessário verificar a presença de null ou utilizar um indicador caso se abra um menu de contexto dentro de um manipulador de eventos ContextMenuOpening.

Suprimir qualquer menu de contexto existente e não exibir nenhum menu de contexto

O cenário final, escrever um manipulador que suprime um menu totalmente, é incomum. Se um determinado controle não deve exibir um menu de contexto, provavelmente há maneiras mais apropriadas de garantir isso do que suprimindo o menu apenas quando um usuário o solicita. Mas se você quiser usar o manipulador para suprimir um menu de contexto e não mostrar nada, então seu manipulador deve simplesmente definir Handled para true nos dados do evento. O ContextMenuService responsável pela exibição de um menu de contexto verificará os dados do evento gerado no controle. Se o evento foi marcado Handled em qualquer lugar ao longo da rota, a ação de abertura do menu de contexto que iniciou o evento será suprimida.

    void HandlerForCMO2(object sender, ContextMenuEventArgs e)
    {
        if (!FlagForCustomContextMenu)
        {
            e.Handled = true; //need to suppress empty menu
            FrameworkElement fe = e.Source as FrameworkElement;
            fe.ContextMenu = BuildMenu();
            FlagForCustomContextMenu = true;
            fe.ContextMenu.IsOpen = true;
        }
    }
}

Ver também