Partilhar via


Como: Tratar o Evento ContextMenuOpening

O evento ContextMenuOpening pode ser tratado em um aplicativo para ajustar um menu de contexto existente antes da exibição ou para suprimir o menu que seria exibido caso contrário, definindo a propriedade Handled como true nos dados de evento. Tipicamente, você define Handled como true nos dados do evento para substituir completamente o menu com um novo objeto ContextMenu, que às vezes necessita que a operação seja cancelada e que se inicie uma nova abertura de menu. Se você escrever tratadores para o evento ContextMenuOpening, você deve estar ciente das questões de sincronismo entre um controle ContextMenu e o serviço que é responsável pela abertura e posicionamento dos menus de contexto para controles em geral. Este tópico ilustra algumas das técnicas de código para várias situações de abertura de menus de contexto e ilustra um caso em que o problema de sincronismo entra em cena.

Há várias situações para tratar o evento ContextMenuOpening:

  • Ajustando os itens de menu antes da visualização.

  • Substituindo o menu inteiro antes da visualização.

  • Completamente eliminando qualquer menu de contexto existente e não exibindo nenhum menu de contexto.

Exemplo

Ajustando os itens de menu antes da visualização

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

A técnica geral é: obtenha a origem do evento, que é o controle específico que foi clicado com o botão direito do mouse, e obtenha a propriedade ContextMenu a partir dele. Você geralmente deseja verificar a coleção Items para ver quais itens de menu de contexto já existem no menu e, em seguida, adicionar ou remover os novos itens MenuItem 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 o menu inteiro antes da visualização

Uma situação alternativa é se você desejar substituir o menu de contexto inteiro. Você poderia, é claro, também usar uma variação do código anterior, para remover todos os itens de uma menu de contexto existente e adicionar novos começando do zero. Mas a abordagem mais intuitiva para substituir todos os itens de menu de contexto é criar um novo ContextMenu, preenchê-lo com os itens e, em seguida, definir a propriedade FrameworkElement.ContextMenu de um controle para ser o novo ContextMenu.

O código a seguir é um tratador simples para substituir um ContextMenu. O código referencia um método BuildMenu personalizado, que é separado porque ele é chamado por mais de um dos tratadores do 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 tratador para ContextMenuOpening, você pode potencialmente expor um problema de sincronismo se o objeto no qual você está definindo ContextMenu não tiver um menu de contexto preexistente. Quando um usuário clicar com botão direito do mouse sobre um controle, ContextMenuOpening é gerado mesmo se o ContextMenu existente estiver vazio ou nulo. Mas nesse caso, qualquer novo ContextMenu que você definir no elemento de origem chega muito tarde para ser exibido. Além disso, se o usuário por acaso clicar com o botão direito do mouse uma segunda vez, desta vez seu novo ContextMenu aparece, o valor é não nulo, e o tratador corretamente substituirá e exibirá o menu quando o tratador for executado uma segunda vez. Isso sugere duas soluções possíveis:

  1. Certifique-se que tratadores ContextMenuOpening sempre executem em controles que possuam pelo menos um ContextMenu temporário, o qual você pretende substituir durante o tratamento de eventos. Nesse caso, você poderá usar o tratador mostrado no exemplo anterior, mas você geralmente deseja atribuir um ContextMenu temporário 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 possa ser nulo, com base em alguma lógica preliminar. Você poderia verificar se ContextMenu é nulo, ou usar um sinalizador em seu código para verificar se o tratador foi executado pelo menos uma vez. Como você supõe que o ContextMenu esteja prestes a ser exibido, o seu tratador então define Handled como true nos dados do evento. Para o ContextMenuService que é responsável pela exibição do menu de contexto, um valor true no Handled dos dados de evento representa uma solicitação para cancelar a exibição do menu de contexto / combinação de controles que geraram o evento.

Agora que você suprimiu o menu de contexto possivelmente suspeito, a próxima etapa é fornecer um novo, e em seguida exibi-lo. Definindo o novo um é basicamente o mesmo sistema autônomo o manipulador de eventos anterior: criar um novo ContextMenu e conjunto a fonte de controle FrameworkElement.ContextMenu propriedade 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, você define a propriedade Popup.IsOpen como true dentro da rotina de tratamento. Cuidado ao fazer isso, pois se você abrir o menu de contexto no tratador será gerado un novo evento ContextMenuOpening. Se você entrar novamente em seu tratador, ele se tornará infinitamente recursivo. É por isso que você sempre precisará verificar null ou usar um sinalizador se você abrir um menu de contexto de dentro de um tratador de eventos ContextMenuOpening.

Eliminando qualquer menu de contexto existente e não exibindo nenhum menu de contexto

A situação final, escrever um tratador que elimina um menu totalmente, é incomum. Se um determinado controle não deve exibir um menu de contexto, é provável que haja maneiras mais adequadas para garantir isso do que eliminando o menu apenas quando um usuário solicitar. Mas se você quiser usar o tratador para eliminar um menu de contexto e não mostrar nada, então o tratador deve simplesmente definir Handled como true nos dados do evento. O ContextMenuService que é responsável pela exibição de um menu de contexto verificará os dados do evento do evento que ele gerou no controle. Se o evento foi marcado como Handled em qualquer lugar ao longo do caminho, então a ação de abertura de 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;
            }
        }
    }

Consulte também

Conceitos

Visão geral sobre elementos base

Visão Geral do ContextMenu

Referência

ContextMenu

FrameworkElement.ContextMenu