키보드 접근성

좋은 키보드 액세스를 앱에서 제공하지 않는 경우, 시각 장애가 있거나 이동하기 어려운 사용자는 앱을 사용하는 데 어려움을 겪거나 전혀 사용하지 못할 수 있습니다.

UI 요소 간의 키보드 탐색

컨트롤로 키보드를 사용하려면 컨트롤에 포커스가 있어야 하며, (포인터를 사용하지 않고) 포커스를 받으려면 탭 탐색을 통해 UI 디자인에서 컨트롤에 액세스할 수 있어야 합니다. 기본적으로, 컨트롤의 탭 순서는 XAML에 나열되거나 프로그래밍 방식으로 컨테이너에 추가되는 순서와 동일합니다.

대부분의 경우, XAML에서 컨트롤을 정의하는 방법에 따른 기본 순서가 가장 좋습니다. 특히 화면 읽기 프로그램에서 그 순서에 따라 컨트롤을 읽기 때문입니다. 그러나 기본 순서가 반드시 시각적 순서와 일치하는 것은 아닙니다. 실제 표시 위치는 부모 레이아웃 컨테이너와 레이아웃에 영향을 주는 자식 요소에 설정할 수 있는 특정 속성에 따라 달라질 수 있습니다. 앱에 좋은 탭 순서가 있는지 확인하려면 이 동작을 직접 테스트합니다. 특히 레이아웃에 대한 그리드 메타포나 테이블 메타포가 있는 경우 사용자가 읽는 순서와 탭 순서가 다를 수 있습니다. 그 자체로 항상 문제가 되지는 않습니다. 그러나 터치 가능한 UI 및 키보드 액세스 가능 UI로 앱의 기능을 테스트하고 UI가 어느 쪽에 적합한지는 확인합니다.

XAML을 조정하여 탭 순서를 시각적 순서와 일치시킬 수 있습니다. 또는 기본 탭 순서를 재정의하기 위해 TabIndex 속성을, 다음 예제에서 보여주듯이 열 우선 탭 탐색을 사용하는 Grid 레이아웃을 사용할 수도 있습니다.

XAML

<!--Custom tab order.-->
<Grid>
  <Grid.RowDefinitions>...</Grid.RowDefinitions>
  <Grid.ColumnDefinitions>...</Grid.ColumnDefinitions>

  <TextBlock Grid.Column="1" HorizontalAlignment="Center">Groom</TextBlock>
  <TextBlock Grid.Column="2" HorizontalAlignment="Center">Bride</TextBlock>

  <TextBlock Grid.Row="1">First name</TextBlock>
  <TextBox x:Name="GroomFirstName" Grid.Row="1" Grid.Column="1" TabIndex="1"/>
  <TextBox x:Name="BrideFirstName" Grid.Row="1" Grid.Column="2" TabIndex="3"/>

  <TextBlock Grid.Row="2">Last name</TextBlock>
  <TextBox x:Name="GroomLastName" Grid.Row="2" Grid.Column="1" TabIndex="2"/>
  <TextBox x:Name="BrideLastName" Grid.Row="2" Grid.Column="2" TabIndex="4"/>
</Grid>

탭 순서에서 컨트롤을 제외할 수 있습니다. 예를 들어 IsEnabled 속성을 false로 설정하는 방식을 통해 컨트롤을 비대화형으로 만들어야만 이 작업을 수행할 수 있습니다. 비활성화된 컨트롤은 탭 순서에서 자동으로 제외됩니다. 그러나 경우에 따라 비활성화되어있지 않은 경우에도 탭 순서에서 컨트롤을 제외할 수 있습니다. 이런 경우 IsTabStop 속성을 false로 설정합니다.

포커스가 있을 수 있는 모든 요소는 일반적으로 기본적으로 탭 순서에 있습니다. 이에 대한 예외는 RichTextBlock 같은 특정 텍스트 표시 형식에 포커스가 있어 텍스트 선택을 위해 클립보드에서 액세스할 수 있다는 것입니다. 그러나 정적 텍스트 요소가 탭 순서에 있을 것으로 예상되지 않으므로 탭 순서에 있지 않습니다. 일반적으로 대화형은 아닙니다(호출할 수 없고 텍스트 입력이 필요하지는 않지만 텍스트에서 선택점을 찾고 조정하도록 지원하는 텍스트 컨트롤 패턴 을 지원합니다). 텍스트에 포커스를 설정하면 가능한 일부 작업을 사용할 수 있다는 의미를 갖지 않아야 합니다. 텍스트 요소를 보조 기술에서 여전히 검색 가능하고 화면 읽기 프로그램에서 소리내어 읽지만, 실제 탭 순서에서 해당 요소를 찾는 것 이외의 기술에 의존합니다.

