라우트된 이벤트 개요(WPF .NET)

WPF(Windows Presentation Foundation) 애플리케이션 개발자 및 구성 요소 작성자는 라우트된 이벤트를 사용하여 요소 트리를 통해 이벤트를 전파하고 트리의 여러 수신기에서 이벤트 처리기를 호출할 수 있습니다. 이러한 기능은 CLR(공용 언어 런타임) 이벤트에서 찾을 수 없습니다. 여러 WPF 이벤트는 ButtonBase.Click과(와) 같은 라우트된 이벤트입니다. 이 문서에서는 기본 라우트된 이벤트 개념에 대해 설명하고 라우트된 이벤트에 응답하는 시기와 방법에 대한 지침을 제공합니다.

중요

.NET 7 및 .NET 6에 관한 데스크톱 가이드 설명서는 제작 중입니다.

필수 구성 요소

이 문서에서는 CLR(공용 언어 런타임), 개체 지향 프로그래밍 및 WPF 요소 레이아웃을 트리로 개념화하는 방법에 대한 기본 지식을 가정합니다. XAML(Extensible Application Markup Language)에 익숙하고 WPF 애플리케이션을 작성하는 방법을 알고 있으면 이 문서의 예제를 따라하는 데 도움이 됩니다.

라우트된 이벤트란?

기능 또는 구현 측면에서 라우트된 이벤트를 고려할 수 있습니다.

  • 기능적 관점에서 라우트된 이벤트는 이벤트 원본뿐만 아니라 요소 트리의 여러 수신기에서 처리기를 호출할 수 있는 이벤트 유형입니다. 이벤트 수신기는 이벤트 처리기가 연결되고 호출되는 요소입니다. 이벤트 원본은 원래 이벤트를 발생시킨 요소 또는 개체입니다.

  • 구현 관점에서 라우트된 이벤트는 WPF 이벤트 시스템에 등록되고 RoutedEvent 클래스의 인스턴스로 뒷받침되고 WPF 이벤트 시스템에서 처리되는 이벤트입니다. 일반적으로 라우트된 이벤트는 CLR 이벤트 "래퍼"를 사용하여 구현되어 CLR 이벤트와 마찬가지로 XAML 및 코드 숨김에서 처리기를 연결할 수 있도록 합니다.

WPF 애플리케이션에는 일반적으로 XAML에서 선언되거나 코드에서 인스턴스화된 많은 요소가 포함됩니다. 애플리케이션의 요소는 해당 요소 트리 내에 있습니다. 라우트된 이벤트가 정의된 방법에 따라 이벤트가 원본 요소에서 발생하는 경우:

  • 원본 요소에서 루트 요소(일반적으로 페이지 또는 창)로 요소 트리를 통해 버블 업 됩니다.
  • 요소 트리를 통해 루트 요소에서 원본 요소로 터널링됩니다.
  • 요소 트리를 통과하지 않고 원본 요소에서만 발생합니다.

다음 부분적 요소 트리를 살펴보세요.

<Border Height="30" Width="200" BorderBrush="Gray" BorderThickness="1">
    <StackPanel Background="LightBlue" Orientation="Horizontal" Button.Click="YesNoCancelButton_Click">
        <Button Name="YesButton">Yes</Button>
        <Button Name="NoButton">No</Button>
        <Button Name="CancelButton">Cancel</Button>
    </StackPanel>
</Border>

요소 트리는 다음 그림과 같이 렌더링됩니다.

예, 아니요 및 취소 단추.

세 개의 단추는 각각 잠재적인 Click 이벤트 원본입니다. 단추 중 하나를 클릭하면 단추에서 루트 요소로 버블 업되는 Click 이벤트가 발생합니다. ButtonBorder 요소에는 이벤트 처리기가 연결되어 있지 않지만 StackPanel은(는) 연결됩니다. 표시되지 않는 트리의 위쪽에 있는 다른 요소에도 Click 이벤트 처리기가 연결되어 있을 수 있습니다. Click 이벤트가 StackPanel 요소에 도달하면 WPF 이벤트 시스템은 해당 요소에 연결된 YesNoCancelButton_Click 처리기를 호출합니다. 예제에서 Click 이벤트에 대한 이벤트 경로는 Button ->StackPanel ->Border -> 연속 부모 요소입니다.

참고

원래 라우트된 이벤트를 발생시킨 요소는 이벤트 처리기 매개 변수에서 RoutedEventArgs.Source(으)로 식별됩니다. 이벤트 수신기는 이벤트 처리기가 연결되고 호출되는 요소이며 이벤트 처리기 매개 변수에서 보낸 사람으로 식별됩니다.

라우트된 이벤트에 대한 최상위 시나리오

