Поделиться через


Доступность клавиатуры

Специальные возможности клавиатуры должны рассматриваться как основная модель взаимодействия, а не как второстепенный запасной вариант. Надежный интерфейс клавиатуры поддерживает пользователей с различными ограниченными возможностями и ограничениями (включая зрение, обучение, ловкость, мобильность и проблемы с языком и взаимодействием). Он также повышает производительность для пользователей, которые предпочитают взаимодействие с клавиатурой для скорости и точности.

Если доступ к клавиатуре не является неполным или несогласованным, пользователи могут потерять доступ к основным функциям приложения, даже если взаимодействие указателя будет полностью реализовано.

Навигация по клавиатуре между элементами пользовательского интерфейса

Для взаимодействия с элементом управления с помощью клавиатуры элементы управления должны быть фокусируемыми и доступными через обход фокуса. Чтобы получить фокус (без использования указателя), элемент управления должен быть доступен с помощью навигации по вкладкам. По умолчанию порядок вкладок элементов управления совпадает с порядком их добавления на поверхность проекта, объявленным в XAML, или программного добавления в контейнер.

Во многих пользовательских интерфейсах это поведение по умолчанию приемлемо и соответствует потоку чтения. Однако визуальный порядок и порядок клавиатуры могут отличаться в зависимости от макета контейнера и расположения дочерних элементов. Это расхождение должно быть намеренно и проверено.

Явным образом проверьте поведение вкладки. Сетка, таблица и аналогичные структуры являются общими источниками несоответствия между предполагаемым порядком чтения и фокусировкой. Проверьте пути взаимодействия только с помощью клавиатуры и сенсорного ввода, чтобы гарантировать, что прохождение остается эффективным и предсказуемым.

Чтобы выровнять обход с визуальным потоком, можно переструктурировать XAML или явно назначить TabIndex. В следующем примере используется Grid с порядком табуляции по столбцам.

<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, которое также отключает взаимодействие. Отключенные элементы управления автоматически удаляются из порядка вкладок.

Если элемент остается интерактивным через другие механизмы, но не должен быть достигнут с помощью TAB, задайте значение IsTabStopзначение false.

Большинство элементов управления с поддержкой фокуса включены в порядок вкладок по умолчанию. Распространенное исключение — это элементы управления, отображающие текст, такие как RichTextBlock, которые могут поддерживать фокус для операций выделения текста и буфера обмена, но обычно не являются точками остановки для клавиши Tab, поскольку они не являются элементами управления, которые можно вызывать командами. Эти элементы управления по-прежнему можно обнаружить с помощью вспомогательных технологий с помощью семантики автоматизации, например шаблона элемента управления "Текст".

Если вы используете обход по умолчанию или явный TabIndex, применяются следующие правила:

  • Если tabIndex не задан для элемента, значение по умолчанию — Int32.MaxValue, а порядок основан на порядке объявления или вставки.
  • Если TabIndex задан для элемента:
    • Элементы с TabIndex, равные 0, добавляются в соответствии с порядком объявления и вставки.
    • Элементы с tabIndex больше 0 добавляются в порядке возрастания TabIndex .
    • Элементы с tabIndex меньше 0 добавляются перед элементами с нулевыми значениями.

В следующем фрагменте кода показаны смешанные параметры 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

Навигация между областями приложений с помощью F6

Область приложения — это видная область задач в окне. В Microsoft Edge, например, области включают адресную строку, панель закладок, полосу вкладок и область содержимого. F6 обычно используется для перемещения фокуса между этими панелями с дочерними элементами, доступными с помощью стандартной навигации клавиатуры.

Хотя соответствующая модель навигации клавиатуры является базовой, модель навигации с помощью клавиатуры также обычно включает:

  • Прослушивание сигнала от клавиши F6 для перемещения между основными областями пользовательского интерфейса.
  • Предоставление сочетаний клавиш для высокочастотных команд.
  • Предоставление ключей доступа для важных элементов управления.

См. сочетания клавиш и клавиши доступа для получения рекомендаций по реализации.

Оптимизация для F6

F6 значительно снижает затраты на навигацию, позволяя пользователям перемещаться между основными регионами вместо последовательного перебора каждого дочернего элемента управления.

