다음을 통해 공유


입력 개요

Windows Presentation Foundation (WPF) 하위 시스템은 마우스, 키보드, 터치 및 스타일러스를 비롯한 다양한 장치에서 입력을 가져오는 데 사용할 수 있는 강력한 API를 제공합니다. 이 항목에서는 WPF가 제공하는 서비스에 대해 설명하고 입력 시스템의 아키텍처를 살펴봅니다.

이 항목에는 다음 단원이 포함되어 있습니다.

  • 입력 API
  • 이벤트 라우팅
  • 입력 이벤트 처리
  • 텍스트 입력
  • 터치 및 조작
  • 포커스
  • 마우스 위치
  • 마우스 캡처
  • 명령
  • 입력 시스템 및 기본 요소
  • 새로운 기능
  • 관련 항목

입력 API

기본 입력 API는 기본 요소 클래스인 UIElement, ContentElement, FrameworkElementFrameworkContentElement에 노출됩니다. 기본 요소에 대한 자세한 내용은 기본 요소 개요를 참조하십시오. 이러한 클래스는 키 누르기, 마우스 단추, 마우스 휠, 마우스 이동, 포커스 관리, 마우스 캡처 등과 관련된 입력 이벤트를 위한 기능을 제공합니다. 입력 아키텍처는 모든 입력 이벤트를 서비스로 처리하는 대신 기본 요소에 입력 API를 배치함으로써 UI의 특정 개체가 입력 이벤트를 발생시키도록 하고, 둘 이상의 요소가 입력 이벤트를 처리할 수 있는 이벤트 라우팅 체계를 지원할 수 있습니다. 대부분의 입력 이벤트에는 연결된 이벤트 쌍이 있습니다 예를 들어 키 누름 이벤트에는 KeyDownPreviewKeyDown 이벤트가 연결되어 있습니다. 이벤트마다 이벤트가 대상 요소로 라우팅되는 방법이 다릅니다. 미리 보기 이벤트는 요소 트리의 루트 요소에서 대상 요소로 터널링됩니다. 버블링 이벤트는 대상 요소에서 루트 요소로 버블링됩니다. WPF의 이벤트 라우팅에 대한 자세한 내용은 본 개요 항목의 이후 단원 및 라우트된 이벤트 개요에서 자세히 설명합니다.

키보드 및 마우스 클래스

기본 요소 클래스의 입력 API 외에도 Keyboard 클래스와 Mouse 클래스는 키보드 및 마우스 입력을 처리하기 위해 추가 API를 제공합니다.

Keyboard 클래스의 입력 API의 예로는 현재 누른 ModifierKeys를 반환하는 Modifiers 속성과 지정된 키가 눌려 있는지 여부를 확인하는 IsKeyDown 메서드가 있습니다.

다음 예제에서는 GetKeyStates 메서드를 사용하여 Key가 눌려 있는지 여부를 확인합니다.

            ' Uses the Keyboard.GetKeyStates to determine if a key is down.
            ' A bitwise AND operation is used in the comparison. 
            ' e is an instance of KeyEventArgs.
            If (Keyboard.GetKeyStates(Key.Return) And KeyStates.Down) > 0 Then
                btnNone.Background = Brushes.Red
// Uses the Keyboard.GetKeyStates to determine if a key is down.
// A bitwise AND operation is used in the comparison. 
// e is an instance of KeyEventArgs.
if ((Keyboard.GetKeyStates(Key.Return) & KeyStates.Down) > 0)
{
    btnNone.Background = Brushes.Red;
}

Mouse 클래스의 입력 API의 예로는 마우스 가운데 단추의 상태를 가져오는 MiddleButton과 마우스 포인터가 현재 가리키고 있는 요소를 가져오는 DirectlyOver가 있습니다.

다음 예제에서는 마우스의 LeftButtonPressed 상태인지 여부를 확인합니다.

            If Mouse.LeftButton = MouseButtonState.Pressed Then
                UpdateSampleResults("Left Button Pressed")
            End If
if (Mouse.LeftButton == MouseButtonState.Pressed)
{
    UpdateSampleResults("Left Button Pressed");
}

MouseKeyboard 클래스에 대해서는 본 개요 항목에서 자세히 다룹니다.

스타일러스 입력

WPF에는 Stylus에 대한 지원이 통합되어 있습니다. Stylus는 Tablet PC에 널리 사용되는 펜 입력 방법입니다. WPF 응용 프로그램은 마우스 API를 사용하여 스타일러스를 마우스로 처리할 수 있지만 WPF는 키보드 및 마우스와 비슷한 모델을 사용하는 스타일러스 장치 추상화도 제공합니다. 스타일러스와 관련된 모든 APIs에는 "Stylus"라는 단어가 포함됩니다.

스타일러스는 마우스처럼 동작할 수 있으므로 마우스 입력만 지원하는 응용 프로그램도 약간의 스타일러스 지원을 자동으로 받을 수 있습니다. 이러한 방식으로 스타일러스를 사용하는 경우 응용 프로그램은 알맞은 스타일러스 이벤트를 처리한 다음 해당 마우스 이벤트를 처리할 수 있게 됩니다. 뿐만 아니라 스타일러스 장치 추상화를 통해 잉크 입력과 같은 높은 수준의 서비스도 사용할 수 있습니다. 잉크 입력에 대한 자세한 내용은 잉크 시작을 참조하십시오.

이벤트 라우팅