다음은 라우트된 이벤트 개념에 동기를 부여하고 일반적인 CLR 이벤트와 구별하는 몇 가지 시나리오입니다.

  • 컨트롤 컴퍼지션 및 캡슐화: WPF의 다양한 컨트롤에는 서식 있는 콘텐츠 모델이 있습니다. 예를 들어 단추의 시각적 트리를 효과적으로 확장하는 Button 내부에 이미지를 배치할 수 있습니다. 그러나 추가된 이미지는 사용자가 이미지 픽셀을 클릭할 때 응답해야 하는 단추의 적중 테스트 동작을 중단해서는 안 됩니다.

  • 단일 처리기 첨부 지점: 각 단추의 Click 이벤트에 대한 처리기를 등록할 수 있지만 라우트된 이벤트를 사용하면 이전 XAML 예제와 같이 단일 처리기를 연결할 수 있습니다. 이렇게 하면 각 단추의 Click 이벤트를 등록하지 않고도 단추 추가 또는 제거와 같은 단일 처리기에서 요소 트리를 변경할 수 있습니다. Click 이벤트가 발생하면 처리기 논리에서 이벤트가 발생한 위치를 확인할 수 있습니다. 이전에 표시된 XAML 요소 트리에 지정된 다음 처리기에는 해당 논리가 포함됩니다.

    private void YesNoCancelButton_Click(object sender, RoutedEventArgs e)
    {
        FrameworkElement sourceFrameworkElement = e.Source as FrameworkElement;
        switch (sourceFrameworkElement.Name)
        {
            case "YesButton":
                // YesButton logic.
                break;
            case "NoButton":
                // NoButton logic.
                break;
            case "CancelButton":
                // CancelButton logic.
                break;
        }
        e.Handled = true;
    }
    
    Private Sub YesNoCancelButton_Click(sender As Object, e As RoutedEventArgs)
        Dim frameworkElementSource As FrameworkElement = TryCast(e.Source, FrameworkElement)
    
        Select Case frameworkElementSource.Name
            Case "YesButton"
                ' YesButton logic.
            Case "NoButton"
                ' NoButton logic.
            Case "CancelButton"
                ' CancelButton logic.
        End Select
    
        e.Handled = True
    End Sub
    
  • 클래스 처리: 라우트된 이벤트는 클래스에서 정의하는 클래스 이벤트 처리기를 지원합니다. 클래스 처리기는 클래스의 인스턴스에서 동일한 이벤트에 대한 인스턴스 처리기 전에 이벤트를 처리합니다.

  • 리플렉션 없이 이벤트 참조: 라우트된 각 이벤트는 이벤트를 식별하기 위해 정적 또는 런타임 리플렉션이 필요하지 않은 강력한 이벤트 식별 기술을 제공하는 RoutedEvent 필드 식별자를 만듭니다.

라우트된 이벤트를 구현하는 방법

라우트된 이벤트는 WPF 이벤트 시스템에 등록되고 RoutedEvent 클래스의 인스턴스로 뒷받침되고 WPF 이벤트 시스템에서 처리되는 이벤트입니다. 등록에서 얻은 RoutedEvent 인스턴스는 일반적으로 등록한 클래스의 public static readonly 멤버로 저장됩니다. 해당 클래스를 이벤트 "소유자" 클래스라고 합니다. 일반적으로 라우트된 이벤트는 동일한 이름의 CLR 이벤트 "래퍼"를 구현합니다. CLR 이벤트 래퍼에는 언어별 이벤트 구문을 통해 XAML 및 코드 숨김에서 처리기를 연결할 수 있도록 하는 addremove 접근자가 포함되어 있습니다. addremove 접근자가 CLR 구현을 재정의하고 라우트된 이벤트 AddHandlerRemoveHandler 메서드를 호출합니다. 라우트된 이벤트 지원 및 연결 메커니즘은 종속성 속성이 DependencyProperty 클래스에서 지원되고 WPF 속성 시스템에 등록된 CLR 속성이 되는 방식과 개념적으로 비슷합니다.

다음 예제에서는 Tap개의 라우트된 이벤트를 등록하고 반환된 RoutedEvent 인스턴스를 저장하며 CLR 이벤트 래퍼를 구현합니다.

// Register a custom routed event using the Bubble routing strategy.
public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
    name: "Tap",
    routingStrategy: RoutingStrategy.Bubble,
    handlerType: typeof(RoutedEventHandler),
    ownerType: typeof(CustomButton));