기본 순서를 사용하든 TabIndex 값을 조정하든 관계없이 다음 규칙이 적용됩니다.

  • 요소에 TabIndex를 설정하지 않은 경우 사용되는 기본값은 Int32.MaxValue이고 탭 순서는 XAML 또는 자식 컬렉션의 선언 순서를 기반으로 합니다.
  • 요소에 TabIndex가 설정된 경우:
    • 0과 동일한 TabIndex 가 있는 UI 요소는 XAML 또는 자식 컬렉션의 선언 순서에 따라 탭 순서에 추가됩니다.
    • 0보다 큰 TabIndex 가 있는 UI 요소는 TabIndex 값에 따라 탭 순서에 추가됩니다.
    • 0보다 작은 TabIndex UI 요소는 탭 순서에 추가되고 모든 0값 앞에 나타납니다. 이는 잠재적으로 HTML tabindex 속성 처리와 다를 수 있습니다(이전 HTML 사양에서는 음의 tabindex 지원이 되지 않았음).

예를 들어 다음 조각은 다양한 TabIndex 설정이 있는 요소의 컬렉션을 보여 줍니다(B에는 Int32.MaxValue또는 2,147,483,647 값이 할당됨).

<StackPanel Background="#333">
  <StackPanel Background="#FF33FF">
    <Button>A</Button>
    <Button TabIndex="2147483647">B</Button>
    <Button>C</Button>
  </StackPanel>
  <StackPanel Background="#33FFFF">
    <Button TabIndex="1">D</Button>
    <Button TabIndex="1">E</Button>
    <Button TabIndex="0">F</Button>
  </StackPanel>
</StackPanel>

그 결과는 다음 탭 순서와 같습니다.

  1. F
  2. D
  3. E
  4. A
  5. B
  6. C

UI 요소 내의 키보드 탐색

복합 요소의 경우 포함된 요소 간에 적절한 내부 탐색을 보장하는 것이 중요합니다. 복합 요소는 현재 활성 자식 요소를 관리하여 모든 자식 요소에 포커스를 둘 수 있는 오버헤드를 줄일 수 있습니다. 이러한 복합 요소는 탭 순서에 포함되며 키보드 탐색 이벤트를 자체적으로 처리합니다. 대부분의 복합 컨트롤에는 이미 컨트롤의 이벤트 처리를 위해 내장된 내부 탐색 논리가 있습니다. 예를 들어, 항목의 화살표 키 통과를 활성화하려면 기본적으로 ListView, GridView, ListBoxFlipView 컨트롤에서 설정되어야 합니다.

특정 컨트롤 요소에 대한 포인터 작업 및 이벤트에 대한 키보드 대안

클릭할 수 있는 UI 요소도 키보드를 사용해 호출할 수 있는지 확인합니다. UI 요소와 함께 키보드를 사용하려면 요소에 포커스가 있어야 합니다. 오직 Control 에서 파생된 클래스만 포커스 및 탐색을 지원합니다.

호출할 수 있는 UI 요소의 경우, 스페이스바 및 Enter 키에 대한 키보드 이벤트 처리기를 구현합니다. 이렇게 하면 기본 키보드 접근성 지원이 완료되고 사용자가 키보드만 사용하여 기본 앱 시나리오를 수행할 수 있습니다. 즉, 사용자는 모든 대화형 UI 요소에 도달 가능하고 기본 기능을 활성화할 수 있습니다.

UI에서 사용하려는 요소에 포커스가 있을 수 없는 경우 사용자 지정 컨트롤을 직접 만들 수 있습니다. 이 IsTabStop 속성을 true 로 설정하여 포커스를 활성화하고, 포커스 표시를 사용하여 UI를 장식하는 시각적 상태를 만들어내 포커스된 상태를 시각적으로 표현해야 합니다. 그러나 컨트롤 컴포지션 사용이 더 용이한 경우가 종종 있어 탭 정지, 포커스 및 Microsoft UI 자동화 피어 및 패턴에 대한 지원을 콘텐츠를 작성하도록 선택한 컨트롤에서 처리합니다.

예를 들어, 이미지에서 포인터 누름 이벤트를 처리하는 대신, 버튼 에서 해당 요소를 래핑하여 포인터, 키보드 및 포커스 지원을 받을 수 있습니다.

XAML