FrameworkElement는 다른 요소를 해당 콘텐츠 모델에 자식 요소로 포함하여 요소 트리를 형성할 수 있습니다. WPF에서 부모 요소는 이벤트를 처리하여 해당 자식 요소 또는 다른 하위 요소를 대상으로 하는 입력에 참가할 수 있습니다. 이는 특히 더 작은 컨트롤에서 컨트롤을 만드는 "컨트롤 컴퍼지션" 또는 "합성"이라고 하는 프로세스에 유용합니다. 요소 트리 및 요소 트리와 이벤트 경로가 서로 어떻게 관련되는지에 대한 자세한 내용은 WPF의 트리를 참조하십시오.

이벤트 라우팅은 이벤트를 여러 요소로 전달함으로써 경로 상의 특정 개체나 요소가 다른 요소에서 발생시킨 이벤트에 대해 이벤트 처리를 통해 의미 있는 응답을 제공할 수 있도록 하는 프로세스입니다. 라우트된 이벤트는 직접, 버블링, 터널링이라는 세 가지 라우팅 메커니즘 중 하나를 사용합니다. 직접 라우팅에서는 소스 요소만 이벤트에 대한 알림을 받으며 이벤트가 다른 요소로 라우팅되지 않습니다. 하지만 직접 라우트된 이벤트도 표준 CLR 이벤트와는 달리 라우트된 이벤트에만 있는 몇몇 추가 기능을 제공합니다. 버블링은 먼저 이벤트에 대해 이벤트를 발생시킨 요소에 알린 다음 부모 요소 등에 알리는 순서로 요소 트리의 위쪽으로 작동합니다. 터널링은 요소 트리의 루트에서 시작하여 아래로 이동한 다음 원래 소스 요소에서 끝납니다. 라우트된 이벤트에 대한 자세한 내용은 라우트된 이벤트 개요를 참조하십시오.

WPF 입력 이벤트는 일반적으로 터널링 이벤트와 버블링 이벤트의 쌍으로 구성되어 제공됩니다. 터널링 이벤트는 "Preview" 접두사가 있다는 점에서 버블링 이벤트와 다릅니다. 예를 들어 PreviewMouseMove는 마우스 이동 이벤트의 터널링 버전이고 MouseMove는 이 이벤트의 버블링 버전입니다. 이 이벤트 쌍은 일반적으로 요소 수준에서 구현되며 WPF 이벤트 시스템의 본질적인 기능은 아닙니다. 자세한 내용은 라우트된 이벤트 개요의 WPF 입력 이벤트 단원을 참조하십시오.

입력 이벤트 처리

요소에서 입력을 받으려면 특정 이벤트에 이벤트 처리기를 연결해야 합니다. XAML에서는 이 작업이 매우 간단합니다. 이벤트를 수신할 요소의 특성으로 이벤트 이름을 참조합니다. 그런 다음 대리자에 따라 정의하는 이벤트 처리기의 이름으로 특성 값을 설정하기만 하면 됩니다. 이벤트 처리기는 C#과 같은 코드로 작성해야 하며 코드 숨김 파일에 포함할 수 있습니다.

키보드 이벤트는 키보드 포커스가 요소에 있는 동안 발생하는 키 동작을 운영 체제에서 보고할 때 발생합니다. 마우스 및 스타일러스 이벤트는 각각 요소를 기준으로 포인터 위치의 변경을 보고하는 이벤트와 장치 단추의 상태 변경을 보고하는 이벤트의 두 범주로 나뉩니다.

키보드 입력 이벤트 예제

다음 예제에서는 왼쪽 화살표 키 누르기를 수신합니다. Button이 있는 StackPanel을 만듭니다. 왼쪽 화살표 키 누르기를 수신할 이벤트 처리기를 Button 인스턴스에 연결합니다.

예제의 첫 번째 섹션에서는 StackPanelButton을 만든 다음 KeyDown에 대한 이벤트 처리기를 연결합니다.

<StackPanel>
  <Button Background="AliceBlue"
          KeyDown="OnButtonKeyDown"
          Content="Button1"/>
</StackPanel>
            ' Create the UI elements.
            Dim keyboardStackPanel As New StackPanel()
            Dim keyboardButton1 As New Button()

            ' Set properties on Buttons.
            keyboardButton1.Background = Brushes.AliceBlue
            keyboardButton1.Content = "Button 1"

            ' Attach Buttons to StackPanel.
            keyboardStackPanel.Children.Add(keyboardButton1)

            ' Attach event handler.
            AddHandler keyboardButton1.KeyDown, AddressOf OnButtonKeyDown
// Create the UI elements.
StackPanel keyboardStackPanel = new StackPanel();
Button keyboardButton1 = new Button();

// Set properties on Buttons.
keyboardButton1.Background = Brushes.AliceBlue;
keyboardButton1.Content = "Button 1";

// Attach Buttons to StackPanel.
keyboardStackPanel.Children.Add(keyboardButton1);

// Attach event handler.
keyboardButton1.KeyDown += new KeyEventHandler(OnButtonKeyDown);

코드로 작성된 두 번째 섹션은 이벤트 처리기를 정의합니다. 왼쪽 화살표 키를 누르면 Button이 키보드 포커스를 가지고 처리기가 실행되어 ButtonBackground 색이 변경됩니다. 왼쪽 화살표 키가 아닌 다른 키를 누르면 ButtonBackground 색이 다시 시작 색으로 변경됩니다.

        Private Sub OnButtonKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
            Dim source As Button = TryCast(e.Source, Button)
            If source IsNot Nothing Then
                If e.Key = Key.Left Then
                    source.Background = Brushes.LemonChiffon
                Else
                    source.Background = Brushes.AliceBlue
                End If
            End If
        End Sub