// Provide CLR accessors for adding and removing an event handler.
public event RoutedEventHandler Tap
{
    add { AddHandler(TapEvent, value); }
    remove { RemoveHandler(TapEvent, value); }
}
' Register a custom routed event using the Bubble routing strategy.
Public Shared ReadOnly TapEvent As RoutedEvent = EventManager.RegisterRoutedEvent(
    name:="Tap",
    routingStrategy:=RoutingStrategy.Bubble,
    handlerType:=GetType(RoutedEventHandler),
    ownerType:=GetType(CustomButton))

' Provide CLR accessors for adding and removing an event handler.
Public Custom Event Tap As RoutedEventHandler
    AddHandler(value As RoutedEventHandler)
        [AddHandler](TapEvent, value)
    End AddHandler

    RemoveHandler(value As RoutedEventHandler)
        [RemoveHandler](TapEvent, value)
    End RemoveHandler

    RaiseEvent(sender As Object, e As RoutedEventArgs)
        [RaiseEvent](e)
    End RaiseEvent
End Event

라우트 전략

라우트된 이벤트는 다음 세 가지 라우트 전략 중 하나를 사용합니다.

  • 버블링: 이벤트 소스에서 이벤트 처리기가 호출됩니다. 그런 다음, 라우트된 이벤트는 연속 부모 요소로 라우팅되어 요소 트리 루트에 도달할 때까지 해당 이벤트 처리기를 차례로 호출합니다. 대부분의 라우트된 이벤트는 버블링 라우트 전략을 사용합니다. 버블링 라우트된 이벤트는 일반적으로 복합 컨트롤 또는 기타 UI 요소에서 입력 또는 상태 변경을 보고하는 데 사용됩니다.

  • 터널링: 처음에 요소 트리 루트에 있는 이벤트 처리기가 호출됩니다. 그런 다음, 라우트된 이벤트는 연속된 자식 요소로 라우팅되어 이벤트 원본에 도달할 때까지 해당 이벤트 처리기를 차례로 호출합니다. 터널링 경로를 따르는 이벤트를 미리 보기 이벤트라고도 합니다. WPF 입력 이벤트는 일반적으로 미리 보기 및 버블링 쌍으로 구현됩니다.

  • 직접: 이벤트 소스에서 이벤트 처리기가 호출됩니다. 이 비 라우팅 전략은 표준 CLR 이벤트인 Windows Forms UI 프레임워크 이벤트와 유사합니다. CLR 이벤트와 달리 직접 라우트된 이벤트는 클래스 처리를 지원하며 EventSettersEventTriggers에서 사용할 수 있습니다.

라우트된 이벤트를 사용하는 이유는 무엇인가요?

애플리케이션 개발자가 항상 처리 중인 이벤트가 라우트된 이벤트로 구현되는지 알거나 주의할 필요는 없습니다. 라우트된 이벤트에는 특별한 동작이 있지만 이벤트가 발생한 요소에서 해당 이벤트를 처리할 경우 이 동작은 대체로 표시되지 않습니다. 그러나 라우트된 이벤트는 복합 컨트롤 내에서와 같이 자식 요소에서 발생하는 이벤트를 처리하기 위해 부모 요소에 이벤트 처리기를 연결하려는 경우에 관련이 있습니다.

라우트된 이벤트 수신기는 클래스의 멤버로 처리되는 라우트된 이벤트가 필요하지 않습니다. UIElement 또는 ContentElement는 라우트된 이벤트의 이벤트 수신기가 될 수 있습니다. 시각적 요소는 UIElement 또는 ContentElement에서 파생되므로 라우트된 이벤트를 애플리케이션의 서로 다른 요소 간의 이벤트 정보 교환을 지원하는 개념적 "인터페이스"로 사용할 수 있습니다. 라우트된 이벤트에 대한 이 “인터페이스” 개념은 특히 입력 이벤트에 적합합니다.

라우트된 이벤트는 각 수신기가 동일한 인스턴스 이벤트 데이터에 액세스할 수 있으므로 이벤트 경로를 따라 요소 간에 이벤트 정보 교환을 지원합니다. 한 요소가 이벤트 데이터에서 무언가를 변경하면 해당 변경 내용이 이벤트 경로의 후속 요소에 표시됩니다.

라우팅 측면 외에도 다음과 같은 이유로 표준 CLR 이벤트 대신 라우트된 이벤트를 구현하도록 선택할 수 있습니다.

  • EventSettersEventTriggers와 같은 일부 WPF 스타일 지정 및 템플릿 지정 기능을 사용하려면 참조된 이벤트가 라우트된 이벤트여야 합니다.

  • 라우트된 이벤트는 수신기 클래스의 모든 인스턴스에 있는 동일한 이벤트에 대한 인스턴스 처리기보다 먼저 이벤트를 처리하는 클래스 이벤트 처리기를 지원합니다. 이 기능은 컨트롤 디자인에서 매우 유용합니다. 클래스 처리기는 인스턴스 처리기가 실수로 억제할 수 없는 이벤트 기반 클래스 동작을 강제 실행할 수 있기 때문입니다.

