본문 바로가기
프로그래밍&IT/C# (Winfrom, WPF)

[WPF] Routed Events

by 성장의 용 2025. 8. 3.
728x90
반응형

WPF - Routed Events

UI 요소들이 계층 구조(Visual Tree / Logical Tree)로 구성되어 있기 때문에, 이벤트가 부모 또는 자식 컨트롤까지 전달될 수 있는 기능이 필요하게 된다.
이러한 이벤트 전달 메커니즘을 Routed Event (라우트 이벤트) 라고 한다.

 

Routed Event의 종류 (이벤트 방향성)

1. Bubbling Event (버블링 이벤트)

 

  • 자식 → 부모 방향으로 전달됨.
  • 가장 일반적이며, 대부분의 컨트롤 이벤트가 이 타입.
  • 예: Button.Click, TextBox.TextChanged

 

2. Tunneling Event

 

  • 부모 → 자식 방향으로 전달됨.
  • 이름에 Preview가 붙음.
  • 예: PreviewMouseDown, PreviewKeyDown

 

3. Direct Event

 

  • 일반적인 C# 이벤트처럼 한 객체에서만 발생.
  • 예: Loaded, Initialized
public class MyControl : Control
{
    // 1. RoutedEvent 등록
    public static readonly RoutedEvent MyClickEvent =
        EventManager.RegisterRoutedEvent(
            "MyClick", 
            RoutingStrategy.Bubble, 
            typeof(RoutedEventHandler), 
            typeof(MyControl));

    // 2. .NET-style wrapper 정의
    public event RoutedEventHandler MyClick
    {
        add { AddHandler(MyClickEvent, value); }
        remove { RemoveHandler(MyClickEvent, value); }
    }

    // 3. 이벤트 발생 메서드
    protected void RaiseMyClickEvent()
    {
        RoutedEventArgs args = new RoutedEventArgs(MyClickEvent);
        RaiseEvent(args);
    }
}

 

Route Event 핸들링

1. xaml에서 핸들링

<Button Click="Button_Click" />

2. 코드에서 핸들러 등록

button.AddHandler(Button.ClickEvent, new RoutedEventHandler(Button_Click));

 

핸들러 안에서 라우팅 중단하고자 할때

private void Button_Click(object sender, RoutedEventArgs e)
{
    e.Handled = true; // 이벤트 라우팅을 여기서 중단!
}

 

  • AddHandler(..., handledEventsToo: true)로 e.Handled = true인 이벤트도 잡을 수 있음.

 

ex) 마우스 이벤트 (터널링 + 버블링)

실제 발생 순서

Grid_PreviewMouseDownButton_PreviewMouseDown →  Button_MouseDown → Grid_MouseDown

<Grid PreviewMouseDown="Grid_PreviewMouseDown"
      MouseDown="Grid_MouseDown">
    <Button Content="Click Me" 
            PreviewMouseDown="Button_PreviewMouseDown"
            MouseDown="Button_MouseDown"/>
</Grid>

 

이벤트 체계 정리

1. Object Lifetime Events

- 컨트롤이나 요소가 생성되고 로드되는 시점에 발생하는 이벤트들.

- Initialized는 XAML에서 객체 초기화 시에만 발생하고, Loaded는 실제 렌더링 준비 완료 시점에 발생해요

이벤트 이름 설명
Initialized 객체가 생성되고 XAML 파싱이 완료되었을 때 발생 (생성자 이후)
Loaded 요소가 논리 트리/비주얼 트리에 붙었을 때 발생
Unloaded 요소가 트리에서 분리될 때 발생
DataContextChanged 바인딩된 DataContext가 변경되었을 때 발생

 

2. Input Events

마우스, 키보드, 터치 등의 사용자 입력을 처리하는 이벤트

Preview 이벤트는 항상 터널링 방식으로 부모에서 자식 방향으로 먼저 호출된다

MouseDown / PreviewMouseDown Bubble / Tunnel 마우스 버튼을 누를 때
MouseUp / PreviewMouseUp Bubble / Tunnel 마우스 버튼을 뗄 때
MouseMove / PreviewMouseMove Bubble / Tunnel 마우스를 움직일 때
MouseEnter / MouseLeave Direct 요소에 마우스가 들어오거나 나갈 때
MouseWheel Bubble 마우스 휠 스크롤
MouseDoubleClick Bubble 더블 클릭 (ButtonBase 계열에서만 지원)

 

이벤트 이름 라우팅 설명
KeyDown / PreviewKeyDown Bubble / Tunnel 키 누름
KeyUp / PreviewKeyUp Bubble / Tunnel 키 뗌
TextInput Bubble 문자 입력이 완료될 때 (IME 포함)

3. Focus Events

이벤트 이름 설명
GotFocus / LostFocus 포커스가 얻어졌거나 잃었을 때
IsKeyboardFocusedChanged 포커스 상태가 변경될 때 (이벤트가 아닌 프로퍼티 변경 감지)

4. Layout Events

이벤트 이름 설명
SizeChanged 요소의 크기가 변경될 때
LayoutUpdated 모든 레이아웃 연산이 끝날 때 (자주 발생, 무겁게 쓰면 안됨)

5. Binding/Property Change Events

 

  • DependencyPropertyDescriptor.AddValueChanged(...) → Dependency Property 변경 감지용
  • INotifyPropertyChanged → 일반 클래스에서 바인딩된 속성의 값이 바뀌었을 때

 

 

추가 내용

 

  • MouseEnter, MouseLeave는 Direct Event라서 라우팅되지 않음.
  • 모든 Preview 이벤트는 중간에서 차단(e.Handled = true) 가능.
  • UI 구성할 때는 Loaded에서 초기화 작업을 하는 게 일반적.