Например, F6 в Microsoft Edge циклирует между адресной строкой, панелью закладки, полосой вкладок и содержимым. Так как страница может содержать множество остановок на вкладках, это делает распространенные задачи навигации гораздо более эффективными.

Последовательность F6 может выровняться с ориентирами или заголовками, но не обязательно должна точно совпадать. Используйте F6 для широкого перемещения на уровне региона и ориентиров и заголовков для семантической структуры внутри и между регионами.

Внимание

В вашем приложении необходимо явно реализовать навигацию при помощи F6; она не предоставляется автоматически.

По возможности каждый целевой регион F6 должен иметь четкое понятное имя либо из семантики ориентира, либо путем установки AutomationProperties.Name в корневом каталоге региона.

Shift+F6 должен пройти один и тот же цикл в обратном порядке.

Навигация по клавиатуре в элементе пользовательского интерфейса

Составные элементы управления должны обеспечивать прогнозируемую внутреннюю навигацию между дочерними элементами. Распространенный шаблон заключается в том, чтобы сохранить композитный корень в порядке табуляции и внутренне управлять активными потомками, а не предоставлять каждого дочернего элемента в виде отдельной остановки табуляции.

Многие встроенные элементы управления уже реализуют это поведение. Например, обход со стрелками по умолчанию доступен для ListView, GridView, ListBox и FlipView.

Варианты клавиатуры для действий и событий указателя для определенных элементов управления

Любой пользовательский интерфейс, который можно активировать указателем, также должен вызываться с помощью клавиатуры. Для активации требуется, чтобы элемент имел фокус (только классы, производные от Control, поддерживают фокус и навигацию по вкладкам).

Для элементов управления, которые можно вызвать, реализуйте обработчики событий клавиатуры для панели пробела и клавиш ВВОД. Это обеспечивает базовую равнозначность функциональности клавиатуры с интеракциями с указателем.

Если элемент не поддерживает фокус по умолчанию, используйте тип элемента управления с фокусом или реализуйте пользовательский элемент управления с явным поведением фокуса. В этом случае задайте для IsTabStopзначение true и обеспечьте видимый индикатор фокуса.

Во многих случаях композиция проще и надежнее, чем поведение, предназначенное только для пользовательского указателя. Например, вместо обработки ввода указателя непосредственно на изображение поместите его внутри кнопки , чтобы наследовать активацию клавиатуры, обработку фокуса и поведение автоматизации.

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

<!--Do this instead.-->
<Button Click="Button_Click"
        AutomationProperties.Name="Open profile photo">
  <Image Source="Assets/profile-photo.png"/>
</Button>

Сочетания клавиш

Помимо навигации и активации, реализуйте сочетание клавиш (сочетание клавиш, которое обеспечивает эффективный доступ к функциям приложения) для важных или часто используемых команд с ускорителями клавиатуры и клавишами доступа.

Ниже приведены два распространенных типа ярлыков :

  • Ускорители: вызов команд напрямую, с соответствующим видимым элементом управления или без нее.
  • Ключи доступа: переместите фокус на определенные элементы управления в пользовательском интерфейсе.

Всегда делайте сочетания клавиш, доступные для пользователей вспомогательных технологий. Передавайте эту информацию с помощью подсказок, метаданных автоматизации, видимых элементов интерфейса и справочной документации.

Чтобы предоставить метаданные ярлыков вспомогательным технологиям, используйте AutomationProperties.AccessKey для мнемонических сочетаний клавиш и AutomationProperties.AcceleratorKey для сочетаний клавиш для команд. Поскольку программы экранного доступа могут представлять ярлыки документа в аналогичном виде, следует документировать их в нескольких каналах.

В следующем примере показано, как документировать сочетания клавиш для кнопок воспроизведения, паузы и остановки мультимедиа.

<Grid>

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

  <MediaPlayerElement x:Name="DemoPlayer"
    Width="500" Height="300" Margin="20"
    HorizontalAlignment="Center"
    AutoPlay="False"
    AreTransportControlsEnabled="True" />

  <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="Ctrl+P"
      AutomationProperties.AccessKey="Alt+P">
      <Button.KeyboardAccelerators>
        <KeyboardAccelerator Modifiers="Control" Key="P"/>
      </Button.KeyboardAccelerators>
      <TextBlock>Play</TextBlock>
    </Button>

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

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

Внимание