<!--Don't do this.-->
<Image Source="sample.jpg" PointerPressed="Image_PointerPressed"/>

<!--Do this instead.-->
<Button Click="Button_Click"><Image Source="sample.jpg"/></Button>

바로 가기 키

앱에 대한 키보드 탐색 및 활성화를 구현하는 것 외에도 앱 기능에 대한 바로 가기를 구현하는 것이 좋습니다. 탭 탐색은 좋은 기본 수준의 키보드 지원을 제공하지만 복잡한 양식의 경우 바로 가기 키에 대한 지원도 추가할 수 있습니다. 이렇게 하면 키보드와 포인팅 디바이스를 모두 사용하는 사용자도 애플리케이션을 보다 효율적으로 사용할 수 있습니다.

사용자가 앱 기능에 효율적으로 액세스할 수 있도록 하여 생산성을 향상하는 키보드 조합이 바로 가기 입니다. 바로 가기는 두 종류로 나뉩니다.

  • 앱에서 UI 부분으로 가는 바로 가기는 액세스 키 입니다. 액세스 키는 Alt 키와 문자 키로 구성됩니다.
  • 앱 명령으로의 바로 가기는 가속기 키 입니다. 앱에는 명령에 정확히 해당하는 UI가 있을 수도 있고 없을 수도 있습니다. 가속기 키는 Ctrl 키와 문자 키로 구성됩니다.

화면 읽기 프로그램 및 기타 보조 기술을 사용하는 사용자가 앱의 바로 가기 키를 검색할 수 있는 쉬운 방법을 제공해야 합니다. 도구 설명, 액세스 가능한 이름, 액세스 가능한 설명 또는 다른 형태의 화상 통신을 사용하여 바로 가기 키를 통신합니다. 바로 가기 키는 앱의 도움말 콘텐츠에서만큼은 잘 문서화되어야 합니다.

화면 읽기 프로그램을 통해 액세스 키를 문서화하려면 AutomationProperties.AccessKey 결합속성을 바로 가기 키를 설명하는 문자열로 설정하면 됩니다. 화면 읽기 프로그램에서 두 속성을 통상 동일한 방식으로 처리하지만 비 니모닉 바로 가기 키를 문서화하기 위한 AutomationProperties.AcceleratorKey 결합속성도 있습니다. 도구 설명서, 자동화 속성 및 작성된 도움말 설명서를 사용하여 여러 가지 방법으로 바로 가기 키를 문서화해 보세요.

다음 예제에서는 미디어 재생, 일시 중지 및 중지 버튼용 바로 가기 키를 문서화하는 방법을 보여 줍니다.

XAML

<Grid KeyDown="Grid_KeyDown">

  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>

  <MediaElement x:Name="DemoMovie" Source="xbox.wmv"
    Width="500" Height="500" Margin="20" HorizontalAlignment="Center" />

  <StackPanel Grid.Row="1" Margin="10"
    Orientation="Horizontal" HorizontalAlignment="Center">

    <Button x:Name="PlayButton" Click="MediaButton_Click"
      ToolTipService.ToolTip="Shortcut key: Ctrl+P"
      AutomationProperties.AcceleratorKey="Control P">
      <TextBlock>Play</TextBlock>
    </Button>

    <Button x:Name="PauseButton" Click="MediaButton_Click"
      ToolTipService.ToolTip="Shortcut key: Ctrl+A"
      AutomationProperties.AcceleratorKey="Control A">
      <TextBlock>Pause</TextBlock>
    </Button>

    <Button x:Name="StopButton" Click="MediaButton_Click"
      ToolTipService.ToolTip="Shortcut key: Ctrl+S"
      AutomationProperties.AcceleratorKey="Control S">
      <TextBlock>Stop</TextBlock>
    </Button>
  </StackPanel>
</Grid>

Important

AutomationProperties.AcceleratorKey 또는 AutomationProperties.AccessKey 설정을 통해 키보드 기능을 활성화할 수 없습니다. 이러한 정보가 보조 기술을 통해 사용자에게 전달될 수 있도록, UI 자동화 프레임워크에 어떤 키를 사용해야 하는지 보고합니다. 키 처리를 위한 구현은 XAML이 아니라 코드에서 수행해야 합니다. 앱에서 바로 가기 키 동작을 실제로 구현하려는 경우에도 연관된 컨트롤에서 KeyDown 또는 KeyUp 이벤트용 처리기를 연결해야 합니다. 액세스 키에 대한 밑줄 텍스트 장식도 자동으로 제공되지 않습니다. UI에 밑줄이 그어진 텍스트를 표시하려면 니모닉의 특정 키에 대한 텍스트를 인라인 밑줄 서식으로 명시적으로 밑줄을 지정해야 합니다.

