이벤트 전파 방식을 이해하기 위해日常生活中에서 흔히 겪는 상황을 살펴보겠습니다.
1. 实例:회사 조직 구조에서의 보고 체계
큰 기업의 조직 구조를 생각해 봅시다: 본사 → 부서 → 팀.
- 버블링 이벤트 (버블링 - WPF 기본 방식): 팀에서 문제가 발생했습니다. 먼저 팀장이 인지하고, 이를 부서장에게 보고하고, 최종적으로 본사 임원에게 전달됩니다. 신호가 안에서 밖으로 전파됩니다.
- 터널링 이벤트 (터널링 - WPF의 Preview 이벤트): 본사에서 새 규정이下发됩니다. 먼저 본사가 이를 배포하고, 부서장이 검토한 후, 마지막으로 팀에 전달됩니다. 신호가 밖에서 안으로 전파됩니다.
WPF에서의 처리:
WPF에서는 터널링 이벤트를 구분하기 위해 PreviewMouseDown과 같은 별도의 이벤트명을 사용합니다.
Avalonia에서의 처리: Avalonia는 "같은 동작에 대해 두 개의 이름(KeyDown과 PreviewKeyDown)을 만들 필요 없이, 하나의 MouseDown만 만들고 구독 시점에 어느 단계에서 처리할지 지정하면 된다"고 생각합니다.
2. 핵심 차이점 비교표
| 특성 | WPF 방식 | Avalonia 방식 |
|---|---|---|
| 터널링 이벤트 (외부 → 내부) | 전용 이름 사용: PreviewKeyDown |
통합 이름 + RoutingStrategies.Tunnel 사용 |
| 버블링 이벤트 (내부 → 외부) | 일반 이름 사용: KeyDown |
통합 이름 + RoutingStrategies.Bubble 사용 |
| 구독 방식 | 직접 += 연산자 사용 |
반드시 AddHandler(...) 메서드 사용 |
3. 실전 코드: 터널링 이벤트 가로채기
StackPanel 안에 Button이 있는 UI가 있다고 가정합시다. 버튼이 클릭되기 전에 StackPanel 수준에서 해당 동작을 가로채고 싶습니다.
WPF에서의 구현 (익숙한 방식):
// WPF는 Preview 접두사가 붙은 전용 이벤트명을 제공
myStackPanel.PreviewMouseDown += MyHandler;
Avalonia에서의 구현 (공식 문서 방식):
Avalonia에는 PreviewMouseDown이라는 멤버가 없습니다. 다음과 같이 작성해야 합니다:
using Avalonia.Interactivity; // 이 네임스페이스 참조 필수
// 첫 번째 인자:监听할 이벤트 (정적 읽기 전용 필드 PointerPressedEvent)
// 두 번째 인자: 처리할 함수
// 세 번째 인자: 라우팅 전략. RoutingStrategies.Tunnel은 "터널 단계"(외부→내부)에서 발생함을 의미
this.AddHandler(InputElement.PointerPressedEvent, OnPointerPressed, RoutingStrategies.Tunnel);
// 처리 함수
private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
// 이 코드는 자식 컨트롤(예: 버튼)이 클릭 신호를 받기 전에 실행됩니다
System.Diagnostics.Debug.WriteLine("터널 단계: 본사에서 클릭을 가로챘습니다!");
}
4. 왜 Avalonia는 AddHandler를 사용해야 할까?
+= 연산자가 훨씬 간결한데, Avalonia는 왜 이렇게 복잡하게 만들었을까요?
- 코드 중복 감소: Avalonia는 각 이벤트에 대해 두 개의 C# 래퍼(KeyDown 및 PreviewKeyDown)를 정의할 필요가 없어 프레임워크 크기가 줄어듭니다.
- 세밀한 제어:
AddHandler를 사용하면 "이미 처리됨(Handled)으로 표시된" 이벤트를 처리할지 명시적으로 지정할 수 있습니다. - **성능 최적화:**的这种显式 구독 방식은 이벤트 시스템의 메모리 관리가 더욱 효율적입니다.
5. 요약: 실제 적용 방법
WPF 코드를 migration하다가 PreviewXXXX 이벤트를 찾 못한다면:
- 일반 이벤트명을 찾으세요 (예:
KeyDown은InputElement.KeyDownEvent에 해당). +=연산자를 사용하지 마세요.this.AddHandler(..., RoutingStrategies.Tunnel)을 사용하세요.