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


Вложенные элементы интерфейса в элементах списка

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

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

Важные API: класс ListView, класс GridView.

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

В этой статье используются термины список, элемент списка и вложенный элемент пользовательского интерфейса согласно указанным определениям:

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

Снимок экрана: части пользовательского интерфейса с вложенными элементами.

Примечание. Элементы ListView и GridView наследуют от класса ListViewBase, поэтому они обладают идентичными функциями, но отображают данные по-разному. В этой статье вся информация о списках актуальна для обоих элементов управления (ListView и GridView).

Основные и дополнительные действия

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

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

Основное действие — это то действие, которое пользователь ожидает при нажатии элемента списка.

Дополнительные действия — это, как правило, ускорители, связанные с элементами списка. Эти ускорители могут использоваться для управления списком или действий над элементом списка.

Варианты дополнительных действий

При создании пользовательского интерфейса со списком сначала необходимо учесть все методы ввода, поддерживаемые Windows. Подробнее о различных типах ввода см. в разделе Азбука методов ввода.

Убедившись, что ваше приложение поддерживает все методы ввода, доступные в Windows, вам следует решить, достаточно ли важны дополнительные действия вашего приложения, чтобы предоставлять их в виде ускорителей в основном списке. Не забывайте, что чем больше действий предоставляется, тем сложнее становится пользовательский интерфейс. Действительно ли вам нужно предоставлять дополнительные действия в основном пользовательском интерфейсе списка или их можно разместить где-то еще?

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

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

Размещение дополнительных действий на странице сведений

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

Дополнительную информацию см. в статье Шаблон "Список и подробные сведения".

Размещение дополнительных действий в контекстном меню

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

Для предоставления дополнительных действия при вводе с помощью геймпада или пульта дистанционного управления мы рекомендуем использовать контекстное меню.

Подробнее см. в разделе Контекстные меню и всплывающие элементы.

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

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

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

Дополнительные сведения см. в статье Взаимодействие с помощью мыши.

Размещение элементов пользовательского интерфейса для основных и дополнительных действий

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

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

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

Рассмотрите все методы ввода

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

Использование вложенных элементов пользовательского интерфейса

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

Вложенные элементы пользовательского интерфейса, с которыми элементы списка выполняют действие

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

Снимок экрана: вложенные элементы пользовательского интерфейса, обозначенные буквами A, B, C и D.

Игровой планшет

При вводе с помощью геймпада предоставьте пользователю следующие возможности:

  • нажатие кнопки навигации "Вправо" при фокусе на элементе A перемещает фокус на элемент B;
  • нажатие кнопки навигации "Вправо" при фокусе на элементе B перемещает фокус на элемент C;
  • при фокусе на элементе C кнопка навигации "Вправо" не выполняет никаких действий или перемещает фокус на фокусируемый элемент пользовательского интерфейса справа от списка, если таковой имеется;
  • нажатие кнопки навигации "Влево" при фокусе на элементе C перемещает фокус на элемент B;
  • нажатие кнопки навигации "Влево" при фокусе на элементе B перемещает фокус на элемент A;
  • при фокусе на элементе A кнопка навигации "Вправо" не выполняет никаких действий или перемещает фокус на фокусируемый элемент пользовательского интерфейса справа от списка, если таковой имеется;
  • нажатие кнопки навигации "Вниз" при фокусе на элементах A, B или C перемещает фокус на элемент D;
  • нажатие кнопки навигации "Вправо" при фокусе на элементе пользовательского интерфейса слева от элемента списка перемещает фокус на элемент A;
  • нажатие кнопки навигации "Влево" при фокусе на элементе пользовательского интерфейса справа от элемента списка перемещает фокус на элемент A.

Клавиатура

При вводе с помощью клавиатуры пользователь получает следующие возможности:

  • нажатие клавиши TAB при фокусе на элементе A перемещает фокус на элемент B;
  • нажатие клавиши TAB при фокусе на элементе B перемещает фокус на элемент C;
  • нажатие клавиши TAB при фокусе на элементе C перемещает фокус на следующий фокусируемый элемент пользовательского интерфейса в последовательности табуляции;
  • нажатие клавиш SHIFT+C при фокусе на элементе C перемещает фокус на элемент B;
  • нажатие клавиш SHITF+TAB или клавиши со стрелкой влево при фокусе на элементе B перемещает фокус на элемент A;
  • нажатие клавиш SHIFT+TAB при фокусе на элементе A перемещает фокус на следующий фокусируемый элемент пользовательского интерфейса в обратной последовательности табуляции;
  • нажатие клавиши со стрелкой вниз при фокусе на элементах A, B или C перемещает фокус на элемент D;
  • нажатие клавиши TAB при фокусе на элементе пользовательского интерфейса слева от элемента списка перемещает фокус на элемент A;
  • нажатие клавиш SHIFT+TAB при фокусе на элементе пользовательского интерфейса справа от элемента списка перемещает фокус на элемент C.

Чтобы создать такой пользовательский интерфейс, установите свойству IsItemClickEnabled значение true в списке. SelectionMode может иметь любое значение.

Код для реализации этого интерфейса см. в разделе Пример данной статьи.

Вложенные элементы пользовательского интерфейса, с которыми элементы списка не выполняют действий

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

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

Снимок экрана: сложный пользовательский интерфейс с множеством вложенных элементов, с которыми может взаимодействовать пользователь.

Чтобы создать такой пользовательский интерфейс, установите следующие свойства в вашем списке:

<ListView SelectionMode="None" IsItemClickEnabled="False" >
    <ListView.ItemContainerStyle>
         <Style TargetType="ListViewItem">
             <Setter Property="IsFocusEngagementEnabled" Value="True"/>
         </Style>
    </ListView.ItemContainerStyle>
