다음을 통해 공유


방법: ContextMenuOpening 이벤트 처리

ContextMenuOpening 이벤트는 애플리케이션에서 처리되어 표시하기 전에 바로 가기 메뉴를 조정하거나 이벤트 데이터에서 Handled 속성을 true로 설정하여 표시되는 메뉴를 표시하지 않을 수 있습니다. 이벤트 데이터에서 Handledtrue로 설정하는 일반적인 이유는 메뉴를 완전히 새 ContextMenu 개체로 바꾸기 때문이며, 경우에 따라 작업을 취소하고 새 열기를 시작해야 합니다. ContextMenuOpening 이벤트에 대한 처리기를 작성하는 경우 일반적으로 컨트롤에 대한 바로 가기 메뉴를 열고 배치하는 ContextMenu 컨트롤과 서비스 간의 타이밍 문제를 알고 있어야 합니다. 이 항목에서는 다양한 바로 가기 메뉴 열기 시나리오에 대한 코드 기술 중 일부를 설명하고 타이밍 문제가 발생하는 경우를 보여 줍니다.

ContextMenuOpening 이벤트를 처리하는 몇 가지 시나리오가 있습니다.

  • 표시하기 전에 메뉴 항목을 조정합니다.

  • 표시하기 전에 전체 메뉴를 바꿉니다.

  • 기존 바로 가기 메뉴를 완전히 표시하지 않고 바로 가기 메뉴를 표시하지 않습니다.

예제

표시하기 전에 메뉴 항목 조정

기존 메뉴 항목을 조정하는 것은 매우 간단하며 가장 일반적인 시나리오일 수 있습니다. 애플리케이션의 현재 상태 정보 또는 바로 가기 메뉴가 요청된 개체의 속성으로 사용할 수 있는 특정 상태 정보에 대한 응답으로 바로 가기 메뉴 옵션을 추가하거나 빼기 위해 이 작업을 수행할 수 있습니다.

일반적인 방법은 마우스 오른쪽 단추로 클릭한 특정 컨트롤인 이벤트의 원본을 가져와서 ContextMenu 속성을 가져오는 것입니다. 일반적으로 Items 컬렉션을 확인하여 메뉴에 이미 있는 바로 가기 메뉴 항목을 확인한 다음, 컬렉션에 적절한 새 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);
}

표시하기 전에 전체 메뉴 바꾸기

다른 시나리오는 전체 바로 가기 메뉴를 바꾸려는 경우입니다. 물론 이전 코드의 변형을 사용하여 기존 바로 가기 메뉴의 모든 항목을 제거하고 항목 0부터 시작하는 새 항목을 추가할 수도 있습니다. 그러나 바로 가기 메뉴의 모든 항목을 바꾸는 보다 직관적인 접근 방식은 새 ContextMenu를 만들고 항목으로 채운 다음, 컨트롤의 FrameworkElement.ContextMenu 속성을 새 ContextMenu로 설정하는 것입니다.

다음은 ContextMenu를 바꾸기 위한 간단한 처리기 코드입니다. 코드는 둘 이상의 예제 처리기에서 호출되므로 구분되는 사용자 지정 BuildMenu 메서드를 참조합니다.

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를 설정하는 개체에 기존 바로 가기 메뉴가 없는 경우 타이밍 문제가 발생할 수 있습니다. 사용자가 컨트롤을 마우스 오른쪽 단추로 클릭하면 기존 ContextMenu가 비어 있거나 null인 경우에도 ContextMenuOpening이 발생합니다. 그러나 이 경우 원본 요소에 설정한 새로운 ContextMenu가 너무 늦게 도착하여 표시할 수 없습니다. 또한 사용자가 두 번째로 마우스 오른쪽 단추로 클릭하면 이번에는 새 ContextMenu가 나타나고 값이 null이 아니며 처리기가 두 번째로 실행될 때 처리기가 메뉴를 적절하게 바꾸고 표시합니다. 이는 다음과 같은 두 가지 가능한 해결 방법을 제안합니다.

  1. 처리기 코드로 대체하려는 자리 표시자 ContextMenu를 사용할 수 있는 컨트롤에 대해 ContextMenuOpening 처리기가 항상 실행되도록 합니다. 이 경우 이전 예제에 표시된 처리기를 계속 사용할 수 있지만 일반적으로 초기 태그에서 자리 표시자 ContextMenu를 할당하려고 합니다.

    <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일 수 있다고 가정합니다. ContextMenu에서 null을 확인하거나 코드의 플래그를 사용하여 처리기가 한 번 이상 실행되었는지 확인할 수 있습니다. ContextMenu가 표시될 것이라고 가정하므로 처리기는 이벤트 데이터에서 Handledtrue로 설정합니다. 바로 가기 메뉴 표시를 담당하는 ContextMenuService에게 이벤트 데이터의 Handled에 대한 true 값은 이벤트를 발생시킨 바로 가기 메뉴/컨트롤 조합에 대한 디스플레이 취소 요청을 나타냅니다.

이제 잠재적으로 의심되는 바로 가기 메뉴를 표시하지 않았으므로 다음 단계는 새 메뉴를 제공한 다음, 표시하는 것입니다. 새 처리기를 설정하는 것은 기본적으로 이전 처리기와 동일합니다. 새 ContextMenu를 빌드하고 이를 사용하여 제어 원본의 FrameworkElement.ContextMenu 속성을 설정합니다. 추가 단계는 첫 번째 시도를 표시하지 않으므로 바로 가기 메뉴를 강제로 표시해야 한다는 것입니다. 표시를 강제 적용하려면 처리기 내에서 Popup.IsOpen 속성을 true로 설정합니다. 처리기에서 바로 가기 메뉴를 열면 ContextMenuOpening 이벤트가 다시 발생하므로 이 작업을 수행할 때는 주의해야 합니다. 처리기를 다시 입력하면 무한 재귀가 됩니다. ContextMenuOpening 이벤트 처리기 내에서 바로 가기 메뉴를 여는 경우 항상 null을 확인하거나 플래그를 사용해야 하는 이유입니다.

기존 바로 가기 메뉴 표시 안 함 및 바로 가기 메뉴 표시 안 함

메뉴를 완전히 표시하지 않는 처리기를 작성하는 최종 시나리오는 일반적이지 않습니다. 지정된 컨트롤이 바로 가기 메뉴를 표시하지 않아야 하는 경우 사용자가 요청할 때 메뉴를 표시하지 않는 것보다 이를 보장하는 더 적절한 방법이 있을 수 있습니다. 그러나 처리기를 사용하여 바로 가기 메뉴를 표시하지 않고 아무 것도 표시하지 않으려면 처리기는 이벤트 데이터에서 Handledtrue로 설정하기만 하면 됩니다. 바로 가기 메뉴 표시를 담당하는 ContextMenuService는 컨트롤에서 발생한 이벤트의 이벤트 데이터를 확인합니다. 이벤트가 경로에 따라 Handled로 표시된 경우 이벤트를 시작한 바로 가기 메뉴 열기 작업이 표시되지 않습니다.

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

참고 항목