private void OnButtonKeyDown(object sender, KeyEventArgs e)
{
    Button source = e.Source as Button;
    if (source != null)
    {
        if (e.Key == Key.Left)
        {
            source.Background = Brushes.LemonChiffon;
        }
        else
        {
            source.Background = Brushes.AliceBlue;
        }
    }
}

마우스 입력 이벤트 예제

다음 예제에서는 마우스 포인터가 Button으로 들어가면 ButtonBackground 색이 변경됩니다. 마우스가 Button을 벗어나면 Background 색이 복원됩니다.

이 예제의 첫 번째 섹션에서는 StackPanelButton 컨트롤을 만든 다음 MouseEnterMouseLeave 이벤트에 대한 이벤트 처리기를 Button에 연결합니다.

<StackPanel>
  <Button Background="AliceBlue"
          MouseEnter="OnMouseExampleMouseEnter"
          MouseLeave="OnMosueExampleMouseLeave">Button

  </Button>
</StackPanel>
            ' Create the UI elements.
            Dim mouseMoveStackPanel As New StackPanel()
            Dim mouseMoveButton As New Button()

            ' Set properties on Button.
            mouseMoveButton.Background = Brushes.AliceBlue
            mouseMoveButton.Content = "Button"

            ' Attach Buttons to StackPanel.
            mouseMoveStackPanel.Children.Add(mouseMoveButton)

            ' Attach event handler.
            AddHandler mouseMoveButton.MouseEnter, AddressOf OnMouseExampleMouseEnter
            AddHandler mouseMoveButton.MouseLeave, AddressOf OnMosueExampleMouseLeave
// Create the UI elements.
StackPanel mouseMoveStackPanel = new StackPanel();
Button mouseMoveButton = new Button();

// Set properties on Button.
mouseMoveButton.Background = Brushes.AliceBlue;
mouseMoveButton.Content = "Button";

// Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton);

// Attach event handler.
mouseMoveButton.MouseEnter += new MouseEventHandler(OnMouseExampleMouseEnter);
mouseMoveButton.MouseLeave += new MouseEventHandler(OnMosueExampleMouseLeave);

예제에서 코드로 작성된 두 번째 섹션은 이벤트 처리기를 정의합니다. 마우스가 Button으로 들어가면 ButtonBackground 색이 SlateGray로 변경됩니다. 마우스가 Button을 벗어나면 ButtonBackground 색이 다시 AliceBlue로 변경됩니다.

        Private Sub OnMouseExampleMouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs)
            ' Cast the source of the event to a Button.
            Dim source As Button = TryCast(e.Source, Button)

            ' If source is a Button.
            If source IsNot Nothing Then
                source.Background = Brushes.SlateGray
            End If
        End Sub
private void OnMouseExampleMouseEnter(object sender, MouseEventArgs e)
{
    // Cast the source of the event to a Button.
    Button source = e.Source as Button;

    // If source is a Button.
    if (source != null)
    {
        source.Background = Brushes.SlateGray;
    }
}
        Private Sub OnMosueExampleMouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs)
            ' Cast the source of the event to a Button.
            Dim source As Button = TryCast(e.Source, Button)

            ' If source is a Button.
            If source IsNot Nothing Then
                source.Background = Brushes.AliceBlue
            End If
        End Sub
private void OnMosueExampleMouseLeave(object sender, MouseEventArgs e)
{
    // Cast the source of the event to a Button.
    Button source = e.Source as Button;

    // If source is a Button.
    if (source != null)
    {
        source.Background = Brushes.AliceBlue;
    }
}

텍스트 입력

TextInput 이벤트를 사용하면 장치 독립적인 방법으로 텍스트 입력을 수신할 수 있습니다. 텍스트 입력에는 주로 키보드를 사용하지만 음성, 필기 및 기타 입력 장치를 통해서도 텍스트 입력을 생성할 수 있습니다.

키보드 입력을 사용하는 경우 WPF가 먼저 적절한 KeyDown/KeyUp 이벤트를 보냅니다. 해당 이벤트가 처리되지 않았으며 키가 방향 화살표 키 또는 기능 키와 같은 제어 키가 아니라 텍스트 키이면 TextInput 이벤트가 발생합니다. 여러 키 입력으로 텍스트 입력의 단일 문자가 생성될 수도 있고 단일 키 입력으로 여러 문자로 된 문자열이 생성될 수도 있으므로 KeyDown/KeyUpTextInput 이벤트 간에 단순한 일대일 매핑은 없습니다. 이는 특히 각 언어의 알파벳으로 수천 개의 가능한 문자를 생성하는 데 Input Method Editors (IMEs)를 사용하는 한국어, 중국어 및 일본어 등의 언어에서 더욱 그렇습니다.

WPF가 KeyUp/KeyDown 이벤트를 전송할 때 키 입력이 TextInput 이벤트의 일부가 될 수 있으면(예: Alt+S를 누른 경우) KeyKey.System으로 설정됩니다. 그러면 KeyDown 이벤트 처리기의 코드가 Key.System이 있는지 확인하고, 있는 경우 이후에 발생한 TextInput 이벤트에 대한 처리를 처리기에 위임합니다. 이 경우 TextCompositionEventArgs 인수의 다양한 속성을 사용하여 원래 키 입력을 확인할 수 있습니다. 마찬가지로 IME가 활성 상태이면 Key의 값이 Key.ImeProcessed이고 ImeProcessedKey는 원래 키 입력을 제공합니다.

다음 예제에서는 Click 이벤트에 대한 처리기와 KeyDown 이벤트에 대한 처리기를 정의합니다.

코드 또는 태그의 첫 번째 세그먼트에서는 사용자 인터페이스를 만듭니다.

