방법: ContextMenuOpening 이벤트 처리How to: Handle the ContextMenuOpening Event

합니다 ContextMenuOpening 는 기존 상황에 맞는 메뉴 표시 하기 전에 설정 하 여 표시 되는 메뉴를 표시 하지 않으려면를 조정 하거나 응용 프로그램에서 이벤트를 처리할 수 있습니다 합니다 Handled 속성을 true 이벤트 데이터의 합니다.The ContextMenuOpening event can be handled in an application to either adjust an existing context menu prior to display or to suppress the menu that would otherwise be displayed by setting the Handled property to true in the event data. 설정에 대 한 일반적인 이유 Handledtrue 전적으로 사용 하 여 새 메뉴를 바꾸려면 데이터가 이벤트 ContextMenu 개체, 경우에 따라 요구 하는 작업을 취소 하 고 새로운 열기를 시작 합니다.The typical reason for setting Handled to true in the event data is to replace the menu entirely with a new ContextMenu object, which sometimes requires canceling the operation and starting a new open. 에 대 한 처리기를 작성 하는 경우를 ContextMenuOpening 이벤트 알아야 사이의 타이밍 문제에 대 한 ContextMenu 컨트롤과 열고 일반적 컨트롤에 대 한 상황에 맞는 메뉴의 위치를 지정 하는 일을 담당 하는 서비스.If you write handlers for the ContextMenuOpening event, you should be aware of timing issues between a ContextMenu control and the service that is responsible for opening and positioning context menus for controls in general. 이 항목에서는 다양 한 상황에 맞는 메뉴 열기 시나리오에 대 한 코드 기술 중 일부를 보여 줍니다 및 타이밍 문제를 고려해 야 하는 있는 경우를 보여 줍니다.This topic illustrates some of the code techniques for various context menu opening scenarios and illustrates a case where the timing issue comes into play.

일부의 시나리오 처리에 대 한는 ContextMenuOpening 이벤트:There are several scenarios for handling the ContextMenuOpening event:

  • 메뉴 항목을 먼저 호출한 다음 표시를 조정합니다.Adjusting the menu items before display.

  • 표시 하기 전에 전체 메뉴를 대체합니다.Replacing the entire menu before display.

  • 완전히 모든 기존 상황에 맞는 메뉴를 표시 안 함 및 없는 상황에 맞는 메뉴를 표시 합니다.Completely suppressing any existing context menu and displaying no context menu.

예제Example

메뉴 항목을 먼저 호출한 다음 표시를 조정합니다.Adjusting the Menu Items Before Display

기존 메뉴 항목을 조정 매우 간단 하 고 가장 일반적인 시나리오 일 것입니다.Adjusting the existing menu items is fairly simple and is probably the most common scenario. 에 더하거나 뺄 상황에 맞는 메뉴 또는 상황에 맞는 메뉴를 요청 하는 개체의 속성으로 사용할 수 있는 특정 상태 정보에 응용 프로그램의 현재 상태 정보에 대 한 응답 하기 위해이 수행할 수 있습니다.You might do this in order to add or subtract context menu options in response to current state information in your application or particular state information that is available as a property on the object where the context menu is requested.

일반적인 방법은 마우스 오른쪽 단추로 클릭는 특정 컨트롤 상태인 이벤트의 소스를 가져오고 가져오는 ContextMenu 속성을 합니다.The general technique is to get the source of the event, which is the specific control that was right-clicked, and get the ContextMenu property from it. 일반적으로 확인 하려는 합니다 Items 컬렉션을 이미 메뉴에 존재 하 고 다음 추가 하거나 제거 적절 한 새 상황에 맞는 메뉴 항목을 참조 MenuItem 하거나 컬렉션에서 항목입니다.You typically want to check the Items collection to see what context menu items already exist in the menu, and then add or remove appropriate new MenuItem items to or from the collection.

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

표시 하기 전에 전체 메뉴를 대체합니다.Replacing the Entire Menu Before Display

