Sdílet prostřednictvím


Jak na to: Ovládání události ContextMenuOpening

Událost ContextMenuOpening lze zpracovat v aplikaci tak, aby před zobrazením upravila existující místní nabídku nebo potlačila nabídku, která by jinak byla zobrazena nastavením vlastnosti Handled na true v datech události. Typickým důvodem nastavení Handled na true v datech události je nahradit nabídku zcela novým objektem ContextMenu, který někdy vyžaduje zrušení operace a spuštění nového otevření. Pokud zapisujete obslužné rutiny pro událost ContextMenuOpening, měli byste vědět o problémech s časováním mezi ovládacím prvku ContextMenu a službou, 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

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

  • Ú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 za účelem přidání nebo odebrání možností místní nabídky v reakci na aktuální stav aplikace nebo konkrétní informace o stavu, které jsou dostupné jako vlastnost objektu, kde je místní nabídka požadována.

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 z ní vlastnost ContextMenu. Obvykle chcete zkontrolovat kolekci Items, abyste viděli, jaké 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é položky MenuItem.

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ím scénářem je, když chcete nahradit celou místní nabídku. Samozřejmě můžete také použít variantu předchozího kódu k odstranění všech položek existující kontextové nabídky a přidání nových, počínaje položkou na pozici nula. 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 novou ContextMenu.

Následuje jednoduchý handler pro nahrazení ContextMenu. Kód odkazuje na vlastní metodu BuildMenu, 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 pro ContextMenuOpening, můžete potenciálně vystavit problém s časováním, pokud objekt, ve kterém nastavujete ContextMenu, nemá předem existující místní nabídku. Když uživatel klikne pravým tlačítkem myši na ovládací prvek, ContextMenuOpening se vyvolá i v případě, že existující ContextMenu je prázdná nebo null. V tomto případě ale nové ContextMenu nastavené na zdrojovém prvku přijde příliš pozdě, aby se zobrazilo. Pokud uživatel náhodou klikne podruhé pravým tlačítkem myši, tentokrát se zobrazí vaše nová ContextMenu, hodnota není nulová a obslužná rutina správně nahradí a zobrazí nabídku, když se podruhé spustí. Navrhnou se dvě možná alternativní řešení:

  1. Ujistěte se, že obslužné rutiny ContextMenuOpening jsou vždy prováděny na ovládacích prvcích, které mají k dispozici alespoň jeden zástupný symbol ContextMenu, který máte v úmyslu 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 pro hodnotu null, nebo pomocí příznaku v kódu zkontrolovat, jestli byla obslužná procedura spuštěna alespoň jednou. Vzhledem k tomu, že předpokládáte, že se ContextMenu bude zobrazovat, obslužná rutina pak nastaví Handled na true v datech událostí. Pro ContextMenuService, která odpovídá za zobrazení místní nabídky, představuje hodnota true pro 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čili potenciálně podezřelou kontextovou nabídku, dalším krokem je poskytnout novou a pak ji zobrazit. Nastavení nového objektu je v podstatě stejné jako předchozí obslužná rutina: sestavíte nový ContextMenu a nastavíte jeho vlastnost FrameworkElement.ContextMenu zdroje ovládacího prvku. Dalším krokem je, že teď musíte vynutit zobrazení místní nabídky, protože jste potlačili první pokus. Chcete-li vynutit zobrazení, nastavíte vlastnost Popup.IsOpen na true v rámci obslužné rutiny. Buďte opatrní, když to děláte, protože znovu vyvoláte událost ContextMenuOpening otevřením místní nabídky v obslužné rutině. Pokud obslužnou rutinu znovu zadáte, stane se nekonečně rekurzivní. Proto je vždy potřeba zkontrolovat null nebo použít příznak, pokud otevřete kontextové menu z obslužné rutiny události ContextMenuOpening.

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

Poslední 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 aby se nezobrazilo nic, obslužná rutina by měla jednoduše nastavit Handled na true v datech události. ContextMenuService, která je zodpovědná za zobrazení místní nabídky, zkontroluje údaje o události, kterou vyvolala na ovládacím prvku. Pokud byla událost označena Handled kdekoli na trase, akce otevření místní nabídky, která iniciovala událost, se potlačí.

    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é