Porady: obsługa zdarzenia ContextMenuOpening

Zdarzenie ContextMenuOpening można obsłużyć w aplikacji, aby dostosować istniejące menu kontekstowe przed wyświetleniem lub pominąć menu, które w przeciwnym razie będzie wyświetlane, ustawiając Handled właściwość na true w danych zdarzenia. Typową przyczyną ustawienia Handledtrue wartości w danych zdarzenia jest zastąpienie menu całkowicie nowym ContextMenu obiektem, który czasami wymaga anulowania operacji i uruchomienia nowego otwarcia. Jeśli piszesz programy obsługi dla ContextMenuOpening zdarzenia, należy pamiętać o problemach z chronometrażem między kontrolką a usługą ContextMenu , która jest odpowiedzialna za otwieranie i pozycjonowanie menu kontekstowych dla kontrolek w ogóle. W tym temacie przedstawiono niektóre techniki kodu dla różnych scenariuszy otwierania menu kontekstowego i przedstawiono przypadek, w którym występuje problem z chronometrażem.

Istnieje kilka scenariuszy obsługi ContextMenuOpening zdarzenia:

  • Dostosowywanie elementów menu przed wyświetleniem.

  • Zastąp całe menu przed wyświetleniem.

  • Całkowicie pomijając wszystkie istniejące menu kontekstowe i nie wyświetlając menu kontekstowego.

Przykład

Dostosowywanie elementów menu przed wyświetleniem

Dostosowanie istniejących elementów menu jest dość proste i prawdopodobnie jest najbardziej typowym scenariuszem. Można to zrobić, aby dodać lub odjąć opcje menu kontekstowego w odpowiedzi na bieżące informacje o stanie w aplikacji lub określone informacje o stanie, które są dostępne jako właściwość w obiekcie, w którym żądane jest menu kontekstowe.

Ogólna technika polega na pobraniu źródła zdarzenia, czyli określonej kontrolce, która została kliknięta prawym przyciskiem myszy i pobraniu ContextMenu właściwości z niego. Zazwyczaj chcesz sprawdzić Items kolekcję, aby zobaczyć, jakie elementy menu kontekstowego już istnieją w menu, a następnie dodać lub usunąć odpowiednie nowe MenuItem elementy do lub z kolekcji.

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

Zamienianie całego menu przed wyświetleniem

Alternatywnym scenariuszem jest zastąpienie całego menu kontekstowego. Oczywiście można również użyć odmiany poprzedniego kodu, aby usunąć każdy element istniejącego menu kontekstowego i dodać nowe, zaczynając od zera elementu. Jednak bardziej intuicyjne podejście do zastępowania wszystkich elementów w menu kontekstowym polega na utworzeniu nowego ContextMenuelementu , wypełnieniu go elementami, a następnie ustawieniu FrameworkElement.ContextMenu właściwości kontrolki na nową ContextMenu.

Poniżej znajduje się prosty kod procedury obsługi zastępujący ContextMenuelement . Kod odwołuje się do metody niestandardowej BuildMenu , która jest oddzielona, ponieważ jest wywoływana przez więcej niż jeden z przykładowych procedur obsługi.

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

Jeśli jednak używasz tego stylu obsługi dla ContextMenuOpeningprogramu , możesz potencjalnie uwidocznić problem z chronometrażem, jeśli obiekt, w którym ustawiono ContextMenu , nie ma istniejącego menu kontekstowego. Gdy użytkownik kliknie prawym przyciskiem myszy kontrolkę, zostanie zgłoszony nawet wtedy, ContextMenuOpening gdy istniejący ContextMenu element jest pusty lub ma wartość null. Ale w tym przypadku, cokolwiek nowego ContextMenu ustawionego na elemecie źródłowym pojawia się za późno, aby być wyświetlane. Ponadto jeśli użytkownik po raz drugi kliknie prawym przyciskiem myszy, tym razem pojawi się nowy ContextMenu , wartość jest niepusta, a procedura obsługi poprawnie zastąpi i wyświetli menu po drugim uruchomieniu programu obsługi. Sugeruje to dwa możliwe obejścia:

  1. Upewnij się, że ContextMenuOpening programy obsługi są zawsze uruchamiane względem kontrolek, które mają co najmniej dostępny symbol zastępczy ContextMenu , który ma zostać zastąpiony przez kod programu obsługi. W takim przypadku nadal można użyć procedury obsługi pokazanej w poprzednim przykładzie, ale zazwyczaj chcesz przypisać symbol zastępczy ContextMenu w początkowym znaczniku:

    <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. Załóżmy, że początkowa ContextMenu wartość może mieć wartość null na podstawie pewnej wstępnej logiki. Możesz sprawdzić ContextMenu wartość null lub użyć flagi w kodzie, aby sprawdzić, czy procedura obsługi została uruchomiona co najmniej raz. Ponieważ zakładasz, że element ContextMenu ma być wyświetlany, procedura obsługi następnie ustawia wartość Handled w true danych zdarzenia. ContextMenuService W przypadku wyświetlania true menu kontekstowego wartość dla Handled danych zdarzenia reprezentuje żądanie anulowania wyświetlania dla kombinacji menu kontekstowego/kontrolki, która wywołała zdarzenie.

Teraz, gdy pominięto potencjalnie podejrzane menu kontekstowe, następnym krokiem jest podanie nowego, a następnie wyświetlenie go. Ustawienie nowego jest w zasadzie takie samo jak poprzedni program obsługi: tworzysz nową ContextMenu i ustawiasz jej właściwość źródła FrameworkElement.ContextMenu kontroli. Dodatkowym krokiem jest to, że musisz teraz wymusić wyświetlanie menu kontekstowego, ponieważ pominięto pierwszą próbę. Aby wymusić Popup.IsOpen wyświetlanie, należy ustawić właściwość na true wartość w programie obsługi. Podczas wykonywania tej czynności należy zachować ostrożność, ponieważ otwarcie menu kontekstowego w programie obsługi powoduje ponowne wywołanie ContextMenuOpening zdarzenia. Jeśli ponownie wygenerujesz procedurę obsługi, staje się ona nieskończenie rekursywna. Dlatego zawsze musisz sprawdzić null lub użyć flagi, jeśli otworzysz menu kontekstowe z poziomu ContextMenuOpening programu obsługi zdarzeń.

Pomijanie dowolnego istniejącego menu kontekstowego i wyświetlanie menu kontekstowego bez menu kontekstowego

Ostatni scenariusz, pisząc procedurę obsługi, która całkowicie pomija menu, jest rzadkością. Jeśli dana kontrolka nie ma wyświetlać menu kontekstowego, prawdopodobnie istnieją bardziej odpowiednie sposoby zapewnienia tego niż pomijając menu tylko wtedy, gdy użytkownik zażąda go. Jeśli jednak chcesz użyć programu obsługi, aby pominąć menu kontekstowe i pokazać nic, program obsługi powinien po prostu ustawić wartość Handledtrue w danych zdarzenia. Element ContextMenuService odpowiedzialny za wyświetlanie menu kontekstowego sprawdzi dane zdarzenia zdarzenia zgłoszonego w kontrolce. Jeśli zdarzenie zostało oznaczone Handled w dowolnym miejscu wzdłuż trasy, zostanie pominięta akcja otwierania menu kontekstowego, która zainicjowała zdarzenie.

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

Zobacz też