</ListView>

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

Игровой планшет

При вводе с помощью геймпада предоставьте пользователю следующие возможности:

  • нажатие кнопки навигации "Вниз" при фокусе на элементе списка перемещает фокус на следующий элемент списка;
  • при фокусе на элементе списка кнопки навигации "Вправо" и "Влево" не выполняют никаких действий или перемещают фокус на фокусируемый элемент пользовательского интерфейса справа от списка, если таковой имеется;
  • нажатие кнопки "A" при фокусе на элементе списка перемещает фокус на вложенный элемент пользовательского интерфейса в порядке сверху вниз и слева направо.
  • Для навигации по вложенному элементу пользовательского интерфейса используется модель фокуса XY. Фокус может перемещаться только в пределах вложенного элемента пользовательского интерфейса внутри текущего элемента списка, пока пользователь не нажмет кнопку "B", после чего фокус переместится на элемент списка.

Клавиатура

При вводе с помощью клавиатуры пользователь получает следующие возможности:

  • нажатие клавиши со стрелкой вниз при фокусе на элементе списка перемещает фокус на следующий элемент списка;
  • при фокусе на элементе списка клавиши со стрелками влево/вправо не выполняют действий;
  • нажатие клавиши TAB при фокусе на элементе списка перемещает фокус на следующую остановку перехода внутри вложенного элемента пользовательского интерфейса;
  • нажатие клавиши TAB при фокусе на одном из вложенных элементов пользовательского интерфейса переходит между такими элементами в последовательности табуляции; после перехода по всем вложенным элементам пользовательского интерфейса фокус перемещается на следующий элемент управления в последовательности табуляции после ListView;
  • клавиши SHIFT+TAB функционируют прямо противоположно клавише TAB.

Пример

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

<ListView SelectionMode="None" IsItemClickEnabled="True"
          ChoosingItemContainer="listview1_ChoosingItemContainer"/>
private void OnListViewItemKeyDown(object sender, KeyRoutedEventArgs e)
{
    // Code to handle going in/out of nested UI with gamepad and remote only.
    if (e.Handled == true)
    {
        return;
    }

    var focusedElementAsListViewItem = FocusManager.GetFocusedElement() as ListViewItem;
    if (focusedElementAsListViewItem != null)
    {
        // Focus is on the ListViewItem.
        // Go in with Right arrow.
        Control candidate = null;

        switch (e.OriginalKey)
        {
            case Windows.System.VirtualKey.GamepadDPadRight:
            case Windows.System.VirtualKey.GamepadLeftThumbstickRight:
                var rawPixelsPerViewPixel = DisplayInformation.GetForCurrentView().RawPixelsPerViewPixel;
                GeneralTransform generalTransform = focusedElementAsListViewItem.TransformToVisual(null);
                Point startPoint = generalTransform.TransformPoint(new Point(0, 0));
                Rect hintRect = new Rect(startPoint.X * rawPixelsPerViewPixel, startPoint.Y * rawPixelsPerViewPixel, 1, focusedElementAsListViewItem.ActualHeight * rawPixelsPerViewPixel);
                candidate = FocusManager.FindNextFocusableElement(FocusNavigationDirection.Right, hintRect) as Control;
                break;
        }

        if (candidate != null)
        {
            candidate.Focus(FocusState.Keyboard);
            e.Handled = true;
        }
    }
    else
    {
        // Focus is inside the ListViewItem.
        FocusNavigationDirection direction = FocusNavigationDirection.None;
        switch (e.OriginalKey)
        {
            case Windows.System.VirtualKey.GamepadDPadUp:
            case Windows.System.VirtualKey.GamepadLeftThumbstickUp:
                direction = FocusNavigationDirection.Up;
                break;
            case Windows.System.VirtualKey.GamepadDPadDown:
            case Windows.System.VirtualKey.GamepadLeftThumbstickDown:
                direction = FocusNavigationDirection.Down;
                break;
            case Windows.System.VirtualKey.GamepadDPadLeft:
            case Windows.System.VirtualKey.GamepadLeftThumbstickLeft:
                direction = FocusNavigationDirection.Left;
                break;
            case Windows.System.VirtualKey.GamepadDPadRight:
            case Windows.System.VirtualKey.GamepadLeftThumbstickRight:
                direction = FocusNavigationDirection.Right;
                break;
            default:
                break;
        }

        if (direction != FocusNavigationDirection.None)
        {
            Control candidate = FocusManager.FindNextFocusableElement(direction) as Control;
            if (candidate != null)
            {
                ListViewItem listViewItem = sender as ListViewItem;

                // If the next focusable candidate to the left is outside of ListViewItem,
                // put the focus on ListViewItem.
                if (direction == FocusNavigationDirection.Left &&
                    !listViewItem.IsAncestorOf(candidate))
                {
                    listViewItem.Focus(FocusState.Keyboard);
                }
                else
                {
                    candidate.Focus(FocusState.Keyboard);
                }
            }

            e.Handled = true;
        }
    }
}

private void listview1_ChoosingItemContainer(ListViewBase sender, ChoosingItemContainerEventArgs args)
{
    if (args.ItemContainer == null)
    {
        args.ItemContainer = new ListViewItem();
        args.ItemContainer.KeyDown += OnListViewItemKeyDown;
    }
}
// DependencyObjectExtensions.cs definition.
public static class DependencyObjectExtensions
{
    public static bool IsAncestorOf(this DependencyObject parent, DependencyObject child)
    {
        DependencyObject current = child;
        bool isAncestor = false;

        while (current != null && !isAncestor)
        {
            if (current == parent)
            {
                isAncestor = true;
            }

            current = VisualTreeHelper.GetParent(current);
        }

        return isAncestor;
    }
}