다른 시나리오는 전체 상황에 맞는 메뉴를 대체 하려는 경우.An alternative scenario is if you want to replace the entire context menu. 물론 위의 코드에서의 변형 기존 상황에 맞는 메뉴의 모든 항목을 제거 하 고 항목 0부터 시작 하는 새 템플릿을 추가할에 사용할 수 있습니다.You could of course also use a variation of the preceding code, to remove every item of an existing context menu and add new ones starting with item zero. 새 상황에 맞는 메뉴에서 모든 항목을 바꾸는 보다 직관적인 방법 이지만 ContextMenu항목으로 채울, 설정한 후 합니다 FrameworkElement.ContextMenu 속성을 새 컨트롤의 ContextMenu.But the more intuitive approach for replacing all items in the context menu is to create a new ContextMenu, populate it with items, and then set the FrameworkElement.ContextMenu property of a control to be the new ContextMenu.

다음은 대체 하는 것에 대 한 간단한 처리기 코드는 ContextMenu합니다.The following is the simple handler code for replacing a ContextMenu. 코드는 사용자 지정 참조 BuildMenu 분산 되어 있는 호출 되므로 둘 이상의 예제 처리기 메서드.The code references a custom BuildMenu method, which is separated out because it is called by more than one of the example handlers.

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

그러나 이러한 처리기에 대 한이 스타일을 사용 하는 경우 ContextMenuOpening, 경우 타이밍 문제가 노출 될 가능성이 있습니다 설정 하는 개체는 ContextMenu 기존 상황에 맞는 메뉴가 없습니다.However, if you use this style of handler for ContextMenuOpening, you can potentially expose a timing issue if the object where you are setting the ContextMenu does not have a preexisting context menu. 사용자 컨트롤을 마우스 오른쪽 단추로 클릭할 때 ContextMenuOpening 발생 하는 경우에 기존 ContextMenu 비어 있거나 null입니다.When a user right-clicks a control, ContextMenuOpening is raised even if the existing ContextMenu is empty or null. 하지만 경우에 새 ContextMenu 원본에 설정한 요소 표시할 너무 늦게 도착 합니다.But in this case, whatever new ContextMenu you set on the source element arrives too late to be displayed. 또한 사용자를 두 번째로 마우스 오른쪽 단추로 클릭 경우 이번 새 ContextMenu 나타나면 값이 null이 아닌 하 고 처리기를 제대로 바꾸고 처리기를 두 번째로 실행 될 때 메뉴를 표시 합니다.Also, if the user happens to right-click a second time, this time your new ContextMenu appears, the value is non null, and your handler will properly replace and display the menu when the handler runs a second time. 이 두 가지 가능한 해결 방법을 제안합니다.This suggests two possible workarounds:

  1. 되도록 ContextMenuOpening 항상 하나 이상의 자리 표시자 컨트롤에 대해 실행 하는 처리기 ContextMenu 사용 가능한 처리기 코드를 교체 하려고 하는 합니다.Insure that ContextMenuOpening handlers always run against controls that have at least a placeholder ContextMenu available, which you intend to be replaced by the handler code. 이 경우 이전 예에서 같이 처리기를 여전히 사용할 수 있지만 일반적으로 자리 표시자를 할당 하려는 ContextMenu 초기 태그에서:In this case, you can still use the handler shown in the previous example, but you typically want to assign a placeholder ContextMenu in the initial markup:

    <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. 가정 초기 ContextMenu 값 null 일 수 있음, 일부 예비 논리를 기반으로 합니다.Assume that the initial ContextMenu value might be null, based on some preliminary logic. 할 수 있는지 확인 하거나 ContextMenu null 또는 처리기 되었는지 여부를 확인 하는 플래그를 사용 하 여 한 번 이상를 실행 합니다.You could either check ContextMenu for null, or use a flag in your code to check whether your handler has been run at least once. 한다고 가정 하기 때문에 ContextMenu 다음을 설정 하는 방법에 대 한 수를 표시, 처리기 Handledtrue 이벤트 데이터에서입니다.Because you assume that the ContextMenu is about to be displayed, your handler then sets Handled to true in the event data. ContextMenuService 상황에 맞는 메뉴 표시를 담당 하는 true 에 대 한 값 Handled 이벤트 데이터는 상황에 맞는 메뉴에 대 한 표시를 취소 / 컨트롤 이벤트를 발생 시킨 조합 요청을 나타냅니다.To the ContextMenuService that is responsible for context menu display, a true value for Handled in the event data represents a request to cancel the display for the context menu / control combination that raised the event.

