Compartilhar via


Como tratar o evento ContextMenuOpening

O ContextMenuOpening evento 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 Handled propriedade como true nos dados do evento. O motivo típico para definir Handled para true os dados do evento é substituir o menu inteiramente por um novo ContextMenu objeto, o que às vezes requer o cancelamento da operação e o início de uma nova abertura. Se você escrever manipuladores para o evento, deverá estar ciente dos problemas de tempo entre um ContextMenu controle e o ContextMenuOpening 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 do menu de contexto e ilustra um caso em que ocorre um problema de sincronismo.

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

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

  • Substituir o menu inteiro antes da exibição.

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

Exemplo

Ajustar os itens de menu antes da exibição

Ajustar os itens de menu existentes é relativamente simples e é provavelmente o cenário mais comum. Você pode fazer isso para adicionar ou subtrair opções do menu de contexto em resposta a informações do estado atual em seu aplicativo ou informações do estado específico que está disponível como uma propriedade no objeto no qual 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 ContextMenu propriedade dele. Normalmente, você deseja verificar a coleção para ver quais itens de menu de contexto já existem no menu e, em seguida, adicionar ou remover novos MenuItem itens apropriados de ou para a Items 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);
}

Substituir o menu inteiro antes da exibição

Um cenário alternativo é se você deseja substituir o menu de contexto inteiro. Você certamente também pode 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, preenchê-lo com itens e, em seguida, definir a FrameworkElement.ContextMenu propriedade de um controle como o novo .ContextMenuContextMenu

A seguir está o código de manipulador simples para substituir um ContextMenuarquivo . O código referencia um método BuildMenu personalizado, que é separado porque ele é 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 no qual você está definindo o 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 ele é gerado mesmo se o existente ContextMenu estiver vazio ou nulo. Mas, nesse caso, qualquer novidade ContextMenu que você definir no elemento de origem chega tarde demais para ser exibida. Além disso, se o usuário clicar com o botão direito do mouse uma segunda vez, desta vez o novo ContextMenu será exibido, o valor não será nulo e o 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 sempre sejam executados em controles que tenham pelo menos um espaço reservado ContextMenu disponível, que ContextMenuOpening você 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 inicial ContextMenu pode ser nulo, com base em alguma lógica preliminar. Você pode verificar se há nulo ou usar um sinalizador em seu código para verificar ContextMenu se o manipulador foi executado pelo menos uma vez. Como você assume que o ContextMenu está prestes a ser exibido, seu manipulador define Handled como true nos dados do evento. Para o que é responsável pela exibição do menu de contexto, um true valor para nos dados do evento representa uma solicitação para cancelar a exibição para Handled a combinação de menu de contexto / controle que gerou o ContextMenuService evento.

Agora que você suprimiu o menu de contexto possivelmente 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 da origem do FrameworkElement.ContextMenu controle com ele. A etapa adicional é que agora você deve forçar a exibição do menu de contexto, pois você suprimiu a primeira tentativa. Para forçar a exibição, defina a Popup.IsOpen propriedade como true dentro do manipulador. Tenha cuidado ao fazer isso, porque abrir o menu de contexto no manipulador gera o ContextMenuOpening evento novamente. Se você digitar novamente o manipulador, ele se tornará infinitamente recursivo. É por isso que você sempre precisa verificar null ou usar um sinalizador se abrir um menu de contexto de dentro de um ContextMenuOpening manipulador de eventos.

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

O cenário final, escrever um manipulador que suprima um menu totalmente, é incomum. Se um determinado controle não deve exibir um menu de contexto, existem maneiras provavelmente mais adequadas para garantir isso do que eliminar o menu apenas quando um usuário solicitar. Mas se você quiser usar o manipulador para suprimir um menu de contexto e não mostrar nada, seu manipulador deve simplesmente definir Handled como true nos dados do evento. O ContextMenuService que é responsável por exibir um menu de contexto verificará os dados do evento que ele gerou 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;
        }
    }
}

Confira também