Postupy: Zpracování události ContextMenuOpening

Událost ContextMenuOpening lze zpracovat v aplikaci tak, aby buď upravila existující místní nabídku před zobrazením, nebo potlačila nabídku, která by jinak byla zobrazena nastavením Handled vlastnosti na true data události. Typickým důvodem pro nastavení Handledtrue dat událostí je nahrazení nabídky zcela novým ContextMenu objektem, který někdy vyžaduje zrušení operace a spuštění nového otevření. Pokud zapisujete obslužné rutiny události ContextMenuOpening , měli byste vědět o problémech s časováním mezi ovládacím prvku a službou ContextMenu , která je zodpovědná za otevírání a umístění kontextových nabídek ovládacích prvků obecně. Toto téma ukazuje některé techniky kódu pro různé scénáře otevírání kontextových nabídek a ilustruje případ, kdy nastane problém s časováním.

Pro zpracování ContextMenuOpening události existuje několik scénářů:

  • Úprava položek nabídky před zobrazením

  • Před zobrazením nahraďte celou nabídku.

  • Úplně potlačuje všechny existující místní nabídky a nezobrazuje žádnou místní nabídku.

Příklad

Úprava položek nabídky před zobrazením

Úprava existujících položek nabídky je poměrně jednoduchá a pravděpodobně je nejběžnějším scénářem. Můžete to udělat, pokud chcete v reakci na aktuální informace o stavu v aplikaci nebo konkrétní informace o stavu, které jsou k dispozici jako vlastnost objektu, kde je požadována místní nabídka, nebo odečíst možnosti místní nabídky.

Obecná technika je získat zdroj události, což je konkrétní ovládací prvek, který byl kliknut pravým tlačítkem myši, a získat ContextMenu vlastnost z ní. Obvykle chcete zkontrolovat Items kolekci, abyste viděli, které položky místní nabídky už v nabídce existují, a pak do nebo z kolekce přidejte nebo odeberte příslušné nové MenuItem položky.

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

Nahrazení celé nabídky před zobrazením

Alternativní scénář je, pokud chcete nahradit celou místní nabídku. Můžete samozřejmě také použít variantu předchozího kódu, abyste odebrali všechny položky existující místní nabídky a přidali nové položky začínající nulou položky. Intuitivnějším přístupem k nahrazení všech položek v místní nabídce je vytvořit nový ContextMenu, naplnit ho položkami a pak nastavit FrameworkElement.ContextMenu vlastnost ovládacího prvku na nový ContextMenu.

Následuje jednoduchý kód obslužné rutiny pro nahrazení znaku ContextMenu. Kód odkazuje na vlastní BuildMenu metodu, která je oddělena, protože je volána více než jedním z ukázkových obslužných rutin.

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

Pokud však použijete tento styl obslužné rutiny ContextMenuOpening, můžete potenciálně vystavit problém s časováním, pokud objekt, ve kterém nastavujete ContextMenu , nemá existující místní nabídku. Když uživatel klikne pravým tlačítkem myši na ovládací prvek, je vyvolán i v případě, ContextMenuOpening že je existující ContextMenu prázdný nebo null. V tomto případě se ale vše, co ContextMenu jste nastavili na zdrojovém prvku, dostane příliš pozdě, aby se zobrazilo. Pokud se uživateli stane, že uživatel klikne pravým tlačítkem myši podruhé, tentokrát se zobrazí nová ContextMenu hodnota, hodnota není null a obslužná rutina se správně nahradí a zobrazí nabídku, když obslužná rutina po druhém spuštění obslužné rutiny spustí. Navrhnou se dvě možná alternativní řešení:

  1. Ujistěte se, že ContextMenuOpening obslužné rutiny vždy běží na ovládacích prvcích, které mají k dispozici alespoň zástupný symbol ContextMenu , který chcete nahradit kódem obslužné rutiny. V tomto případě můžete přesto použít obslužnou rutinu zobrazenou v předchozím příkladu, ale obvykle chcete přiřadit zástupný symbol ContextMenu v počátečním kódu:

    <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. Předpokládejme, že počáteční ContextMenu hodnota může být null na základě určité předběžné logiky. Můžete buď zkontrolovat ContextMenu hodnotu null, nebo pomocí příznaku v kódu zkontrolovat, jestli byla obslužná rutina spuštěna alespoň jednou. Vzhledem k tomu, že předpokládáte, že ContextMenu se má zobrazit, obslužná rutina se pak nastaví Handled na true data události. ContextMenuService Pro zobrazení true místní nabídky představuje hodnota Handled v datech události požadavek na zrušení zobrazení pro místní nabídku nebo kombinaci ovládacích prvků, která vyvolala událost.

Teď, když jste potlačí potenciálně podezřelou místní nabídku, dalším krokem je zadat nový a pak ho zobrazit. Nastavení nového objektu je v podstatě stejné jako předchozí obslužná rutina: vytvoříte novou ContextMenu a nastavíte jeho vlastnost zdroje FrameworkElement.ContextMenu ovládacího prvku. Dalším krokem je, že teď musíte vynutit zobrazení místní nabídky, protože jste potlačí první pokus. Chcete-li vynutit zobrazení, nastavíte Popup.IsOpen vlastnost v true rámci obslužné rutiny. Buďte opatrní, když to uděláte, protože otevření místní nabídky v obslužné rutině ContextMenuOpening vyvolá událost znovu. Pokud obslužnou rutinu znovu zadáte, stane se nekonečně rekurzivní. To je důvod, proč vždy potřebujete zkontrolovat null nebo použít příznak, pokud otevřete místní nabídku z obslužné rutiny ContextMenuOpening události.

Potlačení jakékoli existující místní nabídky a zobrazení žádné místní nabídky

Konečný scénář napsání obslužné rutiny, která zcela potlačuje nabídku, je neobvyklá. Pokud daný ovládací prvek nemá zobrazovat místní nabídku, existuje pravděpodobně vhodnější způsob, jak to zajistit, než potlačováním nabídky jen tehdy, když ji uživatel požádá. Pokud ale chcete obslužnou rutinu použít k potlačení místní nabídky a zobrazení nic, měla by obslužná rutina jednoduše nastavit Handled v true datech události. Ten ContextMenuService je zodpovědný za zobrazení místní nabídky, zkontroluje data události události, kterou vyvolal na ovládacím prvku. Pokud byla událost označena Handled kdekoli podél trasy, místní nabídka otevře akci, která iniciovala událost, je potlačena.

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

Viz také