라우트된 이벤트 처리기 연결 및 구현

XAML에서는 이벤트 수신기 요소에서 이벤트 이름을 특성으로 선언하여 이벤트 처리기를 요소에 연결합니다. 특성 값은 처리기 메서드 이름입니다. 처리기 메서드는 XAML 페이지의 코드 숨김 부분 클래스에서 구현되어야 합니다. 이벤트 수신기는 이벤트 처리기가 연결되고 호출되는 요소입니다.

수신기 클래스의 멤버(상속되거나 다른 경우)인 이벤트의 경우 다음과 같이 처리기를 연결할 수 있습니다.

<Button Name="Button1" Click="Button_Click">Click me</Button>

이벤트가 수신기 클래스의 멤버가 아닌 경우 정규화된 이벤트 이름을 <owner type>.<event name> 형식으로 사용해야 합니다. 예를 들어 StackPanel 클래스는 Click 이벤트를 구현하지 않으므로 해당 요소까지 버블 업하는 Click 이벤트에 대한 StackPanel 처리기를 연결하려면 정규화된 이벤트 이름 구문을 사용해야 합니다.

<StackPanel Name="StackPanel1" Button.Click="Button_Click">
    <Button>Click me</Button>
</StackPanel>

코드 숨김에서 이벤트 처리기 메서드의 서명은 라우트된 이벤트의 대리자 형식과 일치해야 합니다. Click 이벤트에 대한 RoutedEventHandler 대리자의 sender 매개 변수는 이벤트 처리기가 연결된 요소를 지정합니다. args 대리자의 RoutedEventHandler 매개 변수에는 이벤트 데이터가 포함됩니다. Button_Click 이벤트 처리기에 대한 호환 가능한 코드 숨김 구현은 다음과 같습니다.

private void Button_Click(object sender, RoutedEventArgs e)
{
    // Click event logic.
}
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
    ' Click event logic.
End Sub

RoutedEventHandler은(는) 기본 라우트된 이벤트 처리기 대리자이지만 일부 컨트롤 또는 구현 시나리오에는 보다 특수화된 이벤트 데이터를 지원하는 다른 대리자가 필요합니다. 예를 들어 라우트된 DragEnter 이벤트의 경우 처리기는 DragEventHandler 대리자를 구현해야 합니다. 이렇게 하면 처리기 코드는 끌기 작업의 클립보드 페이로드를 포함하는 이벤트 데이터의 DragEventArgs.Data 속성에 액세스할 수 있습니다.

라우트된 이벤트 처리기를 추가하기 위한 XAML 구문은 표준 CLR 이벤트 처리기의 경우와 동일합니다. XAML에서 이벤트 처리기 추가에 대한 자세한 내용은 WPF의 XAML을 참조하세요. XAML을 사용하여 이벤트 처리기를 요소에 추가하는 방법의 전체 예제를 보려면 라우트된 이벤트 처리 방법을 참조하세요.

