Determine em qual controle o ContextMenuStrip foi usado

84

Eu tenho um ContextMenuStripque está atribuído a várias caixas de listagem diferentes. Estou tentando descobrir quando o ContextMenuStripé clicado em que ListBoxfoi usado. Tentei o código abaixo para começar, mas não está funcionando. O sendertem o valor correto, mas quando tento atribuí-lo ao, menuSubmittedele é nulo.

private void MenuViewDetails_Click(object sender, EventArgs e)
{
    ContextMenu menuSubmitted = sender as ContextMenu;
    if (menuSubmitted != null)
    {
        Control sourceControl = menuSubmitted.SourceControl;
    }
}

Qualquer ajuda seria ótimo. Obrigado.

Usando a ajuda abaixo, descobri:

private void MenuViewDetails_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;
            if (menuItem != null)
            {
                ContextMenuStrip calendarMenu = menuItem.Owner as ContextMenuStrip;

                if (calendarMenu != null)
                {
                    Control controlSelected = calendarMenu.SourceControl;
                }
            }
        }
Taryn
fonte
obrigado pela solução que procurava. Eu tive o mesmo problema. mas eu sugiro não aninhar todas essas ifinstruções e usar if (menuItem == null) return;se você for como eu e não quiser que seu código que lida com isso seja aninhado em 2 níveis desnecessários extras.
Shawn Kovac

Respostas:

123

Para um ContextMenu:

O problema é que o senderparâmetro aponta para o item no menu de contexto que foi clicado, não para o menu de contexto em si.

É uma correção simples, porém, porque cada MenuItemum expõe um GetContextMenumétodo que dirá qual ContextMenucontém aquele item de menu.

Altere seu código para o seguinte:

private void MenuViewDetails_Click(object sender, EventArgs e)
{
    // Try to cast the sender to a MenuItem
    MenuItem menuItem = sender as MenuItem;
    if (menuItem != null)
    {
        // Retrieve the ContextMenu that contains this MenuItem
        ContextMenu menu = menuItem.GetContextMenu();

        // Get the control that is displaying this context menu
        Control sourceControl = menu.SourceControl;
    }
}

Para um ContextMenuStrip:

Isso muda um pouco as coisas se você usar um em ContextMenuStripvez de a ContextMenu. Os dois controles não estão relacionados um ao outro e uma instância de um não pode ser convertida em uma instância do outro.

Como antes, o item que foi clicado ainda é retornado no senderparâmetro, então você terá que determinar o ContextMenuStripque possui este item de menu individual. Você faz isso com a Ownerpropriedade . Finalmente, você usará a SourceControlpropriedade para determinar qual controle está exibindo o menu de contexto.

Modifique seu código da seguinte maneira:

private void MenuViewDetails_Click(object sender, EventArgs e)
{
     // Try to cast the sender to a ToolStripItem
     ToolStripItem menuItem = sender as ToolStripItem;
     if (menuItem != null)
     {
        // Retrieve the ContextMenuStrip that owns this ToolStripItem
        ContextMenuStrip owner = menuItem.Owner as ContextMenuStrip;
        if (owner != null)
        {
           // Get the control that is displaying this context menu
           Control sourceControl = owner.SourceControl;
        }
     }
 }
Cody Gray
fonte
@bluefeet: Então você tem algo errado. Acabei de testar este código com três caixas de listagem diferentes e tudo funcionou conforme o esperado. Publique mais alguns códigos de reprodução.
Cody Gray
2
@bluefeet: Eu atualizei o código em minha resposta. Há uma grande diferença entre ContextMenue ContextMenuStrip. (Ah, e vejo que você já descobriu. Bem, melhor ainda para aprender as coisas por conta própria!)
Cody Gray
1
Usei o evento Opening para registrar o SourceControl que abriu o menu em uma variável local e, em seguida, referenciei isso ao lidar com os cliques do item.
QuickDanger
1
@QuickDanger Sim, SourceControlinfelizmente é nulo no momento em que um Clickevento de um ToolStripItemsubitem de ContextMenuStripé disparado. Parece que o ContextMenuStrip's Closedevento é acionado antes que Clickevento, que é provavelmente o que causa o problema; Presumo que a propriedade seja limpa depois que o menu 'fecha'.
Nyerguds
1
@CodyGray Na verdade, se a árvore for mais profunda, você terá que subir a cadeia de OwnerItempropriedades até encontrar um ToolStripItemque tenha um ContextMenuStripem sua Ownerpropriedade. Mas, como acabei de comentar, não funciona; o SourceControlno menu de contexto será nulo. Você disse que não pode reproduzi-lo ... talvez o problema só ocorra com menus mais profundos do que um nível? O meu tinha dois níveis de profundidade.
Nyerguds
3

Postagem mais antiga, mas no caso de alguém como eu descobrir:

Para um ContextMenuStrip, o acima não funcionou para mim, mas levou a encontrar o que funcionou.

void DeleteMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
    ContextMenuStrip menu = sender as ContextMenuStrip;
    Control sourceControl = menu.SourceControl;
    MessageBox.Show(sourceControl.Name);
}

Isso me deu o nome do controle esperado. Você pode colocar validação, etc, com instruções if, estou apenas postando para ir direto ao ponto.

seanu13
fonte
Isso só funciona com o itens diretos em a ContextMenu. O problema é que ItemClickednão dispara ao clicar nos itens do submenu ; eles precisam de seu próprio Clickevento, que teria o item em si como remetente, não o menu.
Nyerguds
3

Tive grande dificuldade em fazer esse código funcionar. Esta é a solução mais simples que encontrei:

Para um ContextMenuStrip:

    Control _sourceControl = null;
    private void contextMenuStrip_Opened(object sender, EventArgs e)
    {
        _sourceControl = contextMenuStrip.SourceControl;
    }

    private void contextMenuItem_Click(object sender, EventArgs e)
    {
        var menuItem = (ToolStripMenuItem)sender;

        _sourceControl.Text = menuItem.Text;
        MessageBox.Show(menuItem.Name);
        MessageBox.Show(sourceControl.Name);
    }
Nick Allan
fonte
0

A solução mais fácil seria:

Control parentControl = ((sender as MenuItem).GetContextMenu()).SourceControl;
 
Emile v Rooyen
fonte