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


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

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

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

Важные 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, B или C вниз направление ключа ставит фокус на D.
  • В элементе пользовательского интерфейса слева от элемента списка правый ключ направления помещает фокус на A.
  • В элементе пользовательского интерфейса справа от элемента списка левая стрелка помещает фокус на A.

Клавиатура

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

  • На вкладке "A" клавиша tab помещает фокус на B.
  • На вкладке B клавиша tab помещает фокус на C.
  • На вкладке C клавиша tab помещает фокус на следующий фокусируемый элемент пользовательского интерфейса в порядке табуляции.
  • Из C клавиша SHIFT+TAB помещает фокус на B.
  • В B клавиша SHIFT+TAB или стрелка влево помещает фокус на A.
  • С помощью клавиши SHIFT+TAB фокус будет сосредоточен на следующем фокусируемом элементе пользовательского интерфейса в порядке обратной вкладки.
  • С клавиши A, B или C стрелка вниз помещает фокус на D.
  • В элементе пользовательского интерфейса слева от элемента списка клавиша tab помещает фокус на A.
  • В элементе пользовательского интерфейса справа от элемента списка клавиша shift tab помещает фокус на C.

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

Сведения о том, как реализовать этот код, см. в разделе "Пример " этой статьи.

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

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

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

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

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

<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 ведет себя в обратном направлении от поведения вкладок.

Пример

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

<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;
    }
}