다음 단계 새 대시보드를 제공 하는 잠재적으로 의심 스러운 상황에 맞는 메뉴 표시 되지 않는 했으므로 표시 합니다.Now that you have suppressed the potentially suspect context menu, the next step is to supply a new one, then display it. 하나는 기본적으로 이전 처리기로 동일한 새 설정: 새 빌드하면 ContextMenu 컨트롤 소스를 설정 하 고 FrameworkElement.ContextMenu 속성을 합니다.Setting the new one is basically the same as the previous handler: you build a new ContextMenu and set the control source's FrameworkElement.ContextMenu property with it. 추가 단계는 첫 번째 시도 억제 하기 때문에 이제 강제 해야 상황에 맞는 메뉴를 표시 합니다.The additional step is that you must now force the display of the context menu, because you suppressed the first attempt. 표시 하도록 설정 합니다 Popup.IsOpen 속성을 true 처리기 내에서.To force the display, you set the Popup.IsOpen property to true within the handler. 주의이 작업을 수행 하는 경우 처리기에서 상황에 맞는 메뉴를 열고 발생 하기 때문에 ContextMenuOpening 이벤트 다시 합니다.Be careful when you do this, because opening the context menu in the handler raises the ContextMenuOpening event again. 처리기를 다시 입력 하는 경우 재귀 됩니다 무한 합니다.If you reenter your handler, it becomes infinitely recursive. 이것이 항상 확인 해야 하는 이유 null 내에서 상황에 맞는 메뉴를 열면 플래그를 사용 하 여 또는 ContextMenuOpening 이벤트 처리기입니다.This is why you always need to check for null or use a flag if you open a context menu from within a ContextMenuOpening event handler.

모든 기존 상황에 맞는 메뉴를 표시 안 함 및 상황에 맞는 메뉴 표시Suppressing Any Existing Context Menu and Displaying No Context Menu

메뉴를 완전히를 억제 하는 처리기를 작성 하는 마지막 시나리오는 흔하지 않습니다.The final scenario, writing a handler that suppresses a menu totally, is uncommon. 지정된 된 컨트롤 상황에 맞는 메뉴를 표시 하지 않는, 경우에이 보장 하기 보다는 사용자가 요청할 때 메뉴를 억제 하 여 보다 적합할 가지가 있습니다.If a given control is not supposed to display a context menu, there are probably more appropriate ways to assure this than by suppressing the menu just when a user requests it. 처리기를 사용 하 여 상황에 맞는 메뉴를 표시 하지 않으려면를 아무 것도 표시 하려는 경우 처리기를 설정 하기만 해야 하지만 Handledtrue 이벤트 데이터에서입니다.But if you want to use the handler to suppress a context menu and show nothing, then your handler should simply set Handled to true in the event data. ContextMenuService 하는 상황에 맞는 메뉴를 표시 하는 컨트롤에서 발생 시킨 이벤트의 이벤트 데이터를 확인 합니다.The ContextMenuService that is responsible for displaying a context menu will check the event data of the event it raised on the control. 이벤트로 표시 되어 있으면 Handled 어디서 나 경로 따르는 다음 이벤트를 시작한 상황에 맞는 메뉴 열기 작업 표시 되지 않습니다.If the event was marked Handled anywhere along the route, then the context menu open action that initiated the event is suppressed.

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

참고자료See also