<StackPanel KeyDown="OnTextInputKeyDown">
  <Button Click="OnTextInputButtonClick"
          Content="Open" />
  <TextBox> . . . </TextBox>
</StackPanel>
            ' Create the UI elements.
            Dim textInputStackPanel As New StackPanel()
            Dim textInputeButton As New Button()
            Dim textInputTextBox As New TextBox()
            textInputeButton.Content = "Open"

            ' Attach elements to StackPanel.
            textInputStackPanel.Children.Add(textInputeButton)
            textInputStackPanel.Children.Add(textInputTextBox)

            ' Attach event handlers.
            AddHandler textInputStackPanel.KeyDown, AddressOf OnTextInputKeyDown
            AddHandler textInputeButton.Click, AddressOf OnTextInputButtonClick
// Create the UI elements.
StackPanel textInputStackPanel = new StackPanel();
Button textInputeButton = new Button();
TextBox textInputTextBox = new TextBox();
textInputeButton.Content = "Open";

// Attach elements to StackPanel.
textInputStackPanel.Children.Add(textInputeButton);
textInputStackPanel.Children.Add(textInputTextBox);

// Attach event handlers.
textInputStackPanel.KeyDown += new KeyEventHandler(OnTextInputKeyDown);
textInputeButton.Click += new RoutedEventHandler(OnTextInputButtonClick);

코드의 두 번째 세그먼트에는 이벤트 처리기가 포함되어 있습니다.

        Private Sub OnTextInputKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
            If e.Key = Key.O AndAlso Keyboard.Modifiers = ModifierKeys.Control Then
                handle()
                e.Handled = True
            End If
        End Sub

        Private Sub OnTextInputButtonClick(ByVal sender As Object, ByVal e As RoutedEventArgs)
            handle()
            e.Handled = True
        End Sub

        Public Sub handle()
            MessageBox.Show("Pretend this opens a file")
        End Sub
private void OnTextInputKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.O && Keyboard.Modifiers == ModifierKeys.Control)
    {
        handle();
        e.Handled = true;
    }
}

private void OnTextInputButtonClick(object sender, RoutedEventArgs e)
{
    handle();
    e.Handled = true;
} 

public void handle()
{
    MessageBox.Show("Pretend this opens a file");
}

입력 이벤트는 이벤트 경로의 위쪽으로 버블링하므로 StackPanel은 어떤 요소에 키보드 포커스가 있는지에 관계없이 입력을 받습니다. 먼저 TextBox 컨트롤이 알림을 받습니다. 그런 다음 TextBox가 입력을 처리하지 않은 경우에만 OnTextInputKeyDown 처리기가 호출됩니다. KeyDown 이벤트 대신 PreviewKeyDown 이벤트가 사용된 경우에는 OnTextInputKeyDown 처리기가 먼저 호출됩니다.

이 예제에서 처리 논리는 Ctrl+O에 대해 한 번, 그리고 단추의 클릭 이벤트에 대해 한 번으로 총 두 번 작성되었습니다. 입력 이벤트를 직접 처리하는 대신 명령을 사용하면 이를 간편하게 처리할 수 있습니다. 명령에 대해서는 본 개요 항목과 명령 개요에서 설명합니다.

터치 및 조작

새로운 하드웨어 및 Windows 7 운영 체제의 API는 응용 프로그램에 여러 터치의 입력을 동시에 받을 수 있는 기능을 제공합니다. WPF에서는 응용 프로그램이 마우스 또는 키보드 등의 다른 입력에 반응하는 방식과 유사하게, 즉 터치가 일어날 때 이벤트를 발생시키는 방식으로 터치를 감지하고 이에 반응할 수 있게 합니다.

WPF에서는 터치가 발생할 경우 두 가지 유형의 이벤트, 즉 터치 이벤트 및 조작 이벤트를 노출합니다. 터치 이벤트는 터치 스크린에 접촉한 각 손가락 및 손가락의 이동에 대한 원시 데이터를 제공합니다. 조작 이벤트는 입력을 특정 동작으로 해석합니다. 이 단원에서는 두 가지 이벤트 유형 모두에 대해 설명합니다.

사전 요구 사항

터치에 반응하는 응용 프로그램을 개발하려면 다음 구성 요소가 필요합니다.

  • Microsoft Visual Studio 2010.

  • Windows 7

  • Windows Touch를 지원하는 터치 스크린 같은 장치

용어

터치에 대한 설명에서는 다음 용어가 사용됩니다.

  • 터치는 Windows 7에서 인식되는 사용자 입력의 유형 중 하나입니다. 일반적으로 터치는 터치 인식 화면에 손가락을 접촉하여 시작됩니다. 터치 패드 같은 장치의 경우 랩톱 컴퓨터에서 널리 사용되지만 해당 장치에서 손가락의 위치 및 움직임을 마우스 입력으로 변환하기만 할 경우 터치가 지원되지 않습니다.

  • 다중 터치는 둘 이상의 점에서 동시에 발생하는 터치입니다. 다중 터치는 Windows 7 및 WPF에서 지원됩니다. WPF 설명서에서 언급되는 터치라는 개념은 항상 다중 터치에 해당합니다.

  • 조작은 터치가 개체에 적용되는 물리적 동작으로 해석되는 경우에 발생합니다. WPF에서 조작 이벤트는 입력을 변환, 확장 또는 회전 조작으로 해석합니다.

  • touch device는 터치 스크린에서 손가락 하나와 같은 터치식 입력을 생성하는 장치를 나타냅니다.

터치에 반응하는 컨트롤