편의상 앞의 예제에서는 "Ctrl+A"와 같은 문자열에 대한 리소스 사용을 생략합니다. 그러나 지역화 도중에 바로 가기 키도 고려해야 합니다. 바로 가기 키로 사용할 키 선택은 일반적으로 요소에 표시되는 텍스트 레이블에 따라 달라지므로 바로 가기 키 지역화는 관련이 있습니다.

바로 가기 키 구현에 대한 자세한 지침은 Windows 사용자 경험 상호작용 지침에 나온 바로 가기 키 를 참조하세요.

키 이벤트 처리기 구현

키 이벤트와 같은 입력 이벤트는 라우트된 이벤트라는 이벤트 개념을 사용합니다. 공용 컨트롤 부모가 여러 자식 요소에 대한 이벤트를 처리할 수 있도록, 복합 컨트롤의 자식 요소를 통해 라우트된 이벤트가 버블업할 수 있습니다. 이 이벤트 모델은 디자인상 포커스가 없거나 탭 순서의 일부가 될 수 없는 여러 복합 부분이 포함된 컨트롤에 대한 바로 가기 키 작업을 정의하는 데 편리합니다.

Ctrl 키와 같은 한정자에 대한 검사를 포함하는 키 이벤트 처리기를 작성하는 방법을 보여 주는 예제 코드를 원하시면 키보드 상호작용을 참조하세요.

사용자 지정 컨트롤을 위한 키보드 탐색

자식 요소 끼리 서로 공간 관계가 있는 경우, 자식 요소 간 탐색을 위한 바로 가기 키로 화살표 키를 사용하는 것이 좋습니다. 트리 뷰 노드에 확장-축소 및 노드 활성화를 처리하기 위한 별도의 하위 요소가 있는 경우, 좌우 방향의 화살표 키를 사용하면 키보드 확장 축소 기능을 쓸 수 있습니다. 컨트롤 콘텐츠 내에서 방향 통과를 지원하는 지향 컨트롤이 있는 경우, 적절한 화살표 키를 사용합니다.

일반적으로 클래스 논리의 일부로 OnKeyDownOnKeyUp 메서드의 재정의를 포함하여 사용자 지정 컨트롤을 위한 사용자 지정 키 처리를 구현합니다.

포커스 표시기의 시각적 상태 예제

사용자가 포커스를 맞출 수 있는 사용자 지정 컨트롤에는 시각적 포커스 표시기가 있어야 한다고 이전에 언급했습니다. 일반적으로 포커스 표시기는 컨트롤의 일반 경계 사각형 주위에 직사각형 모양을 그리는 것만큼 간단합니다. 시각적 포커스의 사각형 은 컨트롤 템플릿에서 컨트롤의 나머지 컴포지션에 대한 피어 요소이지만 아직 컨트롤이 없기 때문에 처음에는 표시 유형 값의 Collapsed 로 설정됩니다. 그런 다음, 컨트롤이 포커스를 가져오면 포커스의 표시 유형Visible로 구체적으로 설정하는 시각적 상태가 호출됩니다. 포커스가 다른 곳으로 이동하면 다른 시각적 상태가 호출되고 표시 유형축소됩니다.

모든 기본 XAML 컨트롤은 포커스가 있을 때(포커스가 있을 수 있는 경우) 적절한 시각적 포커스 표시기를 표시합니다. 또한 사용자가 선택한 테마(특히 사용자가 고대비 모드를 사용하는 경우)에 따라 모양이 달라질 수 있습니다. UI에서 XAML 컨트롤을 사용하고 컨트롤 템플릿을 대체하지 않는 경우, 올바르게 동작하고 표시되는 컨트롤에 대한 시각적 포커스 표시기를 가져오기 위한 추가 작업을 수행할 필요가 없습니다. 그러나 컨트롤을 다시 프로그래밍하려거나 XAML 컨트롤이 시각적 포커스 표시기를 제공하는 방법에 대해 궁금한 경우, 이 섹션의 리메인더에서 XAML 및 컨트롤 논리에서 이 작업을 수행하는 방법을 설명합니다.

다음은 버튼에 대한 기본 XAML 템플릿에서 제공되는 예제 XAML입니다.

XAML

