Determinar en qué control se utilizó ContextMenuStrip

84

Tengo un ContextMenuStripque está asignado a varios cuadros de lista diferentes. Estoy tratando de averiguar cuándo ContextMenuStripse hace clic en en qué ListBoxse usó. Probé el siguiente código para comenzar, pero no funciona. El sendertiene el valor correcto, pero cuando intento para asignarlo a la menuSubmittedque es nulo.

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

Cualquier ayuda sería genial. Gracias.

Usando la ayuda a continuación, lo descubrí:

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
fuente
gracias por la solución que estaba buscando. Yo tuve el mismo problema. pero sugiero no anidar todas esas ifdeclaraciones y usarlas if (menuItem == null) return;si eres como yo y no quieres que tu código que lo maneja esté anidado en 2 niveles adicionales innecesarios.
Shawn Kovac

Respuestas:

123

Para un ContextMenu:

El problema es que el senderparámetro apunta al elemento del menú contextual en el que se hizo clic, no al menú contextual en sí.

Sin embargo, es una solución simple, porque cada uno MenuItemexpone un GetContextMenumétodo que le dirá quéContextMenu contiene ese elemento del menú.

Cambie su código a lo siguiente:

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 un ContextMenuStrip:

Cambia ligeramente las cosas si usa a en ContextMenuStriplugar de a ContextMenu. Los dos controles no están relacionados entre sí, y una instancia de uno no se puede convertir en una instancia del otro.

Como antes, el elemento en el que se hizo clic todavía se devuelve en el senderparámetro, por lo que tendrá que determinar a quién ContextMenuStrippertenece este elemento de menú individual. Haces eso con la Ownerpropiedad . Finalmente, usará la SourceControlpropiedad para determinar qué control muestra el menú contextual.

Modifica tu código así:

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 Grey
fuente
@bluefeet: Entonces tienes algo más mal. Acabo de probar este código con tres listboxes diferentes y todo funcionó como se esperaba. Publica más código de reproducción.
Cody Gray
2
@bluefeet: Actualicé el código en mi respuesta. Hay una gran diferencia entre ContextMenuy ContextMenuStrip. (Ah, y veo que ya lo has descubierto. Bueno, ¡mucho mejor si aprendes las cosas por tu cuenta!)
Cody Gray
1
Usé el evento de apertura para registrar el SourceControl que abrió el menú a una variable local, y luego hice referencia a eso cuando manejé los clics en los elementos.
QuickDanger
1
@QuickDanger Sí, SourceControllamentablemente es nulo en el momento en que se activa un Clickevento de un ToolStripItemsubelemento de ContextMenuStrip. Parece que el evento de ContextMenuStrip' Closedse dispara antes que ese Clickevento, que es probablemente lo que causa el problema; Supongo que la propiedad se borra después de que el menú 'se cierra'.
Nyerguds
1
@CodyGray En realidad, si el árbol es más profundo, debe recorrer la cadena de OwnerItempropiedades hasta que encuentre una ToolStripItemque tenga una ContextMenuStripen su Ownerpropiedad. Pero como acabo de comentar, no funciona; el SourceControlen el menú de contexto será nulo. Sin embargo, dijiste que no puedes reproducirlo ... ¿tal vez el problema solo ocurre con menús de más de un nivel? El mío tenía dos subniveles de profundidad.
Nyerguds
3

Publicación anterior, pero en caso de que alguien como yo la encuentre:

Para un ContextMenuStrip, lo anterior no funcionó para mí, pero me llevó a encontrar lo que sí.

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

Esto me dio el nombre del control esperado. Puede poner en validación, etc. con declaraciones if, solo estoy publicando para ir al grano.

seanu13
fuente
Esto solo funciona con los elementos directos en un ContextMenu. El problema es que ItemClickedno se activa al hacer clic en los elementos del submenú ; necesitan su propio Clickevento que tendría el elemento en sí como remitente, no el menú.
Nyerguds
3

Tuve una gran dificultad para hacer que funcionara este código. Esta es la solución más simple que pude encontrar:

Para un 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
fuente
0

La solución más sencilla sería:

Control parentControl = ((sender as MenuItem).GetContextMenu()).SourceControl;
 
Emile contra Rooyen
fuente