Procedura: 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, è necessario conoscere i problemi di intervallo tra un ContextMenu controllo e il servizio responsabile dell'apertura ContextMenuOpening 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 le voci di menu di scelta rapida già presenti nel menu e quindi aggiungere o rimuovere nuovi MenuItem elementi appropriati da o verso la 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 semplice codice del gestore per la sostituzione di un oggetto 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 usa questo stile di gestore per ContextMenuOpening, è possibile esporre un problema di intervallo 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 novità ContextMenu impostata sull'elemento di origine arrivi troppo tardi per essere visualizzata. 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. Assicurarsi che ContextMenuOpening i gestori vengano sempre eseguiti sui controlli con almeno un segnaposto ContextMenu disponibile, che si intende sostituire con il codice del gestore. 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 verificare ContextMenu la presenza di valori Null oppure usare 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 su trueHandled nei dati dell'evento. Per l'oggetto responsabile della ContextMenuService visualizzazione del menu di scelta rapida, un true valore per Handled nei dati dell'evento rappresenta una richiesta di annullare la visualizzazione per la combinazione di menu di scelta rapida/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 compila un nuovo ContextMenu oggetto e si imposta la proprietà dell'origine del FrameworkElement.ContextMenu controllo con essa. 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 generato nel controllo . Se l'evento è stato contrassegnato Handled ovunque lungo la route, il menu di scelta rapida apre l'azione 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;
        }
    }
}

Vedi anche