내가 거의 C# Winform(윈폼)으로만 먹고살다보니,
WPF의 기본 개념이 어렵다.
목차
- Winform 컨트롤 이벤트와 WPF의 Routed event간의 차이
- RoutedEventArgs에서 많이 사용되는 프로퍼티와 내용
Winform 컨트롤 이벤트와 WPF의 Routed event간의 차이
1. 이벤트 모델 비교
WinForm의 컨트롤 이벤트
- WinForm은 일반적인 .NET 이벤트 모델을 사용합니다.
- 각 컨트롤에 이벤트가 있으며, 이벤트가 발생하면 컨트롤 자체에서 직접 처리됩니다.
- 이벤트의 버블링이나 터널링은 지원하지 않으며, 이벤트는 한 번 발생하면 해당 컨트롤에만 영향을 미칩니다.
- 이벤트 핸들러는 이벤트가 발생한 컨트롤에 바로 연결됩니다.
WPF의 Routed Event
- WPF는 RoutedEvent 모델을 사용합니다.
- 이벤트가 한 컨트롤에서 발생하면 터널링(Tunneling)과 버블링(Bubbling)을 통해 부모 및 자식 컨트롤을 따라 이동할 수 있습니다.
- Tunneling: 이벤트가 루트부터 시작해서 자식 컨트롤로 전달 (Preview 접두어 사용).
- Bubbling: 이벤트가 자식 컨트롤에서 시작해 루트로 전달.
- Routed Event의 핸들링은 특정 컨트롤뿐만 아니라 부모 컨트롤에서도 처리할 수 있어, 더욱 유연한 이벤트 처리가 가능합니다.
- WPF의 이벤트는 RoutedEventArgs를 통해 전달되며, 이를 통해 이벤트 흐름을 제어하거나 취소할 수 있습니다.
2. 이벤트 흐름 차이 (Tunneling/Bubbling)
- WinForm: 이벤트는 특정 컨트롤에서 시작되고 해당 컨트롤에서만 종료됩니다. 이벤트가 부모나 자식 컨트롤로 전달되지 않습니다.
- WPF: 하나의 이벤트가 발생하면, 이 이벤트는 UI 트리를 통해 상위 및 하위 요소들로 이동할 수 있습니다.
3. WPF의 Routed Event 종류
- Direct: WinForm과 유사하게 이벤트가 발생한 컨트롤에서만 처리.
- Bubbling: 이벤트가 자식에서 부모로 전파.
- Tunneling: 이벤트가 부모에서 자식으로 전파 (Preview 접두어 사용).
4. 이벤트 전파의 차이
- WPF에서는 이벤트의 RoutedEventArgs의 Handled 속성을 true로 설정하면, 이벤트가 더 이상 부모로 전파되지 않습니다.
- WinForm에서는 이벤트가 발생하면 그 이벤트는 취소할 수 있어도 다른 컨트롤로 전파되는 것을 제어할 수 있는 기능은 없습니다.
이처럼 WinForm은 상대적으로 단순한 이벤트 모델을 사용하는 반면,
WPF는 복잡하지만 더 유연한 이벤트 라우팅 구조를 통해 다양한 UI 동작을 구현할 수 있는 특징이 있습니다.
<유연하다 못해 복잡하다.>
5. 소스코드로 알아보는 차이
Winform 예제
using System;
using System.Windows.Forms;
public class WinFormExample : Form
{
[STAThread]
public static void Main()
{
Application.Run(new WinFormExample());
}
private Button myButton;
public WinFormExample()
{
myButton = new Button();
myButton.Text = "Click Me";
myButton.Location = new System.Drawing.Point(30, 30);
myButton.Click += MyButton_Click;
Controls.Add(myButton);
}
private void MyButton_Click(object sender, EventArgs e)
{
MessageBox.Show("Button Clicked in WinForm!");
}
}
- MyButton_Click 이벤트 핸들러는 myButton 버튼 컨트롤의 Click 이벤트에서만 발생합니다.
- 이벤트가 발생한 후 다른 부모 컨트롤로 전파되지 않습니다.
WPF 예제
using System.Windows;
using System.Windows.Controls;
namespace WpfEventExample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// StackPanel을 생성하고 Button을 자식으로 추가
StackPanel stackPanel = new StackPanel();
// 부모 StackPanel에 이벤트 추가 (Bubbling)
stackPanel.AddHandler(Button.ClickEvent, new RoutedEventHandler(StackPanel_Click));
Button myButton = new Button();
myButton.Content = "Click Me";
myButton.Click += MyButton_Click;
stackPanel.Children.Add(myButton);
this.Content = stackPanel;
}
private void MyButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Button Clicked!");
}
private void StackPanel_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Event Bubbling to StackPanel!");
}
}
}
- StackPanel의 Button.ClickEvent 핸들러는 버블링 이벤트로, 자식 컨트롤(Button)의 이벤트가 발생했을 때 이를 감지하여 처리할 수 있습니다.
- Button에서 Click 이벤트가 발생해도 StackPanel_Click 메서드가 실행되며, 이처럼 부모 컨트롤에서도 자식의 이벤트를 처리할 수 있는 것이 WPF RoutedEvent의 특징입니다.
RoutedEventArgs에서 많이 사용되는 프로퍼티와 내용
WPF의 RoutedEvent 모델에서 이벤트 데이터가 포함된 클래스입니다.
RoutedEventArgs 및 이를 상속하는 파생 클래스들은 WPF에서 이벤트를 처리할 때 사용되는 다양한 정보를 포함하고 있습니다.
많이 사용되는 프로퍼티들은 이벤트의 흐름을 제어하거나 이벤트가 발생한 요소에 대한 정보, 추가적인 사용자 데이터 등을 제공합니다.
아래는 RoutedEventArgs와 그 파생 클래스에서 많이 사용되는 주요 프로퍼티와 설명입니다:
1. RoutedEventArgs의 주요 프로퍼티
Handled
- 타입: bool
- 설명: 이벤트가 이미 처리되었는지를 나타냅니다.
- 용도: 이벤트 핸들러가 이 값을 true로 설정하면 해당 이벤트는 더 이상 다른 부모나 자식 컨트롤로 전달되지 않습니다. 이는 RoutedEvent가 더 이상 버블링(Bubbling)이나 터널링(Tunneling)되지 않도록 하는 중요한 속성입니다.
- 예제:
private void Button_Click(object sender, RoutedEventArgs e) {// 이벤트가 현재 핸들러에서 처리되었음을 명시e.Handled = true; }
OriginalSource
- 타입: object
- 설명: 이벤트가 최초로 발생한 소스 요소(최하위 UI 요소)를 나타냅니다.
- 용도: 이벤트가 실제로 발생한 컨트롤이 무엇인지를 확인할 때 사용합니다. 예를 들어, 복잡한 UI 트리 구조에서 이벤트가 최상위 요소에 도달했을 때도 OriginalSource를 사용해 실제로 이벤트를 발생시킨 컨트롤을 확인할 수 있습니다.
- 예제:
private void StackPanel_Click(object sender, RoutedEventArgs e) {// 클릭된 실제 컨트롤 확인var sourceControl = e.OriginalSource as FrameworkElement;MessageBox.Show($"Original Source: {sourceControl.Name}"); }
Source
- 타입: object
- 설명: 현재 이벤트가 처리되고 있는 소스 요소를 나타냅니다. Source는 버블링이나 터널링 도중 바뀔 수 있습니다.
- 용도: 이벤트의 경로를 따라가면서 현재 이벤트가 처리되는 중인 요소를 추적할 때 사용합니다.
- 예제:
private void Button_Click(object sender, RoutedEventArgs e) {// 현재 이벤트가 처리되고 있는 컨트롤 출력var currentControl = e.Source as FrameworkElement;MessageBox.Show($"Current Source: {currentControl.Name}"); }
RoutedEvent
- 타입: RoutedEvent
- 설명: 현재 이벤트와 연관된 RoutedEvent의 정보를 나타냅니다.
- 용도: 동일한 핸들러에서 여러 이벤트를 처리할 때, 어떤 이벤트가 발생했는지를 식별할 수 있습니다.
- 예제:
private void AnyElement_RoutedEventHandler(object sender, RoutedEventArgs e) {if (e.RoutedEvent == Button.ClickEvent) {MessageBox.Show("Button Clicked Event Handled.");}}
Timestamp
- 타입: int
- 설명: 이벤트가 발생한 시점을 나타내는 타임스탬프 값입니다.
- 용도: 이벤트 발생 시점을 비교하여 특정 시나리오에서 이벤트가 발생한 순서를 추적할 때 유용합니다.
- 예제:
private void Button_Click(object sender, RoutedEventArgs e) {MessageBox.Show($"Event Timestamp: {e.Timestamp}"); }
2. 파생 클래스의 주요 프로퍼티
RoutedEventArgs를 상속하는 여러 파생 클래스가 있습니다.
이들 중 자주 사용되는 클래스와 그 주요 프로퍼티는 다음과 같습니다:
2.1 MouseEventArgs
- 적용 범위: 마우스 관련 이벤트 (MouseEnter, MouseMove, MouseLeave 등).
- 주요 프로퍼티:
- LeftButton: 마우스 왼쪽 버튼의 상태 (MouseButtonState).
- RightButton: 마우스 오른쪽 버튼의 상태 (MouseButtonState).
- MiddleButton: 마우스 가운데 버튼의 상태 (MouseButtonState).
- GetPosition(UIElement relativeTo): 이벤트 발생 당시 마우스 포인터의 위치를 상대 좌표계로 반환.
2.2 KeyEventArgs
- 적용 범위: 키보드 관련 이벤트 (KeyDown, KeyUp 등).
- 주요 프로퍼티:
- Key: 눌려진 키를 나타냅니다 (Key 열거형).
- IsDown: 특정 키가 눌린 상태인지 여부.
- IsUp: 특정 키가 올라간 상태인지 여부.
- IsRepeat: 키가 눌려진 상태로 반복 입력이 되었는지를 나타냅니다.
2.3 TextChangedEventArgs
- 적용 범위: 텍스트 변경 이벤트 (TextBox.TextChanged).
- 주요 프로퍼티:
- Changes: 텍스트 변경 내역을 나타냅니다.
- UndoAction: 변경이 발생한 동작이 취소 동작인지 여부를 나타냅니다.
2.4 DragEventArgs
- 적용 범위: 드래그 앤 드롭 이벤트 (DragEnter, DragLeave, Drop 등).
- 주요 프로퍼티:
- Data: 드래그 앤 드롭에 포함된 데이터(IDataObject).
- AllowedEffects: 허용된 드롭 효과(DragDropEffects).
- Effects: 실제 드롭된 효과(DragDropEffects).
3. RoutedEventArgs와 EventArgs의 차이
- EventArgs: 기본 이벤트 데이터 클래스, WinForms 및 일반 .NET 이벤트 처리에서 사용됩니다.
- RoutedEventArgs: WPF의 이벤트 라우팅 모델에서 사용되며, 이벤트가 자식 또는 부모 컨트롤로 전달되는 것을 지원합니다.
4. Handled 속성 사용 예시
WPF의 이벤트는 부모 요소에서 Handled 속성을 통해 제어될 수 있습니다.
예를 들어, 부모 StackPanel에서 자식 Button의 Click 이벤트가 더 이상 다른 요소에 전달되지 않도록 할 수 있습니다.
private void StackPanel_Click(object sender, RoutedEventArgs e) {
// 이 이벤트가 StackPanel에서 이미 처리되었으므로 더 이상 버블링되지 않도록 설정
e.Handled = true;
}
위의 설정으로 StackPanel에서 Handled를 true로 설정하면 해당 이벤트는 다른 상위 컨트롤로 전파되지 않습니다.
'프로그래밍&IT > C#.WPF' 카테고리의 다른 글
WPF의 클래스 계층구조 (0) | 2024.09.30 |
---|---|
[C# WPF] 첨부 프로퍼티 (Attached Property)란? (0) | 2024.09.29 |
의존 프로퍼티 (Dependency Property) (2) | 2024.09.28 |
[C# - WPF] x: 의 의미 (0) | 2024.09.13 |