다음 컨트롤의 경우 스크롤되어 보이지 않는 콘텐츠가 있으면 손가락을 컨트롤 위에서 끌어 컨트롤을 스크롤할 수 있습니다.

ScrollViewer에서는 터치 이동이 수직, 수평, 수직/수평 방향으로 활성화되거나 어느 방향으로도 활성화되지 않게 지정할 수 있는 연결된 속성 ScrollViewer.PanningMode를 정의합니다. ScrollViewer.PanningDeceleration 속성은 사용자가 터치 스크린에서 손가락을 뗄 경우 스크롤 속도가 얼마나 빨리 느려지는지 지정합니다. 연결된 속성 ScrollViewer.PanningRatio는 조작 오프셋을 변환할 스크롤 오프셋의 비율을 지정합니다.

터치 이벤트

기본 클래스 UIElement, UIElement3DContentElement는 응용 프로그램에서 터치에 반응하도록 하기 위해 구독할 수 있는 이벤트를 정의합니다. 터치 이벤트는 응용 프로그램에서 터치를 개체 조작 이외의 다른 동작으로 해석하는 경우에 유용합니다. 예를 들어, 사용자가 하나 이상의 손가락으로 그릴 수 있게 하는 응용 프로그램은 터치 이벤트를 구독합니다.

이러한 세 가지 클래스는 모두 정의하는 클래스와 상관없이 유사하게 동작하는 다음 이벤트를 정의합니다.

키보드 및 마우스 이벤트와 마찬가지로 터치 이벤트는 라우트되는 이벤트입니다. Preview로 시작하는 이벤트는 터널링 이벤트이고 Touch로 시작하는 이벤트는 버블링 이벤트입니다. 라우트된 이벤트에 대한 자세한 내용은 라우트된 이벤트 개요를 참조하십시오. 이러한 이벤트를 처리할 경우 GetTouchPoint 또는 GetIntermediateTouchPoints 메서드를 호출하여 특정 요소에 상대적인 입력의 위치를 얻을 수 있습니다.

터치 이벤트 간의 상호 작용을 이해하려면 사용자가 손가락을 특정 요소에 놓고 해당 요소 내에서 이동한 다음 손가락을 떼는 시나리오를 고려하십시오. 다음 그림에서는 버블링 이벤트의 실행을 보여 줍니다. 복잡함을 피하기 위해 터널링 이벤트는 생략되어 있습니다.

터치 이벤트

터치 이벤트 순서

다음 목록에서는 앞의 그림에 있는 이벤트를 순서대로 설명합니다.

  1. 사용자가 요소에 손가락을 놓으면 TouchEnter 이벤트가 한 번 발생합니다.

  2. TouchDown 이벤트는 한 번 발생합니다.

  3. TouchMove 이벤트는 사용자가 요소 내에서 손가락을 움직일 때마다 여러 번 발생합니다.

  4. TouchUp 이벤트는 사용자가 요소에서 손가락을 뗄 때 한 번 발생합니다.

  5. TouchLeave 이벤트는 한 번 발생합니다.

손가락을 세 개 이상 사용하면 각 손가락마다 이벤트가 발생합니다.

조작 이벤트

응용 프로그램을 통해 사용자가 개체를 조작할 수 있는 경우를 위해 UIElement 클래스는 조작 이벤트를 정의합니다. 터치 위치만 보고하는 터치 이벤트와 달리 조작 이벤트는 입력의 해석 방식을 보고합니다. 조작에는 변환, 확장 및 회전이라는 세 가지 유형이 있습니다. 다음 목록에서는 세 가지 유형의 조작을 호출하는 방법에 대해 설명합니다.

  • 변환 조작을 호출하려면 개체 위에 손가락을 놓고 터치 스크린에서 손가락을 이동합니다. 이렇게 하면 일반적으로 해당 개체가 이동합니다.

  • 확장 조작을 호출하려면 두 손가락을 개체 위에 놓은 다음 손가락을 모으거나 벌립니다. 이렇게 하면 일반적으로 해당 개체의 크기가 조정됩니다.

  • 회전 조작을 호출하려면 개체 위에 두 손가락을 놓고 손가락을 서로 회전시킵니다. 이렇게 하면 일반적으로 해당 개체가 회전됩니다.

둘 이상의 조작 유형이 동시에 발생할 수 있습니다.

개체가 조작에 반응하도록 하면 해당 개체가 관성을 갖게 할 수 있습니다. 이렇게 하면 개체를 통해 실제 세계를 시뮬레이션할 수 있습니다. 예를 들어, 테이블에서 책을 충분한 만큼의 강도로 밀면 책에서 손가락을 뗀 이후에도 책이 계속 이동하게 됩니다. WPF에서는 사용자가 개체에서 손가락을 뗀 이후에 조작 이벤트를 발생시켜 이 동작을 시뮬레이션할 수 있게 합니다.

사용자가 개체를 이동 및 회전하고 개체 크기를 조정할 수 있게 하는 응용 프로그램을 만드는 방법에 대한 자세한 내용은 연습: 첫 응용 프로그램 만들기를 참조하십시오.

UIElement에서는 다음 조작 이벤트를 정의합니다.

기본적으로 UIElement에서는 이러한 조작 이벤트를 받지 않습니다. UIElement에서 조작 이벤트를 받으려면 UIElement.IsManipulationEnabled를 true로 설정합니다.

조작 이벤트 실행 경로

사용자가 개체를 던지는 시나리오를 고려해 보십시오. 사용자는 개체 위에 손가락을 놓고 터치 스크린에서 짧은 거리만큼 손가락을 이동한 다음 개체가 이동 중일 때 손가락을 뗍니다. 그 결과 개체는 손가락의 영향을 받아 이동하고 사용자가 손가락을 뗀 후에도 계속 이동합니다.

