Condividi tramite


Come gestire l'evento ContextMenuOpening

L'evento ContextMenuOpening può essere gestito in un'applicazione per modificare un menu di scelta rapida esistente prima di visualizzare o eliminare il menu che altrimenti verrebbe visualizzato impostando la Handled proprietà su true nei dati dell'evento. Il motivo tipico per cui si imposta Handled su true nei dati dell'evento consiste nel sostituire completamente il menu con un nuovo ContextMenu oggetto, che a volte richiede l'annullamento dell'operazione e l'avvio di un nuovo oggetto aperto. Se si scrivono gestori per l'evento ContextMenuOpening, è necessario conoscere i problemi di sincronizzazione tra un controllo ContextMenu e il servizio responsabile dell'apertura e del posizionamento dei menu di scelta rapida per i controlli in generale. Questo argomento illustra alcune delle tecniche di codice per vari scenari di apertura del menu di scelta rapida e illustra un caso in cui entra in gioco il problema di temporizzazione.

Esistono diversi scenari per la gestione dell'evento ContextMenuOpening :

  • Modifica delle voci di menu prima della visualizzazione.

  • Sostituzione dell'intero menu prima della visualizzazione.

  • Elimina completamente qualsiasi menu di scelta rapida esistente e non visualizza alcun menu di scelta rapida.

Esempio

Modifica delle voci di menu prima della visualizzazione

La regolazione delle voci di menu esistenti è piuttosto semplice ed è probabilmente lo scenario più comune. È possibile eseguire questa operazione per aggiungere o sottrarre opzioni di menu di scelta rapida in risposta alle informazioni sullo stato correnti nell'applicazione o informazioni di stato specifiche disponibili come proprietà nell'oggetto in cui è richiesto il menu di scelta rapida.

La tecnica generale consiste nell'ottenere l'origine dell'evento, ovvero il controllo specifico su cui è stato fatto clic con il pulsante destro del mouse e ottenere la ContextMenu proprietà da essa. In genere si vuole controllare la Items raccolta per visualizzare gli elementi del menu contestuale già presenti nel menu e quindi aggiungere o rimuovere nuovi MenuItem elementi appropriati nella raccolta o dalla raccolta.

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);
}

Sostituzione dell'intero menu prima della visualizzazione

Uno scenario alternativo è se si vuole sostituire l'intero menu di scelta rapida. Naturalmente è anche possibile usare una variante del codice precedente per rimuovere ogni elemento di un menu di scelta rapida esistente e aggiungerne di nuovi a partire da zero. Tuttavia, l'approccio più intuitivo per sostituire tutti gli elementi nel menu di scelta rapida consiste nel creare un nuovo ContextMenuoggetto , popolarlo con gli elementi e quindi impostare la FrameworkElement.ContextMenu proprietà di un controllo come nuovo ContextMenuoggetto .

Di seguito è riportato il codice semplice per la sostituzione di un ContextMenu. Il codice fa riferimento a un metodo personalizzato BuildMenu , separato perché viene chiamato da più gestori di esempio.

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;
}

Tuttavia, se si utilizza questo stile di gestore per ContextMenuOpening, può potenzialmente esporre un problema di temporizzazione se l'oggetto in cui si sta impostando ContextMenu non dispone di un menu di scelta rapida preesistente. Quando un utente fa clic con il pulsante destro del mouse su un controllo, ContextMenuOpening viene generato anche se l'oggetto esistente ContextMenu è vuoto o null. In questo caso, tuttavia, qualsiasi nuovo ContextMenu impostato sull'elemento di origine arriva troppo tardi per essere visualizzato. Inoltre, se l'utente fa clic con il pulsante destro del mouse una seconda volta, questa volta viene visualizzato il nuovo ContextMenu , il valore non è Null e il gestore sostituirà e visualizzerà correttamente il menu quando il gestore esegue una seconda volta. Ciò suggerisce due possibili soluzioni alternative:

  1. Assicuratevi che ContextMenuOpening i gestori di eventi vengano sempre eseguiti su controlli che abbiano almeno un segnaposto ContextMenu disponibile, da sostituire con il codice del gestore di eventi. In questo caso, è comunque possibile usare il gestore illustrato nell'esempio precedente, ma in genere si vuole assegnare un segnaposto ContextMenu nel markup iniziale:

    <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. Si supponga che il valore iniziale ContextMenu sia Null, in base a una logica preliminare. È possibile controllare ContextMenu per null, oppure utilizzare un flag nel codice per verificare se il gestore è stato eseguito almeno una volta. Poiché si presuppone che l'oggetto ContextMenu stia per essere visualizzato, il gestore imposta quindi Handled su true nei dati dell'evento. Per l'oggetto ContextMenuService incaricato della visualizzazione del menu di scelta rapida, un valore true per Handled nei dati dell'evento rappresenta una richiesta di annullare la visualizzazione del menu di scelta rapida in corrispondenza del controllo che ha generato l'evento.

Ora che è stato eliminato il menu di scelta rapida potenzialmente sospetto, il passaggio successivo consiste nell'specificare un nuovo menu, quindi visualizzarlo. L'impostazione del nuovo è fondamentalmente uguale al gestore precedente: si crea un nuovo ContextMenu e si imposta la proprietà FrameworkElement.ContextMenu dell'origine del controllo. Il passaggio aggiuntivo consiste nel fatto che è ora necessario forzare la visualizzazione del menu di scelta rapida, perché il primo tentativo è stato eliminato. Per forzare la visualizzazione, impostare la Popup.IsOpen proprietà su true all'interno del gestore. Prestare attenzione quando si esegue questa operazione, perché l'apertura del menu di scelta rapida nel gestore genera nuovamente l'evento ContextMenuOpening . Se si immette nuovamente il gestore, diventa infinitamente ricorsivo. Questo è il motivo per cui è sempre necessario cercare null o usare un flag se si apre un menu di scelta rapida dall'interno di un ContextMenuOpening gestore eventi.

Eliminazione di qualsiasi menu di scelta rapida esistente e visualizzazione di nessun menu di scelta rapida

Lo scenario finale, la scrittura di un gestore che elimina completamente un menu, non è comune. Se un determinato controllo non dovrebbe visualizzare un menu di scelta rapida, è probabile che ci siano modi più appropriati per garantire questa situazione che eliminando il menu solo quando un utente lo richiede. Tuttavia, se si vuole usare il gestore per eliminare un menu di scelta rapida e non visualizzare nulla, il gestore deve semplicemente impostare su Handledtrue nei dati dell'evento. L'oggetto ContextMenuService responsabile della visualizzazione di un menu di scelta rapida controlla i dati dell'evento che ha generato sul controllo. Se l'evento è stato contrassegnato Handled ovunque lungo la route, l'azione di apertura del menu di scelta rapida che ha avviato l'evento viene soppressa.

    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;
        }
    }
}

Vedere anche