<ControlTemplate TargetType="Button">
...
    <Rectangle
      x:Name="FocusVisualWhite"
      IsHitTestVisible="False"
      Stroke="{ThemeResource FocusVisualWhiteStrokeThemeBrush}"
      StrokeEndLineCap="Square"
      StrokeDashArray="1,1"
      Opacity="0"
      StrokeDashOffset="1.5"/>
    <Rectangle
      x:Name="FocusVisualBlack"
      IsHitTestVisible="False"
      Stroke="{ThemeResource FocusVisualBlackStrokeThemeBrush}"
      StrokeEndLineCap="Square"
      StrokeDashArray="1,1"
      Opacity="0"
      StrokeDashOffset="0.5"/>
...
</ControlTemplate>

지금까지는 단지 컴포지션이었습니다. 포커스 표시기의 표시 유형을 제어하려면, 표시 유형 속성을 토글하는 시각적 상태를 정의합니다. 이 작업은 컴퍼지션을 정의하는 루트 요소에 적용되는 VisualStateManager 및 VisualStateManager.VisualStateGroups 연결된 속성을 사용하여 수행합니다.

XAML

<ControlTemplate TargetType="Button">
  <Grid>
    <VisualStateManager.VisualStateGroups>
       <!--other visual state groups here-->
       <VisualStateGroup x:Name="FocusStates">
         <VisualState x:Name="Focused">
           <Storyboard>
             <DoubleAnimation
               Storyboard.TargetName="FocusVisualWhite"
               Storyboard.TargetProperty="Opacity"
               To="1" Duration="0"/>
             <DoubleAnimation
               Storyboard.TargetName="FocusVisualBlack"
               Storyboard.TargetProperty="Opacity"
               To="1" Duration="0"/>
         </VisualState>
         <VisualState x:Name="Unfocused" />
         <VisualState x:Name="PointerFocused" />
       </VisualStateGroup>
     <VisualStateManager.VisualStateGroups>
<!--composition is here-->
   </Grid>
</ControlTemplate>

어떻게 명명된 상태 중 하나만이 표시 유형을 직접 조정하는 반면 나머지 상태들은 비어 있는 것처럼 보이게 되는지 확인하세요. 시각적 상태의 작동 방식은 동일한 VisualStateGroup의 다른 상태를 컨트롤이 사용하는 즉시, 이전 상태에서 적용된 모든 애니메이션이 즉시 취소되는 것입니다. 컴퍼지션의 기본 표시 유형축소되었으므로, 사각형이 나타나지 않습니다. 컨트롤 논리는 GotFocus 같은 포커스 이벤트를 수신 대기하고 GoToState를 사용하여 상태를 변경하여 제어합니다. 기본 컨트롤을 사용하거나 해당 동작이 이미 있는 컨트롤을 기반으로 사용자 지정하는 경우 이미 처리되어 있는 경우가 많습니다.

키보드 접근성 및 Windows Phone

Windows Phone 디바이스에는 일반적으로 전용 하드웨어 키보드가 없습니다. 그러나 SIP(소프트 입력 패널)에서 여러 키보드 접근성 시나리오를 지원할 수 있습니다. 화면 읽기 프로그램은 삭제 발표를 포함하여 텍스트 SIP에서 텍스트 입력을 읽을 수 있습니다. 화면 읽기 프로그램에서 사용자가 키를 스캔하고 있음을 감지하고 스캔한 키 이름을 소리 내어 읽기 때문에 사용자는 손가락이 어디에 있는지 찾을 수 있습니다. 또한 키보드 지향 접근성 개념 중 일부는 키보드를 전혀 사용하지 않는 관련 보조 기술 동작에 매핑할 수 있습니다. 예를 들어 SIP에 Tab 키가 포함되지 않더라도 내레이터는 Tab 키를 누르는 것과 동일한 터치 제스처를 지원하므로 UI의 컨트롤을 통해 유용한 탭 순서를 갖는 것은 여전히 중요한 접근성 원칙입니다. 복잡한 컨트롤 내의 부분을 탐색하는 데 사용되는 화살표 키도 내레이터 터치 제스처를 통해 지원됩니다. 텍스트 입력이 아닌 컨트롤에 포커스가 도달하면 내레이터는 해당 컨트롤의 동작을 호출하는 제스처를 지원합니다.

SIP에는 Control 또는 Alt 키가 포함되지 않으므로 일반적으로 바로 가기 키는 Windows Phone 앱과 관련이 없습니다.

예제

WinUI 3 갤러리 앱에는 대부분의 WinUI 3 컨트롤, 특징, 기능의 대화형 예제가 포함되어 있습니다. 앱을 Microsoft Store 에서 다운로드하거나 GitHub에서 소스 코드를 가져오세요.