2008-11-11 43 views
10

Estoy portando una aplicación MFC a .NET WinForms. En la aplicación MFC, puede hacer clic con el botón derecho en un menú o en un elemento del menú contextual y mostramos otro menú contextual con elementos de diagnóstico y configuración. Estoy tratando de portar esta funcionalidad a .NET, pero estoy teniendo problemas.Cómo mostrar un menú contextual al hacer clic con el botón derecho en un elemento de menú

He podido capturar el clic derecho, desactivar el clic del menú subyacente y abrir un menú contextual en la ubicación correcta, pero el menú original desaparece tan pronto como pierde el foco.

En MFC, mostramos el nuevo menú contextual llamando TrackPopupMenuEx con la bandera TPM_RECURSE.

ContextMenu y el más nuevo ContextMenuStrip clases en .NET sólo tienen un Muestra método. ¿Alguien sabe cómo hacer esto en .NET?

EDITAR

He intentado usar TrackPopupMenuEx a través de un p/invocar, sino que se limita al uso de un ContextMenu en lugar de un ContextMenuStrip que parece fuera de lugar en nuestra aplicación. Tampoco funciona correctamente. No funciona con la nueva MenuStrip y ContextMenuStrip.

También he intentado crear una subclase de ToolStripMenuItem para ver si puedo agregarle un menú contextual. Eso está funcionando para MenuStrip, pero ContextMenuStrip aún permite que los eventos de clic derecho pasen como clics.

+0

Por qué no utilizar un submenú, por lo que cuando se pasa sobre el elemento de menú, se muestra más artículos? –

+0

Muchos de nuestros menús ya tienen submenús. Usamos esta característica para que los administradores puedan ajustar la seguridad de los menús. Por ejemplo, haga clic derecho en un menú y configúrelo en deshabilitado. Eso se almacena en la base de datos y afecta a todos los usuarios. –

Respuesta

10

Editar, debido a un comentario:

En:

protected override void OnClick(EventArgs e) 
{ 
    if (SecondaryContextMenu == null || MouseButtons != MouseButtons.Right) 
    { 
     base.OnClick(e); 
    } 
} 

esta parte

MouseButtons != MouseButtons.Right 

debe y no compilar, ya que es una llamada a Control.MouseButtons. Como el Formulario hereda la clase Control, es suficiente llamar directamente a la propiedad MouseButtons.

Espero que esto ayude:

public partial class Form1 : Form 
{ 
    class CustomToolStripMenuItem : ToolStripMenuItem 
    { 
     private ContextMenuStrip secondaryContextMenu; 

     public ContextMenuStrip SecondaryContextMenu 
     { 
      get 
      { 
       return secondaryContextMenu; 
      } 
      set 
      { 
       secondaryContextMenu = value; 
      } 
     } 

     public CustomToolStripMenuItem(string text) 
      : base(text) 
     { } 

     protected override void Dispose(bool disposing) 
     { 
      if (disposing) 
      { 
       if (secondaryContextMenu != null) 
       { 
        secondaryContextMenu.Dispose(); 
        secondaryContextMenu = null; 
       } 
      } 

      base.Dispose(disposing); 
     } 

     protected override void OnClick(EventArgs e) 
     { 
      if (SecondaryContextMenu == null || MouseButtons != MouseButtons.Right) 
      { 
       base.OnClick(e); 
      } 
     } 
    } 

    class CustomContextMenuStrip : ContextMenuStrip 
    { 
     private bool secondaryContextMenuActive = false; 
     private ContextMenuStrip lastShownSecondaryContextMenu = null; 

     protected override void Dispose(bool disposing) 
     { 
      if (disposing) 
      { 
       if (lastShownSecondaryContextMenu != null) 
       { 
        lastShownSecondaryContextMenu.Close(); 
        lastShownSecondaryContextMenu = null; 
       } 
      } 
      base.Dispose(disposing); 
     } 

     protected override void OnControlAdded(ControlEventArgs e) 
     { 
      e.Control.MouseClick += new MouseEventHandler(Control_MouseClick); 
      base.OnControlAdded(e); 
     } 

     protected override void OnControlRemoved(ControlEventArgs e) 
     { 
      e.Control.MouseClick -= new MouseEventHandler(Control_MouseClick); 
      base.OnControlRemoved(e); 
     } 

