publicclass MyButtonSimple: Button
{
// Create a custom routed event by first registering a RoutedEventID// This event uses the bubbling routing strategypublicstaticreadonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
"Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));
// Provide CLR accessors for the eventpublicevent RoutedEventHandler Tap
{
add { AddHandler(TapEvent, value); }
remove { RemoveHandler(TapEvent, value); }
}
// This method raises the Tap eventvoid RaiseTapEvent()
{
RoutedEventArgs newEventArgs = new RoutedEventArgs(MyButtonSimple.TapEvent);
RaiseEvent(newEventArgs);
}
// For demonstration purposes we raise the event when the MyButtonSimple is clickedprotectedoverridevoid OnClick()
{
RaiseTapEvent();
}
}
public static readonly ~ 부분에서 RegisterRoutedEvent 메서드를 통해 RoutedEvent를 등록한다. 이벤트의 이름은 Tap 이고 라우팅전략은 Bubble 이다. 이 후의 두 인수는 RoutedEventHandler, 이벤트를 소유하는 클래스의 타입이다.
다음 부분에서 이벤트를 생성해주고,
RaiseTapEvent()라는 함수를 만들어 원하는 시점에 이 함수를 호출하여 이벤트를 발생시키도록 한다.
이벤트를 만들었다면 해당 메서드에서 MyButtonSimple.TapEvent 부분만 수정하면 된다.
원하는 시점에 RaiseTapEvent() 함수를 호출하면 되는데, 위의 예제에서는 버튼이 클릭될 때 호출되도록 하였다.
간단히 버튼을 클릭했을 때 발생하는 Click 이벤트, 혹은 패널을 클릭했을 때 발생하는 MouseDown, MouseUp 등의 것들이 Routed Event 에 포함된다.
하지만 꼭 엘리먼트에서만 발생하는 것은 아니고 코드 상에서 임의로 발생시킬 수도 있기 때문에, MSDN의 정의를 참조하도록 하자.
. 기능 측면의 정의 : Routed Event 는 이벤트를 발생시킨 특정 개체 뿐 아니라 엘리먼트 트리의 여러 리스너(listener)의 핸들러(handler)를 호출할 수 있는 이벤트 형식이다.
. 구현 측면의 정의 : Routed Event 는 RoutedEvent 클래스 객체의 지원을 받으며, WPF의 이벤트 시스템에 의해 처리된다.
WPF에는 Routed Event 를 두가지로 분류할 수 있다. 이는 버블링(Bubbling) 과 터널링(Tunneling) 이다.
버블링 이벤트는 발생한 이벤트 소스의 이벤트 처리기가 호출되고, 이후 트리 루트에 도달할 때까지 부모 엘리먼트로 라우팅하며, 일반적으로 사용된다.
반면 터널링 이벤트는 엘리먼트의 트리 루트(root)의 이벤트 처리기를 호출하고 자식 엘리먼트로 라우팅하며 이벤트 소스 엘리먼트까지 전달된다.
일반적으로 Tunneling 이벤트의 경우 접두사로 Preview가 붙고, PreviewMouseDown, PreviewDragDown 등으로 쓰여져 있으면 이를 Tunneling 이벤트라고 이해하면 된다.
- 상기 그림은 MSDN에서 퍼옴
위의 그림에서 element#2에서 이벤트가 발생했다고 하면 버블링, 터널링 이벤트가 발생하는 순서는 다음과 같다.
1. PreviewMouseDown (tunnel) on root element.
2. PreviewMouseDown (tunnel) on intermediate element #1.
3. PreviewMouseDown (tunnel) on source element #2.
4. MouseDown (bubble) on source element #2.
5. MouseDown (bubble) on intermediate element #1.
6. MouseDown (bubble) on root element.
* 이 때 더이상 이벤트가 라우팅 되지 않도록 하는 방법이 있다.
모든 라우팅된 이벤트는 공통 이벤트 데이터의 기본 클래스인 RoutedEventArgs를 공유한다. 이 클래스에는 Handled라는 속성이 존재하는데, 이 속성은 기본으로 false로 되어 있다. 한 이벤트 처리기에서 이 속성, 즉 RoutedEventArgs.Handled 를 true라고 설정하면 이 이벤트는 처리된 것으로 되어 더이상 라우팅 되지 않는다.
즉 위의 그림의 구조에서 3번 이벤트에 대한 이벤트 핸들러에서 e.Handled = true; 라는 문장을 써주면 4, 5, 6번의 이벤트는 라우팅되지 않는다.
두 트랜스폼의 차이점은 트랜스폼이 이루어지는 시점인데, 각 트랜스폼은 다음의 시점에 이루어진다.
LayoutTransform
Measure
Arrange
RenderTransform
Render
따라서 LayoutTransform의 결과는 Measure, Arrange 의 연산 결과에 반영되지만 RenderTransform의 결과는 이에 반영되지 않고 렌더링 과정에만 반영된다. 따라서 퍼포먼스는 RenderTransform의 경우가 LayoutTransform의 결과보다 좋다.
차이를 확인하기 위해 간단한 프로젝트를 만들어서 테스트해보았다.
StackPanel 에 버튼을 3개씩 만들고, 두번째 버튼에 RotateTransform 을 각각 LayoutTransform, RenderTransform으로 적용한 결과이다.
결론은, 가능하다면 RenderTransform을 사용하는 것이 좋고(더 빠르기 때문), 사용이 불가능하다면 LayoutTransform을 사용하면 되겠다.
WPF 의 컨트롤들 중 ContentControl 이라는 것이 있다. Control 중에 'Content' 속성을 지닌 컨트롤을 의미한다. Button 이나 Label, Frame과 같은 컨트롤들이 'ContentControl' 에 속한다.
상황에 따라 이 ContentControl 을 커스텀화 해서 사용하고 싶은 경우가 있다. 가령 Panel 처럼 생겼지만 우측 상단에 접었다 폈다 할 수 있는 기능을 가지는 버튼을 일괄적으로 넣고 싶다거나, border, label 등으로 일괄적으로 변경을 하려고 하는 등의 경우이다.
물론 코드상에서 적용하고자 하는 부분에 모두 border, button, label 등을 추가할 수 있지만, 이를 템플릿화하여 ContentControl 로 만들어놓는다면 재사용성을 높일 수 있을 것이다.
내가 구현하고자 했던 컨트롤은 상단에 두 개의 버튼을 갖는 ContentControl 이다. 이 컨트롤 내부에 어떤 내용이 들어갈 지는 모르지만 공통된 기능을 위해 두 개의 버튼을 달았고, 이 컨트롤은 템플릿처럼 여러 부분에서 일괄적으로 사용하려고 한다.
컨트롤을 생성한 후 사용은 Button 이나 Label 에서와 동일하다. 즉,
<local:MyControl>
<StackPanel>
<Canvas />
<Button />
</StackPanel>
</local:MyControl>
다음과 같이 사용할 수 있다.
1. 먼저 '아이템 추가' 에서 WPF > Custom Control (WPF) 를 추가한다.
하면 다음과 같은 파일이 생기며,
프로젝트 부분을 잘 보면 'Generic.xaml' 파일이 생성(되거나 이미 있다면 해당 Custom ContentControl 부분이 추가) 되어 있는 것을 확인할 수 있다.
2. ContentControl 클래스를 상속받도록 한다.
- 기존에는 MyCustomClass : Control 과 같이 선언되어 있을 것이다.
- 이를 MyCustomClass : ContentControl 로 변경한다.
3. 'Generic.xaml' 코드에 알맞게 작성한다.
- 처음 생성했을 때 Generic.xaml 코드 상에는 'Content' 속성에 해당하는 부분이 들어갈 부분이 없다. 따라서 Content 를 넣을 부분에 'ContentControl' 컨트롤을 삽입하고,
- 그 외 부분을 원하는대로 적절하게 작성하면 된다.
- ContentControl 을 상속받았기 때문에 바인딩 시켜줘야할 변수들이 제법 많다 (Background, Alignment, Margin 등등). 하여 간단히 작성한 코드를 첨부한다.