다음 그림에서는 조작 이벤트의 실행 경로 및 각 이벤트에 대한 중요한 정보를 보여 줍니다.

조작 이벤트

조작 이벤트 순서

다음 목록에서는 앞의 그림에 있는 이벤트를 순서대로 설명합니다.

  1. 사용자가 개체 위에 손가락을 놓으면 ManipulationStarting 이벤트가 발생합니다. 특히 이 이벤트는 사용자가 ManipulationContainer 속성을 설정할 수 있게 합니다. 후속 이벤트에서 조작 위치는 ManipulationContainer에 상대적입니다. ManipulationStarting 이외의 이벤트에서 이 속성은 읽기 전용이므로 이 속성을 설정할 수 있는 유일한 시점은 ManipulationStarting 이벤트입니다.

  2. 이어서 ManipulationStarted 이벤트가 발생합니다. 이 이벤트는 조작의 원점을 보고합니다.

  3. ManipulationDelta 이벤트는 사용자가 터치 스크린에서 손가락을 움직일 때마다 여러 번 발생합니다. ManipulationDeltaEventArgs 클래스의 DeltaManipulation 속성은 조작이 이동, 확장 또는 변환 중 어느 것으로 해석되는지 보고합니다. 바로 여기서 개체 조작 작업의 대부분이 수행됩니다.

  4. ManipulationInertiaStarting 이벤트는 사용자가 개체에서 손가락을 떼면 발생합니다. 이 이벤트를 사용하면 관성이 발생하는 동안 수행되는 조작의 감속을 지정할 수 있습니다. 따라서 사용자가 선택한 여러 가지 실제 공간 또는 특성이 개체를 통해 에뮬레이트될 수 있습니다. 예를 들어 응용 프로그램에 실제 세계의 항목을 나타내는 개체가 두 개 있고 한 개체가 다른 개체보다 무거운 경우를 가정해 봅니다. 이 경우 무거운 개체가 가벼운 개체보다 더 빠르게 감속되도록 할 수 있습니다.

  5. ManipulationDelta 이벤트는 관성이 생길 때마다 여러 번 발생합니다. 이 이벤트는 사용자가 터치 스크린에서 손가락을 이동하고 WPF에서 관성을 시뮬레이션하는 경우에 발생합니다. 즉, ManipulationDeltaManipulationInertiaStarting 이벤트 이전 및 이후에 발생합니다. ManipulationDeltaEventArgs.IsInertial 속성은 관성 도중 ManipulationDelta 이벤트가 발생하는지 여부를 보고하므로 이 속성을 확인하고 값에 따라 서로 다른 작업을 수행할 수 있습니다.

  6. ManipulationCompleted 이벤트는 조작 및 관성이 끝나면 발생합니다. 즉, 모든 ManipulationDelta 이벤트가 발생한 이후에 조작 완료를 알리기 위해 ManipulationCompleted 이벤트가 발생합니다.

UIElement에서는 ManipulationBoundaryFeedback 이벤트도 정의합니다. 이 이벤트는 ManipulationDelta 이벤트에서 ReportBoundaryFeedback 메서드가 호출되면 발생합니다. ManipulationBoundaryFeedback 이벤트를 통해 응용 프로그램 또는 구성 요소는 개체가 경계에 닿을 때 시각적 피드백을 제공할 수 있습니다. 예를 들어, Window 클래스에서는 ManipulationBoundaryFeedback 이벤트를 처리하여 창 모서리가 닿으면 창이 약간 이동하도록 합니다.

ManipulationBoundaryFeedback 이벤트를 제외한 모든 조작 이벤트에서 이벤트 인수에 대해 Cancel 메서드를 호출하면 조작을 취소할 수 있습니다. Cancel을 호출하면 터치에 대해 조작 이벤트가 더 이상 발생하지 않고 마우스 이벤트가 발생합니다. 다음 표에서는 조작이 취소되는 시점과 발생하는 마우스 이벤트 간의 관계에 대해 설명합니다.

Cancel이 호출되는 이벤트

이미 발생한 입력에 대해 발생하는 마우스 이벤트

ManipulationStartingManipulationStarted

마우스 누름 이벤트

ManipulationDelta

마우스 누름 및 마우스 이동 이벤트

ManipulationInertiaStartingManipulationCompleted

마우스 누름, 마우스 이동 및 마우스 놓기 이벤트

조작이 관성 도중에 있는 경우 Cancel을 호출하면 해당 메서드에서 false를 반환하고 입력은 마우스 이벤트를 발생시키지 않습니다.

터치 이벤트와 조작 이벤트의 관계

UIElement는 항상 터치 이벤트를 받을 수 있습니다. IsManipulationEnabled 속성이 true로 설정되어 있으면 UIElement는 터치 이벤트와 조작 이벤트를 둘 다 받을 수 있습니다. TouchDown 이벤트가 처리되지 않는 경우(즉, Handled 속성이 false인 경우) 조작 논리에서 요소에 터치를 캡처하고 조작 이벤트를 생성합니다. TouchDown 이벤트에서 Handled 속성이 true로 설정된 경우 조작 논리에서 조작 이벤트를 생성하지 않습니다. 다음 그림에서는 터치 이벤트와 조작 이벤트의 관계를 보여 줍니다.

터치 이벤트 및 조작 이벤트

터치 이벤트와 조작 이벤트의 관계

다음 목록에서는 앞의 그림에 나와 있는 터치 이벤트와 조작 이벤트의 관계에 대해 설명합니다.

