본문 바로가기

TechLog

실버라이트의 저수준 터치 이벤트, Touch.FrameReported

실버라이트는 멀티터치를 처리할 수 있는 저수준 프로그래밍 인터페이스와 고수준 프로그래밍 인터페이스를 제공한다. 고수준 인터페이스는 UIElement 클래스의 이벤트인 ManipulationStarted, ManipulationDelta, ManipulationCompleted 이벤트를 사용한다. 이들 이벤트는 차례대로 발생하며, 여러 손가락의 터치 입력을 종합하여 움직임 정도를 알아낼 수 있다. 터치를 처리하는 저수준 인터페이스의 경우 Touch.FrameReported 이벤트를 통해서 터치 입력을 처리한다. 이 이벤트는 제스처를 지원하지 않는다.

실버라이트의 터치 입력을 처리하는 저수준 프로그래밍 인터페이스의 핵심 클래스는 TouchPoint이다. 이 클래스에는 다음과 같은 네 개의 읽기 전용 프로퍼티가 있다:

Action

TouchAction 열거형 타입이며, 이 타입에는 Down, Move, Up 멤버가 있다.

Position

Point 타입. 터치가 일어난 위치를 나타낸다.

Size

Size 타입. 터치 영역 및 터치 압력을 나타내는 값이다.

(하지만 WP7에서는 터치 영역이나 압력을 지원하지 않는다. 쓸모없는 값이다)

TouchDevice

TouchDevice 타입 객체. 터치에 대한 추가적인 정보를 제공한다.

 

TouchDevice 프로퍼티는 두 개의 읽기 전용 프로퍼티를 갖고 있다.

Id

어떤 손가락에서 터치 입력이 일어나는지를 구분하기 위한 int 타입의 숫자이다. 터치 이벤트를 통해 동일한 손가락에서 Down → Move → Up 이벤트가 일어나는 동안 해당 터치 이벤트에는 유일한 Id 값이 부여된다.

DirectlyOver

UIElement 타입 객체로, 화면에서 터치가 입력된 위치를 기준으로 가장 앞에 있는 요소를 나타낸다.

 

저수준 터치 프로그래밍 인터페이스를 사용하려면 다음 코드처럼 Touch.FrameReported 이벤트를 핸들링하면 된다 :

 

Touch.FrameReported += OnTouchFrameReported;

...

void OnTouchFrameReported(object sender, TouchFrameEventArgs e) 
{ 

}

 

Touch는 System.Windows.Input.Touch 클래스를 나타내며, FrameReported 이벤트 또한 정적 타입이므로 프로그램 코드 어디서든 이벤트를 핸들링하든 관계없다.

어쨌든, 위와 같이 이벤트 핸들러 메서드를 작성하면 프로그램에서 터치 이벤트가 발생할 때마다 OnTouchFrameReported 메서드가 호출된다. 이 메서드의 매개 변수인 TouchFrameEventArgs 객체는 터치 이벤트가 일어난 시간을 가리키는 int 타입의 TimeStamp 프로퍼티를 갖고 있다. 화면 상에서 터치가 입력된 위치를 얻으려면 이 TouchFrameEventArgs 객체의 GetTouchPoints, GetPrimaryTouchPoint 메서드를 사용해야 한다.

GetTouchPoints 메서드의 원형은 GetTouchPoints(ref UIElement)이며, 이 메서드는 UIElement 클래스를 상속받은 요소를 참조 형식의 메서드 인자로 전달한다. 그 결과로 이 메서드는 TouchPointCollection 객체를 반환하며, 이 컬렉션에 틀어있는 TouchPoint 객체는 사용자의 터치 입력이 메서드의 인자로 전달된 요소의 위치에 대해 상대적으로 어느 위치에 있는지를 나타내는 값을 담은 Position 프로퍼티를 갖고 있다. GetTouchPoints 메서드에 null 인자를 넘겨주면 이 메서드는 화면의 좌측 상단을 기준으로 터치 입력의 위치를 반환한다. 이 때문에 GetTouchPoints 메서드에서 반환되는 Position 프로퍼티의 값은 음수 값을 갖고 있을 수도 있다. 손가락이 메서드의 인자로 전달된 요소보다 왼쪽, 혹은 위쪽의 위치를 터치할 수도 있기 때문이다.

GetPrimaryTouchPoint 메서드의 원형은 GetPrimaryTouchPoint(ref UIElement)이며, GetTouchPoints 메서드와 동일한 역할을 한다. 하지만, 여러 개의 손가락으로 터치가 일어났을 경우 가장 먼저 터치한 손가락을 기준으로 한 개의 TouchPoint 객체를 반환한다. 원래 FrameReported 이벤트는 데스크톱 버전의 실버라이트를 기준으로 만들어진 것이다. 그렇다보니 실버라이트도, FrameReported 이벤트도 마우스의 입력을 처리하기 위한 목적으로 설계되어 있다. 실버라이트에서는 마우스 이벤트 승격(Mouse-event Promotion) 과정을 거쳐 터치 이벤트를 마우스 이벤트로 변환한다. 하지만 이러한 승격 과정에서, 터치 입력을 마우스 입력과 비슷하게 처리하기 위해 오직 하나의 터치 입력만 남기고 나머지 터치 입력은 무시하는 로직을 사용한다. 최종적으로 마우스 입력으로 인식되는 하나의 터치 입력은 (손가락이 닿아있지 않던 상태에서) 터치 디스플레이에 첫 번째로 터치된 손가락에 대한 터치 입력이 되며, 그 외의 손가락 터치는 무시해버린다. GetPrimaryTouchPoint 메서드는 이와 동일한 로직으로 터치된 위치를 반환하는 것이다.

이러한 마우스 이벤트 승격을 취소하기 위해 SuspendMousePromotionUntilTouchUp 메서드를 사용할 수 있다. 다음 코드를 살펴보자.

 

void OnTouchFrameReported(object sender, TouchFrameEventArgs args)
{
    TouchPoint primaryTouchPoint = args.GetPrimaryTouchPoint(null);
    if (primaryTouchPoint != null && primaryTouchPoint.Action == TouchAction.Down)
    {
        args.SuspendMousePromotionUntilTouchUp();
    }
    …
}

 

SuspendMousePromotionUntilTouchUp 메서드는 주의해서 사용해야 한다. 앞에서 설명한대로 이 메서드는 기본적으로 모든 마우스 이벤트 승격을 취소해버린다. 그렇게 되면, 마우스로 조작될 것을 가정하고 설계되어 있는 실버라이트 컨트롤(Button 요소 등이 그렇다)은 마치 비활성화된 것처럼 사용자의 터치 입력에 반응하지 않게 된다. 이러한 컨트롤은 터치 이벤트에는 반응하지 않고, 마우스 이벤트에만 반응하기 때문이다.

마우스 이벤트 승격이 특정 요소에 대해서만 일어나도록 하기 위해 DirectlyOver 프로퍼티를 사용할 수도 있다. 하지만, WP7에서는 위와 같이 터치 입력을 지원하지 않는 컨트롤의 문제를 제외한다면 마우스 이벤트를 사용할 일이 없다. 그러므로 (그러한 컨트롤을 사용하지 않는다면) 마우스 이벤트 승격을 전혀 사용하지 않는 것이 나을 수도 있다. 데스크톱에서 작성된 실버라이트 컨트롤을 WP7 실버라이트 프로그램에 사용해야 될 상황이 생긴다면 이러한 문제를 충분히 고려해야 한다.