     private void Control_MouseClick(object sender, MouseEventArgs e) 
     { 
      ShowSecondaryContextMenu(e); 
     } 

     protected override void OnMouseClick(MouseEventArgs e) 
     { 
      ShowSecondaryContextMenu(e); 
      base.OnMouseClick(e); 
     } 

     private bool ShowSecondaryContextMenu(MouseEventArgs e) 
     { 
      CustomToolStripMenuItem ctsm = this.GetItemAt(e.Location) as CustomToolStripMenuItem; 

      if (ctsm == null || ctsm.SecondaryContextMenu == null || e.Button != MouseButtons.Right) 
      { 
       return false; 
      } 

      lastShownSecondaryContextMenu = ctsm.SecondaryContextMenu; 
      secondaryContextMenuActive = true; 
      ctsm.SecondaryContextMenu.Closed += new ToolStripDropDownClosedEventHandler(SecondaryContextMenu_Closed); 
      ctsm.SecondaryContextMenu.Show(Cursor.Position); 
      return true; 
     } 

     void SecondaryContextMenu_Closed(object sender, ToolStripDropDownClosedEventArgs e) 
     { 
      ((ContextMenuStrip)sender).Closed -= new ToolStripDropDownClosedEventHandler(SecondaryContextMenu_Closed); 
      lastShownSecondaryContextMenu = null; 
      secondaryContextMenuActive = false; 
      Focus(); 
     } 

     protected override void OnClosing(ToolStripDropDownClosingEventArgs e) 
     { 
      if (secondaryContextMenuActive) 
      { 
       e.Cancel = true; 
      } 

      base.OnClosing(e); 
     } 
    } 

    public Form1() 
    { 
     InitializeComponent(); 


     CustomToolStripMenuItem itemPrimary1 = new CustomToolStripMenuItem("item primary 1"); 
     itemPrimary1.SecondaryContextMenu = new ContextMenuStrip(); 
     itemPrimary1.SecondaryContextMenu.Items.AddRange(new ToolStripMenuItem[] { 
      new ToolStripMenuItem("item primary 1.1"), 
      new ToolStripMenuItem("item primary 1.2"), 
     }); 

     CustomToolStripMenuItem itemPrimary2 = new CustomToolStripMenuItem("item primary 2"); 
     itemPrimary2.DropDownItems.Add("item primary 2, sub 1"); 
     itemPrimary2.DropDownItems.Add("item primary 2, sub 2"); 
     itemPrimary2.SecondaryContextMenu = new ContextMenuStrip(); 
     itemPrimary2.SecondaryContextMenu.Items.AddRange(new ToolStripMenuItem[] { 
      new ToolStripMenuItem("item primary 2.1"), 
      new ToolStripMenuItem("item primary 2.2"), 
     }); 

     CustomContextMenuStrip primaryContextMenu = new CustomContextMenuStrip(); 
     primaryContextMenu.Items.AddRange(new ToolStripItem[]{ 
      itemPrimary1, 
      itemPrimary2 
     }); 

     this.ContextMenuStrip = primaryContextMenu; 
    } 
} 
+0

Voy a intentar esto, gracias. –

+0

Gracias, esto funciona para ContextMenuStrip y modificado para los menús de nivel superior de un MenuStrip. Todavía no puedo hacer que funcione para los menús secundarios de MenuStrip, pero creo que es solo un problema con la llamada a GetItemAt. –

+0

La anulación de OnClick en la clase CustomToolstripMenuItem no se compila. No se puede tener (MouseButtons! = MouseButtons.Right). ¿Qué se supone que es esa línea? ¿Cómo se determina qué botón se presionó, en el evento de clic? –

2

Probablemente tendrá que p/invocar el método.

[DllImport("user32.dll")] 
static extern bool TrackPopupMenuEx(IntPtr hmenu, uint fuFlags, int x, int y, 
IntPtr hwnd, IntPtr lptpm); 

const int TPM_RECURSE = 0x0001; 
+0

Hasta ahora no he conseguido que esto funcione. –

+0

Tengo esto trabajando parcialmente con el ContextMenu anterior, pero no con ContextMenuStrip. Incluso entonces estaba teniendo algunos problemas con el enfoque y el resaltado, además las antiguas clases de ContextMenu no se ven bien con los menús más nuevos. –

Cuestiones relacionadas