코드를 사용하여 라우트된 이벤트에 대한 이벤트 처리기를 요소에 연결하려면 일반적으로 다음 두 가지 옵션이 있습니다.

  • AddHandler 메서드를 직접 호출합니다. 라우트된 이벤트 처리기는 항상 이러한 방식으로 연결할 수 있습니다. 이 예제에서는 AddHandler 메서드를 사용하여 단추에 Click 이벤트 처리기를 연결합니다.

    Button1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
    
    Button1.[AddHandler](ButtonBase.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
    

    단추의 Click이벤트에 대한 처리기를 StackPanel1 이름의 StackPanel과(와) 같은 이벤트 경로의 다른 요소에 연결하려면:

    StackPanel1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
    
    StackPanel1.[AddHandler](ButtonBase.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
    
  • 라우트된 이벤트가 CLR 이벤트 래퍼를 구현하는 경우 표준 CLR 이벤트와 마찬가지로 언어별 이벤트 구문을 사용하여 이벤트 처리기를 추가합니다. 대부분의 기존 WPF 라우트된 이벤트는 CLR 래퍼를 구현하므로 언어별 이벤트 구문을 사용하도록 설정합니다. 이 예제에서는 언어별 구문을 사용하여 단추에 Click 이벤트 처리기를 연결합니다.

    Button1.Click += Button_Click;
    
    AddHandler Button1.Click, AddressOf Button_Click
    

이벤트 처리기를 코드에 추가하는 방법의 예제를 보려면 코드를 사용하여 이벤트 처리기 추가를 참조하세요. Visual Basic에서 코딩 중인 경우 Handles 키워드를 사용하여 처리기를 처리기 선언의 일부로 추가할 수도 있습니다. 자세한 내용은 Visual Basic 및 WPF 이벤트 처리를 참조하세요.

Handled 개념

라우트된 모든 이벤트는 RoutedEventArgs 클래스인 이벤트 데이터에 대한 공통 기본 클래스를 공유합니다. RoutedEventArgs 클래스는 부울 Handled 속성을 정의합니다. Handled 속성의 목적은 이벤트 경로를 따라 있는 이벤트 처리기가 라우트된 이벤트를 처리됨으로 표시할 수 있게 하는 것입니다. 이벤트를 처리된 것으로 표시하려면 이벤트 처리기 코드에서 값을 Handled에서 true(으)로 설정합니다.

Handled의 값은 라우트된 이벤트가 이벤트 경로를 따라 이동할 때 처리되는 방법에 영향을 미칩니다. 라우트된 이벤트의 공유 이벤트 데이터에서 Handled이(가) true인 경우 이벤트 경로를 따라 다른 요소에 연결된 핸들러는 일반적으로 해당 특정 이벤트 인스턴스에 대해 호출되지 않습니다. 가장 일반적인 처리기 시나리오에서 이벤트를 처리됨으로 표시하면 이벤트 경로에 따라 인스턴스 또는 클래스 처리기의 후속 처리기가 해당 특정 이벤트 인스턴스에 응답하지 않도록 효과적으로 중지됩니다. 그러나 처리된 것으로 표시된 라우트된 이벤트에 응답하기 위해 이벤트 처리기가 필요한 드문 경우는 다음과 같습니다.

Handled의 개념은 애플리케이션을 디자인하고 이벤트 처리기를 코딩하는 방법에 영향을 줄 수 있습니다. 라우트된 이벤트를 처리하기 위한 간단한 프로토콜로 Handled을(를) 개념화할 수 있습니다. 이 프로토콜을 사용하는 방법은 사용자에게 달려 있지만 Handled 매개 변수의 예상 용도는 다음과 같습니다.

  • 라우트된 이벤트가 처리됨으로 표시되면 해당 경로를 따라 있는 다른 요소가 해당 라우트된 이벤트를 다시 처리할 필요가 없습니다.

  • 라우트된 이벤트가 처리된 것으로 표시되지 않으면 이벤트 경로의 앞부분에 있는 수신기에 이벤트에 대한 처리기가 없거나 등록된 처리기 중 어느 것도 이벤트를 처리된 것으로 표시하는 것을 정당화하는 방식으로 이벤트에 응답하지 않습니다. 현재 수신기의 처리기에는 세 가지 가능한 작업 과정이 있습니다.

    • 아무 작업도 수행하지 않습니다. 이벤트는 처리되지 않은 상태로 유지되고 트리의 다음 수신기로 라우팅됩니다.

    • 이벤트에 대한 응답으로 코드를 실행하지만 이벤트를 처리된 것으로 표시하는 것이 정당화되는 범위는 아닙니다. 이벤트는 처리되지 않은 상태로 유지되고 트리의 다음 수신기로 라우팅됩니다.

    • 이벤트를 처리된 것으로 표시할 수 있는 범위 내에서 이벤트에 대한 응답으로 코드를 실행합니다. 이벤트를 이벤트 데이터에서 처리된 것으로 표시합니다. 이벤트는 여전히 트리의 다음 수신기로 라우팅되지만 대부분의 수신기는 추가 처리기를 호출하지 않습니다. 예외는 true(으)로 설정된 handledEventsToo(으)로 특별히 등록된 처리기가 있는 수신기입니다.

라우트된 이벤트를 처리에 대한 자세한 내용은 라우트된 이벤트를 처리됨 및 클래스 처리로 표시를 참조하세요.

발생시킨 개체에서 버블링 라우트된 이벤트만 처리하는 개발자는 다른 수신기에 대해 걱정하지 않을 수 있지만 어쨌든 이벤트를 처리된 것으로 표시하는 것이 좋습니다. 이렇게 하면 이벤트 경로를 따라 더 나아가는 요소에 동일한 라우트된 이벤트에 대한 처리기가 있는 경우 예기치 않은 부작용이 발생하지 않습니다.

클래스 처리기

라우트된 이벤트 처리기는 인스턴스 처리기 또는 클래스 처리기입니다. 지정된 클래스에 대한 클래스 처리기는 해당 클래스의 인스턴스에서 동일한 이벤트에 응답하는 인스턴스 처리기보다 먼저 호출됩니다. 이 동작 때문에, 라우트된 이벤트가 처리된 것으로 표시되는 경우 클래스 처리기 내에서도 처리된 것으로 표시되는 경우가 자주 있습니다. 클래스 처리기는 다음 두 가지 유형이 있습니다.

일부 WPF 컨트롤에는 특정 라우트된 이벤트에 대한 고유 클래스 처리 기능이 있습니다. 클래스 처리는 라우트된 이벤트가 발생하지 않는 외부 모양을 제공할 수 있지만 실제로는 클래스 처리기에 의해 처리된 것으로 표시됩니다. 처리된 이벤트에 응답하기 위해 이벤트 처리기가 필요한 경우 true(으)로 설정된 handledEventsToo을(를) 사용하여 처리기를 등록할 수 있습니다. 고유한 클래스 처리기를 구현하거나 원치 않는 클래스 처리를 해결하는 방법에 대한 자세한 내용은 라우트된 이벤트를 처리됨으로 표시 및 클래스 처리를 참조하세요.

WPF의 연결된 이벤트

XAML 언어는 연결된 이벤트라는 특수 이벤트 형식을 정의합니다. 연결된 이벤트는 비 요소 클래스에서 새 라우트된 이벤트를 정의하고 트리의 모든 요소에서 해당 이벤트를 발생시키는 데 사용할 수 있습니다. 이렇게 하려면 연결된 이벤트를 라우트된 이벤트로 등록하고 연결된 이벤트 기능을 지원하는 특정 지원 코드를 제공해야 합니다. 연결된 이벤트는 라우트된 이벤트로 등록되므로 요소에서 발생하는 경우 요소 트리를 통해 전파됩니다.

XAML 구문에서 연결된 이벤트는 이벤트 이름 소유자 형식으로 <owner type>.<event name> 지정됩니다. 이벤트 이름이 소유자 형식의 이름으로 정규화되기 때문에, 구문을 사용하여 인스턴스화할 수 있는 모든 요소에 이벤트를 연결할 수 있습니다. 이 구문은 이벤트 경로를 따라 임의 요소에 연결하는 일반 라우트된 이벤트의 처리기에도 적용됩니다. 처리기가 연결해야 하는 개체에 대해 AddHandler 메서드를 호출하여 코드 숨김의 연결된 이벤트에 대한 처리기를 연결할 수도 있습니다.

WPF 입력 시스템은 연결된 이벤트를 광범위하게 사용합니다. 그러나 거의 모든 연결된 이벤트는 기본 요소를 통해 연결되지 않은 동일한 라우트된 이벤트로 표시됩니다. 연결된 이벤트를 직접 사용하거나 처리하는 경우는 거의 없습니다. 인스턴스의 경우 XAML 또는 코드 숨김에서 연결된 이벤트 구문을 사용하는 것보다 동일한 UIElement.MouseDown 라우트된 이벤트를 통해 UIElement에서 기본 연결된 Mouse.MouseDown 이벤트를 처리하는 것이 더 쉽습니다.

WPF의 연결된 이벤트에 대한 자세한 내용은 연결된 이벤트 개요를 참조하세요.

XAML의 정규화된 이벤트 이름

<owner type>.<event name> 구문은 이벤트 이름을 소유자 형식의 이름으로 한정합니다. 이 구문을 사용하면 이벤트를 클래스의 멤버로 구현하는 요소뿐만 아니라 모든 요소에 이벤트를 연결할 수 있습니다. 이 구문은 이벤트 경로를 따라 임의의 요소에서 연결된 이벤트 또는 라우트된 이벤트에 대해 XAML에서 처리기를 연결할 때 적용됩니다. 자식 요소에서 발생한 라우트된 이벤트를 처리하기 위해 부모 요소에 처리기를 연결하려는 시나리오를 고려합니다. 부모 요소에 라우트된 이벤트가 멤버로 없는 경우 정규화된 이벤트 이름 구문을 사용해야 합니다. 다음은 그 예입니다.

<StackPanel Name="StackPanel1" Button.Click="Button_Click">
    <Button>Click me</Button>
</StackPanel>

예제에서 이벤트 처리기가 추가되는 부모 요소 수신기는 StackPanel입니다. 그러나 Click 라우트된 이벤트는 ButtonBase 클래스에서 구현되고 발생하며 상속을 통해 Button 클래스에서 사용할 수 있습니다. Button 클래스는 Click 이벤트를 "소유"하지만 라우트된 이벤트 시스템에서는 CLR 이벤트에 대한 처리기를 가질 수 있는 모든 UIElement 또는 ContentElement 인스턴스 수신기에 연결될 수 있는 라우트된 이벤트에 대한 처리기를 허용합니다. 일반적으로 이러한 정규화된 이벤트 특성 이름에 대한 기본 xmlns 네임스페이스는 기본 WPF xmlns 네임스페이스이지만 사용자 지정 라우트된 이벤트에 대한 접두사가 추가된 네임스페이스를 지정할 수도 있습니다. xmlns에 대한 자세한 내용은 WPF XAML을 위한 XAML 네임스페이스 및 네임스페이스 매핑을 참조하세요.

WPF 입력 이벤트

WPF 플랫폼 내에서 라우트된 이벤트가 자주 사용되는 한 가지 애플리케이션은 입력 이벤트와 관련됩니다. 규칙에 따라 터널링 경로를 따르는 WPF 라우트된 이벤트에는 "미리 보기" 접두사로 된 이름이 있습니다. 미리 보기 접두사는 쌍을 이루는 버블링 이벤트가 시작되기 전에 미리 보기 이벤트가 완료됨을 나타냅니다. 입력 이벤트는 종종 쌍으로 제공되며, 하나는 미리 보기 이벤트이고 다른 하나는 버블링 라우트된 이벤트입니다. 예를 들어 PreviewKeyDownKeyDown를 지정합니다. 이벤트 쌍은 PreviewKeyDownKeyDown의 형식인 KeyEventArgs에 대한 이벤트 데이터의 동일한 인스턴스를 공유합니다. 입력 이벤트에 버블링 버전만 포함되거나 직접 라우트된 버전만 포함되는 경우도 있습니다. API 설명서에서 라우트된 이벤트 항목은 라우트된 이벤트 쌍을 상호 참조하고 라우트된 각 이벤트에 대한 라우팅 전략을 명확히 합니다.

쌍으로 제공되는 WPF 입력 이벤트는 마우스 단추 누르기와 같은 입력 장치의 단일 사용자 작업이 미리 보기 및 버블링 라우트된 이벤트를 순차적으로 발생시킵니다. 먼저, 미리 보기 이벤트가 발생하고 경로가 완료됩니다. 미리 보기 이벤트가 완료되면 버블링 이벤트가 발생하고 경로가 완료됩니다. 버블링 이벤트를 발생시키는 구현 클래스의 RaiseEvent 메서드 호출은 버블링 이벤트에 대한 미리 보기 이벤트의 이벤트 데이터를 다시 사용합니다.

처리됨으로 표시된 미리 보기 입력 이벤트는 미리 보기 경로의 나머지 부분에 대해 정상적으로 등록된 이벤트 처리기를 호출하지 않으며, 쌍을 이루는 버블링 이벤트가 발생하지 않습니다. 이 처리 동작은 적중 테스트 기반 입력 이벤트 또는 포커스 기반 입력 이벤트를 컨트롤의 최상위 수준에서 보고하려는 복합 컨트롤 디자이너에 유용합니다. 컨트롤의 최상위 요소는 컨트롤 하위 구성 요소에서 미리 보기 이벤트를 클래스 처리하여 최상위 컨트롤 관련 이벤트로 "대체"할 수 있습니다.

입력 이벤트 처리가 적용되는 방식을 그리는 방법은 다음 입력 이벤트 예제를 살펴보겠습니다. 다음 트리 그림에서는 leaf element #2은(는) 쌍을 이루는 PreviewMouseDownMouseDown 이벤트의 소스입니다.

이벤트 라우팅 다이어그램.

리프 요소 #2에서 마우스를 누른 후 이벤트 처리 순서는 다음과 같습니다.

  1. 루트 요소의 PreviewMouseDown 터널링 이벤트.
  2. 중간 요소 #1의 PreviewMouseDown 터널링 이벤트.
  3. 원본 요소인 리프 요소 #2의 PreviewMouseDown 터널링 이벤트.
  4. 원본 요소인 리프 요소 #2의 MouseDown 버블링 이벤트.
  5. 중간 요소 #1의 MouseDown 버블링 이벤트.
  6. 루트 요소의 MouseDown 버블링 이벤트.

라우트된 이벤트 처리기 대리자는 이벤트를 발생시킨 개체 및 처리기가 호출된 개체에 대한 참조를 제공합니다. 이벤트가 처음 발생한 개체는 이벤트 데이터의 Source 속성을 통해 보고됩니다. 처리기가 호출된 개체는 발신자 매개 변수를 통해 보고됩니다. 주어진 라우트된 이벤트 인스턴스의 경우 이벤트가 요소 트리를 통과할 때 이벤트를 발생시킨 개체는 변경되지 않지만 sender은(는) 변경됩니다. 이전 다이어그램의 3단계와 4단계에서 Sourcesender은(는) 동일한 개체입니다.

입력 이벤트 처리기가 이벤트를 처리하는 데 필요한 애플리케이션별 논리를 완료하는 경우 입력 이벤트를 처리된 것으로 표시해야 합니다. 일반적으로 입력 이벤트가 Handled(으)로 표시되면 이벤트 경로를 따라 추가 처리기가 호출되지 않습니다. 그러나 true(으)로 설정된 handledEventsToo 매개 변수에 등록된 입력 이벤트 처리기는 이벤트가 처리된 것으로 표시된 경우에도 호출됩니다. 자세한 내용은 미리 보기 이벤트라우트된 이벤트를 처리된 것으로 표시 및 클래스 처리를 참조하세요.

미리 보기 및 버블링 이벤트 쌍의 개념으로, 공유 이벤트 데이터와 미리 보기 이벤트의 순차적 발생, 버블링 이벤트는 일부 WPF 입력 이벤트에만 적용되며 모든 라우트된 이벤트에는 적용되지 않습니다. 고급 시나리오를 해결하기 위해 사용자 고유의 입력 이벤트를 구현하는 경우 WPF 입력 이벤트 쌍 접근 방식을 따르는 것이 좋습니다.

입력 이벤트에 응답하는 고유한 복합 컨트롤을 구현하는 경우 미리 보기 이벤트를 사용하여 하위 구성 요소에서 발생한 입력 이벤트를 표시하지 않고 전체 컨트롤을 나타내는 최상위 이벤트로 바꾸는 것이 좋습니다. 자세한 내용은 라우트된 이벤트를 처리된 것으로 표시 및 클래스 처리를 참조하세요.

WPF 입력 시스템과 입력 및 이벤트가 일반적인 애플리케이션 시나리오에서 상호 작용하는 방식에 대한 자세한 내용은 입력 개요를 참조하세요.

EventSetter 및 EventTrigger

태그 스타일에서는 EventSetter을(를) 사용하여 미리 선언된 XAML 이벤트 처리 구문을 포함할 수 있습니다. XAML이 처리되면 참조된 처리기가 스타일 지정된 인스턴스에 추가됩니다. 라우트된 이벤트의 경우 EventSetter만 선언할 수 있습니다. 다음 예제에서는 참조된 ApplyButtonStyle 이벤트 처리기 메서드가 코드 숨김에서 구현됩니다.

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="{x:Type Button}">
            <EventSetter Event="Click" Handler="ApplyButtonStyle"/>
        </Style>
    </StackPanel.Resources>
    <Button>Click me</Button>
    <Button Click="Button_Click">Click me</Button>
</StackPanel>

Style 노드에 지정된 형식의 컨트롤과 관련된 다른 스타일 정보가 이미 포함되어 있고, EventSetter이(가) 해당 스타일의 일부가 되도록 하면 태그 수준에서도 코드 재사용이 촉진될 수 있습니다. 또한 EventSetter은(는) 메서드 이름을 일반적인 애플리케이션 및 페이지 태그에서 추상화합니다.

WPF의 라우트된 이벤트와 애니메이션 기능을 결합하는 또 다른 특수화된 구문은 EventTrigger입니다. EventSetter로 라우트된 이벤트의 경우 EventTrigger만을 선언할 수 있습니다. 일반적으로 EventTrigger은(는) 스타일의 일부로 선언되지만, EventTrigger은(는) 페이지 수준 요소에서 Triggers 컬렉션의 일부로 선언되거나 ControlTemplate에서 선언될 수 있습니다. EventTrigger를 사용하면 라우트된 이벤트가 해당 이벤트에 대한 EventTrigger를 선언하는 경로의 요소에 도달할 때마다 실행되는 Storyboard를 지정할 수 있습니다. 이벤트를 처리하고 이벤트가 기존 스토리보드를 시작하게 하는 방법에 비해 EventTrigger의 장점은 EventTrigger가 스토리보드를 더 세밀하게 제어할 수 있다는 것입니다. 자세한 내용은 Storyboard를 시작한 후 이벤트 트리거를 사용하여 제어를 참조하세요.

라우트된 이벤트에 대한 추가 정보

사용자 고유의 클래스에서 사용자 지정 라우트된 이벤트를 만들 때 이 문서의 개념과 지침을 시작점으로 사용할 수 있습니다. 특수 이벤트 데이터 클래스 및 대리자를 사용하여 사용자 지정 이벤트를 지원할 수도 있습니다. 라우트된 이벤트 소유자는 클래스일 수 있지만 라우트된 이벤트는 UIElement 또는 ContentElement 파생 클래스에서 발생하고 처리되어야 유용하게 사용할 수 있습니다. 사용자 지정 이벤트에 대한 자세한 내용은 사용자 지정 라우트된 이벤트 만들기를 참조하세요.

참고 항목