포커스

WPF에는 포커스와 관련된 두 가지 주요 개념인 키보드 포커스와 논리적인 포커스가 있습니다.

키보드 포커스

키보드 포커스는 키보드 입력을 받고 있는 요소를 말합니다. 바탕 화면에서 하나의 요소만 키보드 포커스를 가질 수 있습니다. WPF에서 키보드 포커스를 갖는 요소에는 IsKeyboardFocused가 true로 설정됩니다. 정적 Keyboard 메서드 FocusedElement는 현재 키보드 포커스를 가진 요소를 반환합니다.

키보드 포커스는 탭을 사용하여 요소로 이동하거나 TextBox와 같은 특정 요소를 마우스로 클릭하여 얻을 수 있습니다. 또는 Keyboard 클래스에서 Focus 메서드를 사용하여 프로그래밍 방식으로 키보드 포커스를 설정할 수도 있습니다. Focus는 지정한 요소에 키보드 포커스를 설정하려고 시도합니다. Focus가 반환하는 요소는 현재 키보드 포커스를 가진 요소입니다.

요소가 키보드 포커스를 가지려면 Focusable 속성과 IsVisible 속성이 true로 설정되어 있어야 합니다. Panel과 같은 일부 클래스는 Focusable이 기본적으로 false로 설정되어 있기 때문에 이러한 요소가 포커스를 갖도록 하려면 이 속성을 true로 설정해야 할 수 있습니다.

다음 예제에서는 Focus를 사용하여 Button에 키보드 포커스를 설정합니다. 응용 프로그램에서 초기 포커스를 두기에 가장 좋은 위치는 Loaded 이벤트 처리기입니다.

        Private Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
            ' Sets keyboard focus on the first Button in the sample.
            Keyboard.Focus(firstButton)
        End Sub
private void OnLoaded(object sender, RoutedEventArgs e)
{
    // Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton);
}

키보드 포커스에 대한 자세한 내용은 포커스 개요를 참조하십시오.

논리적인 포커스

논리적인 포커스는 포커스 범위 내의 FocusManager.FocusedElement를 말합니다. 응용 프로그램에서 여러 요소가 논리적인 포커스를 가질 수 있지만 특정 포커스 범위 내에서 하나의 요소만 논리적인 포커스를 가질 수 있습니다.

포커스 범위는 범위 내에서 FocusedElement를 추적하는 컨테이너 요소입니다. 포커스가 포커스 범위를 벗어나면 포커스를 가진 요소는 키보드 포커스를 잃지만 논리적인 포커스는 그대로 유지합니다. 포커스가 포커스 범위로 돌아오면 포커스를 가진 요소가 키보드 포커스를 갖습니다. 이렇게 하면 여러 포커스 범위 사이에서 키보드 포커스가 변경되더라도 포커스 범위 내에서 포커스를 가진 요소는 포커스가 다시 돌아올 때 포커스를 가진 상태로 유지됩니다.

Extensible Application Markup Language (XAML)에서 FocusManager의 연결된 속성 IsFocusScope을 true로 설정하거나 코드에서 SetIsFocusScope 메서드를 사용하여 이 연결된 속성을 설정하면 요소를 포커스 범위로 만들 수 있습니다.

다음 예제에서는 연결된 속성 IsFocusScope를 설정하여 StackPanel을 포커스 범위로 만듭니다.

<StackPanel Name="focusScope1" 
            FocusManager.IsFocusScope="True"
            Height="200" Width="200">
  <Button Name="button1" Height="50" Width="50"/>
  <Button Name="button2" Height="50" Width="50"/>
</StackPanel>
            Dim focuseScope2 As New StackPanel()
            FocusManager.SetIsFocusScope(focuseScope2, True)
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);

WPF에서 기본적으로 포커스 범위인 클래스는 Window, Menu, ToolBarContextMenu입니다.

키보드 포커스를 가진 요소는 자신이 속한 포커스 범위에 대한 논리적인 포커스도 가지므로 Keyboard 클래스 또는 기본 클래스의 Focus 메서드를 사용하여 한 요소에 포커스를 설정하면 해당 요소에 키보드 포커스와 논리적인 포커스가 설정됩니다.

포커스 범위에서 포커스를 가진 요소를 확인하려면 GetFocusedElement를 사용합니다. 포커스 범위에서 포커스를 가진 요소를 변경하려면 SetFocusedElement를 사용합니다.

논리적인 포커스에 대한 자세한 내용은 포커스 개요를 참조하십시오.

마우스 위치

WPF 입력 API는 좌표 공간과 관련된 유용한 정보를 제공합니다. 예를 들어 좌표 (0,0)은 좌표의 왼쪽 위를 나타냅니다. 하지만 이 좌표가 트리에서 어떤 요소의 왼쪽 위를 나타내는지는 알 수 없습니다. 즉, 좌표가 나타내는 요소가 입력 대상인지, 이벤트 처리기를 연결한 요소인지 또는 다른 요소인지 알 수 없습니다. 이러한 혼동을 피하기 위해 WPF 입력 API에서 마우스로 좌표를 사용할 때는 참조 프레임을 지정해야 합니다. GetPosition 메서드는 지정된 요소를 기준으로 마우스 포인터의 좌표를 반환합니다.

마우스 캡처

