Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Многие приложения содержат коллекции содержимого в виде списков, сетк и деревьев, с которыми пользователи могут управлять. Например, пользователи могут удалять, переименовать, помечать или обновлять элементы. В этой статье показано, как использовать контекстные команды для реализации таких действий таким образом, чтобы обеспечить оптимальный интерфейс для всех типов входных данных.
Важные API: интерфейс ICommand, свойство UIElement.ContextFlyout, интерфейс INotifyPropertyChanged
Создание команд для всех типов входных данных
Так как пользователи могут взаимодействовать с приложением Windows с помощью широкого спектра устройств и входных данных, ваше приложение должно предоставлять команды с помощью как контекстных меню, не зависящих от метода ввода, так и акселераторов, специфичных для метода ввода. В том числе и то, и другое позволяет пользователю быстро вызывать команды для содержимого независимо от типа ввода или устройства.
В этой таблице показаны некоторые типичные команды коллекции и способы их раскрытия.
| Command | независимый от входных данных | Акселератор мыши | Акселератор клавиатуры | Акселератор сенсорного ввода |
|---|---|---|---|---|
| Удаление элемента | Контекстное меню | Кнопка наведения указателя мыши | Клавиша DEL | Проводите пальцем, чтобы удалить |
| Элемент флага | Контекстное меню | Кнопка наведения указателя мыши | Ctrl+Shift+G | Проводите пальцем к флагу |
| Обновление данных | Контекстное меню | N/A | Клавиша F5 | Потяните, чтобы обновить |
| Добавить элемент в избранное | Контекстное меню | Кнопка наведения указателя мыши | F, Ctrl+S | Проводите пальцем в избранное |
Как правило, необходимо сделать все команды для элемента доступным в контекстном меню элемента. Контекстные меню доступны пользователям независимо от типа ввода и должны содержать все контекстные команды, которые могут выполнять пользователь.
Для часто доступных команд рекомендуется использовать акселераторы ввода. Ускорители ввода данных позволяют пользователю быстро выполнять действия, используя свое устройство ввода. К ускорителям входных данных относятся:
- Проводите пальцем для действия (сенсорный ускоритель)
- Потяните вниз для обновления данных (ускоритель сенсорного ввода)
- Сочетания клавиш (акселератор клавиатуры)
- Клавиши доступа (акселератор клавиатуры)
- Кнопки мыши и наведения пера (акселератор указателя)
Замечание
Пользователи должны иметь доступ ко всем командам любого типа устройства. Например, если команды вашего приложения доступны только через ускорители кнопок при наведении курсора, пользователи сенсорного ввода не смогут получить к ним доступ. Как минимум, используйте контекстное меню для предоставления доступа ко всем командам.
Пример: модель данных PodcastObject
Чтобы продемонстрировать наши главные рекомендации, в этой статье создается список подкастов для приложения подкастов. В примере кода показано, как позволить пользователю добавить в избранное определенный подкаст из списка.
Ниже приведено определение объекта podcast, с которым мы будем работать:
public class PodcastObject : INotifyPropertyChanged
{
// The title of the podcast
public String Title { get; set; }
// The podcast's description
public String Description { get; set; }
// Describes if the user has set this podcast as a favorite
public bool IsFavorite
{
get
{
return _isFavorite;
}
set
{
_isFavorite = value;
OnPropertyChanged("IsFavorite");
}
}
private bool _isFavorite = false;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
Обратите внимание, что PodcastObject реализует INotifyPropertyChanged для реагирования на изменения свойств, когда пользователь переключает свойство IsFavorite.
Определение команд с помощью интерфейса ICommand
Интерфейс ICommand помогает определить команду, доступную для нескольких типов входных данных. Например, вместо написания одного и того же кода для команды удаления в двух разных обработчиках событий один раз, когда пользователь нажимает клавишу Delete и один, когда пользователь щелкает правой кнопкой мыши "Удалить" в контекстном меню, можно реализовать логику удаления один раз, как ICommand, а затем сделать ее доступной для разных типов входных данных.
Нам нужно определить ICommand, представляющее действие "Избранное". Мы будем использовать метод Execute команды для добавления подкаста в избранное. Конкретный подкаст будет предоставлен методу execute с помощью параметра команды, который можно привязать с помощью свойства CommandParameter.
public class FavoriteCommand: ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
// Perform the logic to "favorite" an item.
(parameter as PodcastObject).IsFavorite = true;
}
}
Чтобы использовать одну и ту же команду с несколькими коллекциями и элементами, можно сохранить команду как ресурс на странице или в приложении.
<Application.Resources>
<local:FavoriteCommand x:Key="favoriteCommand" />
</Application.Resources>
Чтобы выполнить команду, вызовите метод Execute .
// Favorite the item using the defined command
var favoriteCommand = Application.Current.Resources["favoriteCommand"] as ICommand;
favoriteCommand.Execute(PodcastObject);
Создание UserControl для реагирования на различные входные данные
Если у вас есть список элементов, и каждый из этих элементов должен реагировать на несколько входных данных, можно упростить код, определив UserControl для элемента и используя его для определения контекстного меню и обработчиков событий элементов.
Чтобы создать UserControl в Visual Studio, выполните следующее:
- В обозревателе решений щелкните проект правой кнопкой мыши. Откроется контекстное меню.
- Выберите "Добавить > новый элемент" ...
Откроется диалоговое окно "Добавить новый элемент ". - Выберите UserControl из списка элементов. Присвойте ему нужное имя и нажмите кнопку "Добавить". Visual Studio создаст стаб UserControl для вас.
В нашем примере подкастов каждый подкаст будет отображаться в списке, который предоставит различные способы отметить подкаст как избранное. Пользователь сможет выполнить следующие действия, чтобы отметить подкаст как "Избранное":
- Вызов контекстного меню
- Выполнение сочетаний клавиш
- Отображение кнопки наведения указателя мыши
- Выполнение жеста прокрутки
Чтобы инкапсулировать эти поведения и использовать команду FavoriteCommand, давайте создадим новый UserControl с именем PodcastUserControl, чтобы представить подкаст в списке.
PodcastUserControl отображает поля PodcastObject в виде TextBlocks и реагирует на различные взаимодействия с пользователем. Мы будем ссылаться на PodcastUserControl и расширять его использование на протяжении всей этой статьи.
PodcastUserControl.xaml
<UserControl
x:Class="ContextCommanding.PodcastUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
IsTabStop="True" UseSystemFocusVisuals="True"
>
<Grid Margin="12,0,12,0">
<StackPanel>
<TextBlock Text="{x:Bind PodcastObject.Title, Mode=OneWay}" Style="{StaticResource TitleTextBlockStyle}" />
<TextBlock Text="{x:Bind PodcastObject.Description, Mode=OneWay}" Style="{StaticResource SubtitleTextBlockStyle}" />
<TextBlock Text="{x:Bind PodcastObject.IsFavorite, Mode=OneWay}" Style="{StaticResource SubtitleTextBlockStyle}"/>
</StackPanel>
</Grid>
</UserControl>
PodcastUserControl.xaml.cs
public sealed partial class PodcastUserControl : UserControl
{
public static readonly DependencyProperty PodcastObjectProperty =
DependencyProperty.Register(
"PodcastObject",
typeof(PodcastObject),
typeof(PodcastUserControl),
new PropertyMetadata(null));
public PodcastObject PodcastObject
{
get { return (PodcastObject)GetValue(PodcastObjectProperty); }
set { SetValue(PodcastObjectProperty, value); }
}
public PodcastUserControl()
{
this.InitializeComponent();
// TODO: We will add event handlers here.
}
}
Обратите внимание, что PodcastUserControl удерживает ссылку на PodcastObject в качестве DependencyProperty. Это позволяет нам привязать PodcastObjects к podcastUserControl.
После создания некоторых podcastObjects можно создать список подкастов, привязав PodcastObjects к ListView. Объекты PodcastUserControl описывают визуализацию PodcastObjects и поэтому задаются с помощью элемента ItemTemplate ListView.
MainPage.xaml
<ListView x:Name="ListOfPodcasts"
ItemsSource="{x:Bind podcasts}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:PodcastObject">
<local:PodcastUserControl PodcastObject="{x:Bind Mode=OneWay}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<!-- The PodcastUserControl will entirely fill the ListView item and handle tabbing within itself. -->
<Style TargetType="ListViewItem" BasedOn="{StaticResource ListViewItemRevealStyle}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Padding" Value="0"/>
<Setter Property="IsTabStop" Value="False"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
Создание контекстных меню
Контекстные меню отображают список команд или параметров, когда пользователь запрашивает их. Контекстные меню предоставляют контекстные команды, связанные с присоединенным элементом, и обычно зарезервированы для дополнительных действий, относящихся к этому элементу.
Пользователь может вызывать контекстные меню с помощью следующих "действий контекста":
| Ввод | Действие контекста |
|---|---|
| Mouse | Щелкните правой кнопкой мыши |
| Keyboard | Shift+F10, кнопка меню |
| Трогать | Длинное нажатие элемента |
| Перо | Нажатие кнопки "Barrel", длительное нажатие на элементе |
| Геймпад | Кнопка меню |
Так как пользователь может открыть контекстное меню независимо от типа ввода, контекстное меню должно содержать все контекстные команды, доступные для элемента списка.
Контекстное всплывающее меню
Свойство ContextFlyout, определенное классом UIElement, упрощает создание контекстного меню, которое работает со всеми типами входных данных. Вы предоставляете всплывающее окно, представляющее контекстное меню с помощью MenuFlyout или CommandBarFlyout; когда пользователь выполняет "контекстное действие", как определено выше, будет отображаться MenuFlyout или CommandBarFlyout, соответствующий соответствующему элементу.
См. меню и контекстные меню для определения меню и сценариев контекстного меню и рекомендаций о том, когда использовать всплывающее меню и всплывающий элемент командной строки.
В этом примере мы будем использовать MenuFlyout и начнем с добавления ContextFlyout в PodcastUserControl. MenuFlyout, указанный как ContextFlyout, содержит один элемент для добавления подкаста в избранное. Обратите внимание, что этот MenuFlyoutItem использует команду favoriteCommand, определенную выше, и параметр команды CommandParameter, привязанный к объекту PodcastObject.
PodcastUserControl.xaml
<UserControl>
<UserControl.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Favorite" Command="{StaticResource favoriteCommand}" CommandParameter="{x:Bind PodcastObject, Mode=OneWay}" />
</MenuFlyout>
</UserControl.ContextFlyout>
<Grid Margin="12,0,12,0">
<!-- ... -->
</Grid>
</UserControl>
Обратите внимание, что можно также использовать событие ContextRequested для реагирования на действия контекста. Событие ContextRequested не будет запускаться, если был указан ContextFlyout.
Создание акселераторов входных данных
Хотя каждый элемент в коллекции должен содержать контекстное меню, содержащее все контекстные команды, может потребоваться, чтобы пользователи могли быстро выполнять меньший набор часто выполняемых команд. Например, в почтовом приложении могут быть дополнительные команды, такие как "Ответ", "Архив", "Переместить в папку", "Задать флаг" и "Удалить", которые отображаются в контекстном меню, но наиболее распространенные команды : "Удалить" и "Флаг". После определения наиболее распространенных команд можно использовать акселераторы на основе входных данных, чтобы упростить выполнение этих команд для пользователя.
В приложении podcast часто выполняется команда "Избранное".
Клавиатурные ускорители
Сочетания клавиш и прямое управление клавишами
В зависимости от типа содержимого можно определить определенные сочетания ключей, которые должны выполнять действие. Например, в приложении электронной почты ключ DEL может использоваться для удаления выбранного сообщения электронной почты. В приложении для подкастов клавиши Ctrl+S или F могут добавлять подкаст в избранное для последующего прослушивания. Хотя некоторые команды имеют распространенные сочетания клавиш, такие как DEL для удаления, другие команды имеют сочетания клавиш, относящиеся к приложению или домену. Если это возможно, используйте известные сочетания клавиш или рассмотрите возможность предоставления текста напоминания в подсказке , чтобы научить пользователя об этой команде.
Приложение может реагировать, когда пользователь нажимает клавишу с помощью события KeyDown . Как правило, пользователи ожидают, что приложение будет реагировать, когда они сначала нажимают клавишу вниз, а не ждать, пока они не отпустят ключ.
В этом примере показано, как добавить обработчик KeyDown в PodcastUserControl, чтобы добавить подкаст в избранное, когда пользователь нажимает клавиши Ctrl+S или F. Этот процесс использует ту же команду, что и раньше.
PodcastUserControl.xaml.cs
// Respond to the F and Ctrl+S keys to favorite the focused item.
protected override void OnKeyDown(KeyRoutedEventArgs e)
{
var ctrlState = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Control);
var isCtrlPressed = (ctrlState & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down || (ctrlState & CoreVirtualKeyStates.Locked) == CoreVirtualKeyStates.Locked;
if (e.Key == Windows.System.VirtualKey.F || (e.Key == Windows.System.VirtualKey.S && isCtrlPressed))
{
// Favorite the item using the defined command
var favoriteCommand = Application.Current.Resources["favoriteCommand"] as ICommand;
favoriteCommand.Execute(PodcastObject);
}
}
Акселераторы мыши
Пользователи знакомы с контекстными меню правой кнопкой мыши, но вы можете предоставить пользователям возможность выполнять распространенные команды только одним щелчком мыши. Чтобы включить этот функционал, можно добавить специальные кнопки на элемент коллекции. Чтобы пользователи могли быстро действовать с помощью мыши и свести к минимуму визуальный беспорядок, можно выбрать только те кнопки, когда пользователь имеет указатель в определенном элементе списка.
В этом примере команда "Избранное" представлена кнопкой, определенной непосредственно в PodcastUserControl. Обратите внимание, что кнопка в этом примере использует ту же команду FavoriteCommand, как и раньше. Чтобы переключить видимость этой кнопки, можно использовать VisualStateManager для переключения между визуальными состояниями при вводе указателя и выходе из элемента управления.
PodcastUserControl.xaml
<UserControl>
<UserControl.ContextFlyout>
<!-- ... -->
</UserControl.ContextFlyout>
<Grid Margin="12,0,12,0">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="HoveringStates">
<VisualState x:Name="HoverButtonsShown">
<VisualState.Setters>
<Setter Target="hoverArea.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="HoverButtonsHidden" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel>
<TextBlock Text="{x:Bind PodcastObject.Title, Mode=OneWay}" Style="{StaticResource TitleTextBlockStyle}" />
<TextBlock Text="{x:Bind PodcastObject.Description, Mode=OneWay}" Style="{StaticResource SubtitleTextBlockStyle}" />
<TextBlock Text="{x:Bind PodcastObject.IsFavorite, Mode=OneWay}" Style="{StaticResource SubtitleTextBlockStyle}"/>
</StackPanel>
<Grid Grid.Column="1" x:Name="hoverArea" Visibility="Collapsed" VerticalAlignment="Stretch">
<AppBarButton Icon="OutlineStar" Label="Favorite" Command="{StaticResource favoriteCommand}" CommandParameter="{x:Bind PodcastObject, Mode=OneWay}" IsTabStop="False" VerticalAlignment="Stretch" />
</Grid>
</Grid>
</UserControl>
Кнопки наведения указателя должны отображаться и исчезать при вводе мыши и выходе из элемента. Для реагирования на события мыши можно использовать события PointerEntered и PointerExited в PodcastUserControl.
PodcastUserControl.xaml.cs
protected override void OnPointerEntered(PointerRoutedEventArgs e)
{
base.OnPointerEntered(e);
// Only show hover buttons when the user is using mouse or pen.
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse || e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Pen)
{
VisualStateManager.GoToState(this, "HoverButtonsShown", true);
}
}
protected override void OnPointerExited(PointerRoutedEventArgs e)
{
base.OnPointerExited(e);
VisualStateManager.GoToState(this, "HoverButtonsHidden", true);
}
Кнопки, отображаемые в состоянии наведения указателя, будут доступны только через тип ввода указателя. Так как эти кнопки ограничены вводом указателя, можно свести к минимуму или удалить заполнение вокруг значка кнопки, чтобы оптимизировать входные данные указателя. Если вы решили сделать это, убедитесь, что размер кнопки не менее 20x20px, чтобы она оставалась пригодной для использования со стилусом и мышью.
Акселераторы для ускорения сенсорного взаимодействия
Проведите пальцем
Команда прокрутки — это акселератор сенсорного ввода, который позволяет пользователям на сенсорных устройствах выполнять распространенные вторичные действия с помощью сенсорного ввода. Функция смахивания позволяет пользователям сенсорного ввода быстро и естественно взаимодействовать с содержимым, используя распространенные действия, такие как смахивание для удаления или смахивание для вызова. Дополнительные сведения см. в статье по командам прокрутки .
Чтобы интегрировать свайп в коллекцию, вам потребуется два компонента: SwipeItems с командами, и SwipeControl, который оборачивает элемент и позволяет взаимодействовать с ним с помощью свайпа.
В PodcastUserControl можно определить SwipeItems в качестве ресурса. В этом примере SwipeItems содержит команду для добавления элемента в избранное.
<UserControl.Resources>
<SymbolIconSource x:Key="FavoriteIcon" Symbol="Favorite"/>
<SwipeItems x:Key="RevealOtherCommands" Mode="Reveal">
<SwipeItem IconSource="{StaticResource FavoriteIcon}" Text="Favorite" Background="Yellow" Invoked="SwipeItem_Invoked"/>
</SwipeItems>
</UserControl.Resources>
SwipeControl обертывает элемент и позволяет пользователю взаимодействовать с ним с помощью жеста свайпа. Обратите внимание, что SwipeControl содержит ссылку на SwipeItems в качестве своих RightItems. Элемент Избранного будет отображаться, когда пользователь проводит пальцем вправо налево.
<SwipeControl x:Name="swipeContainer" RightItems="{StaticResource RevealOtherCommands}">
<!-- The visual state groups moved from the Grid to the SwipeControl, since the SwipeControl wraps the Grid. -->
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="HoveringStates">
<VisualState x:Name="HoverButtonsShown">
<VisualState.Setters>
<Setter Target="hoverArea.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="HoverButtonsHidden" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid Margin="12,0,12,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel>
<TextBlock Text="{x:Bind PodcastObject.Title, Mode=OneWay}" Style="{StaticResource TitleTextBlockStyle}" />
<TextBlock Text="{x:Bind PodcastObject.Description, Mode=OneWay}" Style="{StaticResource SubtitleTextBlockStyle}" />
<TextBlock Text="{x:Bind PodcastObject.IsFavorite, Mode=OneWay}" Style="{StaticResource SubtitleTextBlockStyle}"/>
</StackPanel>
<Grid Grid.Column="1" x:Name="hoverArea" Visibility="Collapsed" VerticalAlignment="Stretch">
<AppBarButton Icon="OutlineStar" Command="{StaticResource favoriteCommand}" CommandParameter="{x:Bind PodcastObject, Mode=OneWay}" IsTabStop="False" LabelPosition="Collapsed" VerticalAlignment="Stretch" />
</Grid>
</Grid>
</SwipeControl>
Когда пользователь проводит пальцем, чтобы вызвать команду "Избранное", вызывается метод Invoked.
private void SwipeItem_Invoked(SwipeItem sender, SwipeItemInvokedEventArgs args)
{
// Favorite the item using the defined command
var favoriteCommand = Application.Current.Resources["favoriteCommand"] as ICommand;
favoriteCommand.Execute(PodcastObject);
}
Потяните, чтобы обновить
Вытягивание для обновления позволяет пользователю извлекать коллекцию данных с помощью сенсорного ввода, чтобы получить дополнительные данные. Дополнительные сведения см. в статье по запросу для обновления .
Ускорители пера
Тип ввода пера обеспечивает точность ввода указателя. Пользователи могут выполнять распространенные действия, такие как открытие контекстных меню с помощью ускорителей на основе пера. Чтобы открыть контекстное меню, пользователи могут касаться экрана с нажатием кнопки "баррель" или долго нажимать на содержимое. Пользователи также могут использовать перо для наведения указателя мыши на содержимое, чтобы получить более глубокое представление о пользовательском интерфейсе, например отображение подсказок, или для отображения дополнительных действий наведения указателя мыши, похожих на мышь.
Чтобы оптимизировать приложение для ввода пера, см. статью о взаимодействии пера и стилуса.
Recommendations
- Убедитесь, что пользователи могут получить доступ ко всем командам со всех типов устройств Windows.
- Включите контекстное меню, которое предоставляет доступ ко всем командам, доступным для элемента коллекции.
- Укажите акселераторы ввода для часто используемых команд.
- Используйте интерфейс ICommand для реализации команд.
Связанные темы
Windows developer