Настройка AutomationProperties.AcceleratorKey или AutomationProperties.AccessKey не реализует поведение клавиатуры. Эти свойства предоставляют метаданные для автоматизации пользовательского интерфейса, поэтому вспомогательные технологии могут объявлять ожидаемые сочетания клавиш.

Поведение клавиатуры по-прежнему должно быть реализовано в коде. Используйте декларативные определения KeyboardAccelerator , если это возможно, и используйте обработчики KeyDown или KeyUp , где требуется пользовательская логика маршрутизации. Кроме того, обратите внимание, что стилизация подчеркивания клавиш доступа не является автоматической. Если вы хотите отображать видимые подчеркивания, делайте это явно (например, с подчеркиванием).

Для краткости пример опускает строковые ресурсы, такие как "Ctrl+A". В производственной среде локализуйте текст ярлыков и проверяйте соответствие мнемоник локали, так как выбор ключей часто зависит от переведенных меток.

Дополнительные рекомендации см. в разделе "Сочетания клавиш " в руководстве по взаимодействию с пользователем Windows.

Реализация обработчика событий клавиатуры

Основной вход использует маршрутизируемые события. Маршрутизируемые события могут распространяться от дочерних объектов к родительскому контейнеру, что даёт возможность родительскому элементу обрабатывать сочетания клавиш для нескольких потомков. Эта модель событий удобна для определения сочетания клавиш для элемента управления, содержащего несколько дочерних элементов, ни один из которых не может быть фокусом или частью порядка вкладок.

Примеры кода, включающие проверки модификатора клавиш (например, CTRL), см. в разделе "Взаимодействие с клавиатурой".

Навигация по клавиатуре для пользовательских элементов управления

Для пользовательских элементов управления используйте клавиши со стрелками, когда дочерние элементы связаны пространственно. В сценариях структуры дерева, где развертывание и сворачивание и активация представляют собой отдельные взаимодействия, сопоставляйте стрелки влево и вправо с функцией развертывания и сворачивания. Для ориентированных элементов управления сопоставляйте клавиши направления с ориентацией визуального элемента управления.

Поведение пользовательских клавиш обычно реализуется путем переопределения OnKeyDown и OnKeyUp.

Пример визуального состояния для индикатора фокуса

Любой фокусируемый пользовательский элемент управления должен предоставлять четкий индикатор визуального фокуса. В общем шаблоне используется наложение прямоугольника, которое изначально скрыто с помощью свойства видимости и появляется при фокусировке.

Встроенные элементы управления XAML уже предоставляют индикаторы фокуса. Точный внешний вид может отличаться от параметров темы, включая режим высокой контрастности. При повторном изменении шаблона контролов сохраните эквивалентное поведение видимости фокуса.

Следующий пример адаптирован из шаблона button по умолчанию.

<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 на корневом элементе шаблона.

<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"/>
          </Storyboard>
        </VisualState>
        <VisualState x:Name="Unfocused" />
        <VisualState x:Name="PointerFocused" />
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <!--composition is here-->
  </Grid>
</ControlTemplate>

Только одно состояние в этой группе явно изменяет визуальный элемент фокуса. Другие состояния могут оставаться пустыми, так как переходы внутри той же VisualStateGroup отменяют предыдущие анимации состояния. События фокуса, такие как GotFocus, в сочетании с GoToState, обычно управляют этими переходами.

Доступность клавиатуры и устройства без физической клавиатуры

Некоторые устройства полагаются на панель обратимого ввода (SIP) вместо аппаратной клавиатуры. Средства чтения с экрана могут определить, что пользователь сканирует клавиши и сообщать о переходах пользователя по клавишам SIP. Многие концепции доступности клавиатуры продолжают действовать через жестовые эквиваленты.

Например, даже без физической клавиши TAB экранный диктор поддерживает жесты, которые сопоставляют функции клавиши TAB. Это означает, что последовательное порядок вкладок по-прежнему критически важен. Экранный диктор также предоставляет жестовые эквиваленты для навигации по сложным элементам управления (см. клавиатурные команды и сенсорные жесты экранного диктора).

Примеры

Значок коллекции WinUI 3 Приложение WinUI 3 Gallery содержит интерактивные примеры элементов управления и функций WinUI. Получите приложение из Microsoft Store или просмотрите исходный код GitHub.