마우스 장치에는 마우스 캡처라고 하는 모달 특성이 있습니다. 마우스 캡처는 끌어서 놓기 작업이 시작될 때 전환되는 입력 상태를 유지함으로써 마우스 포인터의 명목상 화면 위치와 관련된 다른 작업이 발생할 필요가 없도록 하기 위해 사용됩니다. 마우스를 끄는 동안 끌어서 놓기 작업을 중단하지 않고는 클릭할 수 없으므로 끌기 원점에서 마우스 캡처를 보유하는 동안에는 대부분의 마우스 이동 동작이 적절하지 않게 됩니다. 입력 시스템은 마우스 캡처 상태를 확인할 수 있는 APIs와 특정 요소에 마우스 캡처를 적용하거나 마우스 캡처 상태를 취소할 수 있는 APIs를 제공합니다. 끌어서 놓기 작업에 대한 자세한 내용은 끌어서 놓기 개요를 참조하십시오.

명령

명령을 사용하면 장치 입력에 비해 보다 의미적 수준에서 입력을 처리할 수 있습니다. 명령은 Cut, Copy, Paste 또는 Open과 같은 단순한 지시문입니다. 명령은 명령 논리를 중앙 집중화하는 데 유용합니다. 즉, 동일한 명령을 ToolBarMenu에서 또는 바로 가기 키를 통해 액세스할 수 있습니다. 또한 명령은 명령을 사용할 수 없을 때 컨트롤을 비활성화하는 메커니즘도 제공합니다.

RoutedCommandICommand에 대한 WPF 구현입니다. RoutedCommand를 실행하면 명령 대상에서 PreviewExecutedExecuted 이벤트가 발생하여 다른 입력과 마찬가지로 요소 트리를 터널링 및 버블링합니다. 명령 대상이 설정되어 있지 않으면 키보드 포커스를 가진 요소가 명령 대상이 됩니다. 명령을 수행하는 논리는 CommandBinding에 연결되어 있습니다. Executed 이벤트가 특정 명령에 대한 CommandBinding에 도달하면 CommandBinding에서 ExecutedRoutedEventHandler가 호출됩니다. 이 처리기는 명령의 작업을 수행합니다.

명령에 대한 자세한 내용은 명령 개요를 참조하십시오.

WPF는 ApplicationCommands, MediaCommands, ComponentCommands, NavigationCommandsEditingCommands로 구성된 일반 명령 라이브러리를 제공합니다. 또는 자체적으로 명령 라이브러리를 정의할 수도 있습니다.

다음 예제에서는 클릭할 경우 TextBox에서 Paste 명령을 호출하도록 MenuItem을 설정하는 방법을 보여 줍니다. 이 예제에서는 TextBox에 키보드 포커스가 있다고 가정합니다.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
            ' Creating the UI objects
            Dim mainStackPanel As New StackPanel()
            Dim pasteTextBox As New TextBox()
            Dim stackPanelMenu As New Menu()
            Dim pasteMenuItem As New MenuItem()

            ' Adding objects to the panel and the menu
            stackPanelMenu.Items.Add(pasteMenuItem)
            mainStackPanel.Children.Add(stackPanelMenu)
            mainStackPanel.Children.Add(pasteTextBox)

            ' Setting the command to the Paste command
            pasteMenuItem.Command = ApplicationCommands.Paste
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;

WPF의 명령에 대한 자세한 내용은 명령 개요를 참조하십시오.

입력 시스템 및 기본 요소

Mouse, KeyboardStylus 클래스가 정의하는 연결된 이벤트와 같은 입력 이벤트는 입력 시스템에 의해 발생하며 런타임에 시각적 트리에 대한 적중 테스트를 기반으로 개체 모델의 특정 위치에 삽입됩니다.

Mouse, KeyboardStylus가 연결된 이벤트로 정의하는 각 이벤트는 기본 요소 클래스인 UIElementContentElement가 새로운 라우트된 이벤트로 다시 노출합니다. 기본 요소의 라우트된 이벤트는 원래 연결된 이벤트를 처리하고 이벤트 데이터를 다시 사용하는 클래스를 통해 생성됩니다.

입력 이벤트가 해당 기본 요소 입력 이벤트 구현을 통해 특정 소스 요소와 연결되는 경우 이 입력 이벤트는 논리적 트리 개체 및 시각적 트리 개체의 조합에 기반을 둔 이벤트 경로의 나머지 부분을 이동할 수 있으며 응용 프로그램 코드에서 처리할 수 있습니다. 이러한 장치 관련 입력 이벤트의 경우 XAML과 코드 모두에서 보다 이해하기 쉬운 이벤트 처리기 구문을 사용할 수 있으므로 일반적으로 UIElementContentElement에서 라우트된 이벤트를 사용하여 처리하는 것이 더 편리합니다. 대신 프로세스를 시작한 연결된 이벤트를 처리할 수도 있지만 이 경우 몇 가지 문제에 직면할 수 있습니다. 예를 들어 기본 요소 클래스 처리를 통해 연결된 이벤트가 처리된 것으로 표시될 수 있습니다. 또한 연결된 이벤트에 대한 처리기를 연결하기 위해 실제 이벤트 구문 대신 접근자 메서드를 사용해야 합니다.

새로운 기능

이제 보다 다양한 방법으로 WPF에서 입력을 처리할 수 있게 되었습니다. 따라서 WPF에서 사용하는 라우트된 이벤트 메커니즘 및 다양한 형식의 입력 이벤트에 대해 보다 잘 이해하고 있어야 합니다.

WPF 프레임워크 요소 및 이벤트 라우팅에 대해 보다 자세히 설명하는 추가 리소스가 있습니다. 자세한 내용은 명령 개요, 포커스 개요기본 요소 개요WPF의 트리라우트된 이벤트 개요와 같은 개요를 참조하십시오.

참고 항목

개념

포커스 개요

명령 개요

라우트된 이벤트 개요

기본 요소 개요

기타 리소스

속성