Perintah kontekstual untuk koleksi dan daftar
Banyak aplikasi berisi kumpulan konten dalam bentuk daftar, kisi, dan pohon yang dapat dimanipulasi pengguna. Misalnya, pengguna mungkin dapat menghapus, mengganti nama, menandai, atau menyegarkan item. Artikel ini menunjukkan kepada Anda cara menggunakan perintah kontekstual untuk mengimplementasikan tindakan semacam ini dengan cara yang memberikan pengalaman terbaik untuk semua jenis input.
API Penting: Antarmuka ICommand, properti UIElement.ContextFlyout, antarmuka INotifyPropertyChanged
Membuat perintah untuk semua jenis input
Karena pengguna dapat berinteraksi dengan aplikasi Windows menggunakan berbagai perangkat dan input, aplikasi Anda harus mengekspos perintah melalui menu konteks input-agnostik dan akselerator khusus input. Termasuk keduanya memungkinkan pengguna dengan cepat memanggil perintah pada konten, terlepas dari input atau jenis perangkat.
Tabel ini memperlihatkan beberapa perintah koleksi umum dan cara untuk mengekspos perintah tersebut.
Perintah | Input-agnostik | Akselerator mouse | Akselerator keyboard | Akselerator sentuh |
---|---|---|---|---|
Hapus item | Menu Konteks | Tombol Arahkan mouse ke arah yang lebih tinggi | Kunci DEL | Gesek untuk menghapus |
Bendera item | Menu Konteks | Tombol Arahkan mouse ke arah yang lebih tinggi | Ctrl+Shift+G | Gesek ke bendera |
Memuat ulang data | Menu Konteks | T/A | Kunci F5 | Tarik untuk menyegarkan |
Favoritkan item | Menu Konteks | Tombol Arahkan mouse ke arah yang lebih tinggi | F, Ctrl+S | Geser ke favorit |
Secara umum, Anda harus membuat semua perintah untuk item tersedia di menu konteks item. Menu konteks dapat diakses oleh pengguna terlepas dari jenis input, dan harus berisi semua perintah kontekstual yang dapat dilakukan pengguna.
Untuk perintah yang sering diakses, pertimbangkan untuk menggunakan akselerator input. Akselerator input memungkinkan pengguna melakukan tindakan dengan cepat, berdasarkan perangkat input mereka. Akselerator input meliputi:
- Gesek ke tindakan (akselerator sentuh)
- Tarik untuk menyegarkan data (akselerator sentuh)
- Pintasan keyboard (akselerator keyboard)
- Tombol akses (akselerator keyboard)
- Tombol Mouse & Pena melayang (akselerator penunjuk)
Catatan
Pengguna harus dapat mengakses semua perintah dari semua jenis perangkat. Misalnya, jika perintah aplikasi Anda hanya diekspos melalui akselerator penunjuk tombol hover, pengguna sentuh tidak akan dapat mengaksesnya. Minimal, gunakan menu konteks untuk menyediakan akses ke semua perintah.
Contoh: Model data PodcastObject
Untuk menunjukkan rekomendasi perintah kami, artikel ini membuat daftar podcast untuk aplikasi podcast. Contoh kode menunjukkan cara mengaktifkan pengguna untuk "favorit" podcast tertentu dari daftar.
Berikut adalah definisi untuk objek podcast yang akan kita kerjakan:
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));
}
}
Perhatikan bahwa PodcastObject mengimplementasikan INotifyPropertyChanged untuk merespons perubahan properti saat pengguna mengaktifkan properti IsFavorite.
Menentukan perintah dengan antarmuka ICommand
Antarmuka ICommand membantu Anda menentukan perintah yang tersedia untuk beberapa jenis input. Misalnya, alih-alih menulis kode yang sama untuk perintah hapus di dua penanganan aktivitas yang berbeda, satu untuk ketika pengguna menekan tombol Hapus dan satu untuk ketika pengguna mengklik kanan "Hapus" di menu konteks, Anda dapat mengimplementasikan logika penghapusan Anda sekali, sebagai ICommand, dan kemudian membuatnya tersedia untuk jenis input yang berbeda.
Kita perlu menentukan ICommand yang mewakili tindakan "Favorit". Kita akan menggunakan metode Jalankan perintah untuk memfavoritkan podcast. Podcast tertentu akan diberikan ke metode eksekusi melalui parameter perintah, yang dapat diikat menggunakan properti 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;
}
}
Untuk menggunakan perintah yang sama dengan beberapa koleksi dan elemen, Anda dapat menyimpan perintah sebagai sumber daya di halaman atau di aplikasi.
<Application.Resources>
<local:FavoriteCommand x:Key="favoriteCommand" />
</Application.Resources>
Untuk menjalankan perintah, Anda memanggil metode Execute-nya.
// Favorite the item using the defined command
var favoriteCommand = Application.Current.Resources["favoriteCommand"] as ICommand;
favoriteCommand.Execute(PodcastObject);
Membuat UserControl untuk merespons berbagai input
Ketika Anda memiliki daftar item dan masing-masing item tersebut harus merespons beberapa input, Anda dapat menyederhanakan kode Anda dengan menentukan UserControl untuk item dan menggunakannya untuk menentukan menu konteks item dan penanganan peristiwa Anda.
Untuk membuat UserControl di Visual Studio:
- Di Penjelajah Solusi, klik kanan proyek. Menu konteks muncul.
- Pilih Tambahkan > Item Baru...
Dialog Tambahkan Item Baru muncul. - Pilih UserControl dari daftar item. Beri nama yang Anda inginkan dan klik Tambahkan. Visual Studio akan menghasilkan UserControl stub untuk Anda.
Dalam contoh podcast kami, setiap podcast akan ditampilkan dalam daftar, yang akan mengekspos berbagai cara untuk "Favorit" podcast. Pengguna akan dapat melakukan tindakan berikut ke "Favorit" podcast:
- Memanggil menu konteks
- Melakukan pintasan keyboard
- Perlihatkan tombol hover
- Melakukan gerakan gesek
Untuk merangkum perilaku ini dan menggunakan FavoriteCommand, mari kita buat UserControl baru bernama "PodcastUserControl" untuk mewakili podcast dalam daftar.
PodcastUserControl menampilkan bidang PodcastObject sebagai TextBlocks, dan merespons berbagai interaksi pengguna. Kami akan mereferensikan dan memperluas PodcastUserControl di seluruh artikel ini.
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.
}
}
Perhatikan bahwa PodcastUserControl mempertahankan referensi ke PodcastObject sebagai DependencyProperty. Ini memungkinkan kita untuk mengikat PodcastObjects ke PodcastUserControl.
Setelah membuat beberapa PodcastObjects, Anda dapat membuat daftar podcast dengan mengikat PodcastObjects ke ListView. Objek PodcastUserControl menjelaskan visualisasi PodcastObjects, dan karenanya diatur menggunakan 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>
Membuat menu konteks
Menu konteks menampilkan daftar perintah atau opsi saat pengguna memintanya. Menu konteks menyediakan perintah kontekstual yang terkait dengan elemen terlampirnya, dan umumnya dicadangkan untuk tindakan sekunder khusus untuk item tersebut.
Pengguna dapat memanggil menu konteks menggunakan "tindakan konteks" ini:
Input | Tindakan konteks |
---|---|
Mouse | Klik kanan |
Keyboard | Shift+F10, tombol Menu |
Sentuh | Tekan lama pada item |
Pena | Tekan tombol barel, tekan panjang item |
Gamepad | Tombol Menu |
Karena pengguna dapat membuka menu konteks terlepas dari jenis input, menu konteks Anda harus berisi semua perintah kontekstual yang tersedia untuk item daftar.
ContextFlyout
Properti ContextFlyout, yang ditentukan oleh kelas UIElement, memudahkan untuk membuat menu konteks yang berfungsi dengan semua jenis input. Anda menyediakan flyout yang mewakili menu konteks Anda menggunakan MenuFlyout atau CommandBarFlyout, dan ketika pengguna melakukan "tindakan konteks" seperti yang didefinisikan di atas, MenuFlyout atau CommandBarFlyout yang sesuai dengan item akan ditampilkan.
Lihat menu dan menu konteks untuk membantu mengidentifikasi menu vs. skenario menu konteks dan panduan tentang kapan menggunakan flyout menu vs. flyout bilah perintah.
Untuk contoh ini, kita akan menggunakan MenuFlyout dan akan dimulai dengan menambahkan ContextFlyout ke PodcastUserControl. MenuFlyout yang ditentukan sebagai ContextFlyout berisi satu item untuk memfavoritkan podcast. Perhatikan bahwa MenuFlyoutItem ini menggunakan favoriteCommand yang ditentukan di atas, dengan CommandParameter terikat ke 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>
Perhatikan bahwa Anda juga dapat menggunakan peristiwa ContextRequested untuk merespons tindakan konteks. Peristiwa ContextRequested tidak akan diaktifkan jika ContextFlyout telah ditentukan.
Membuat akselerator input
Meskipun setiap item dalam koleksi harus memiliki menu konteks yang berisi semua perintah kontekstual, Anda mungkin ingin memungkinkan pengguna untuk dengan cepat melakukan serangkaian perintah yang sering dilakukan. Misalnya, aplikasi surat mungkin memiliki perintah sekunder seperti Balas, Arsip, Pindahkan ke Folder, Atur Bendera, dan Hapus yang muncul di menu konteks, tetapi perintah yang paling umum adalah Hapus dan Bendera. Setelah mengidentifikasi perintah mana yang paling umum, Anda dapat menggunakan akselerator berbasis input untuk membuat perintah ini lebih mudah dilakukan pengguna.
Di aplikasi podcast, perintah yang sering dilakukan adalah perintah "Favorit".
Akselerator keyboard
Pintasan dan penanganan kunci langsung
Bergantung pada jenis konten, Anda dapat mengidentifikasi kombinasi kunci tertentu yang harus melakukan tindakan. Di aplikasi email, misalnya, kunci DEL dapat digunakan untuk menghapus email yang dipilih. Dalam aplikasi podcast, kunci Ctrl+S atau F dapat memfavoritkan podcast untuk nanti. Meskipun beberapa perintah memiliki pintasan keyboard umum dan terkenal seperti DEL untuk dihapus, perintah lain memiliki pintasan khusus aplikasi atau domain. Gunakan pintasan terkenal jika memungkinkan, atau pertimbangkan untuk memberikan teks pengingat dalam tipsalat untuk mengajari pengguna tentang perintah pintasan.
Aplikasi Anda dapat merespons saat pengguna menekan tombol menggunakan peristiwa KeyDown . Secara umum, pengguna mengharapkan bahwa aplikasi akan merespons ketika mereka pertama kali menekan tombol ke bawah, daripada menunggu sampai mereka merilis kunci.
Contoh ini menjelaskan cara menambahkan handler KeyDown ke PodcastUserControl ke podcast favorit saat pengguna menekan Ctrl+S atau F. Ini menggunakan perintah yang sama seperti sebelumnya.
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);
}
}
Akselerator mouse
Pengguna terbiasa dengan menu konteks klik kanan, tetapi Anda mungkin ingin memberdayakan pengguna untuk melakukan perintah umum hanya menggunakan satu klik mouse. Untuk mengaktifkan pengalaman ini, Anda dapat menyertakan tombol khusus di kanvas item koleksi Anda. Untuk memberdayakan pengguna untuk bertindak cepat menggunakan mouse, dan untuk meminimalkan kekacauan visual, Anda dapat memilih untuk hanya mengungkapkan tombol-tombol ini ketika pengguna memiliki pointer mereka dalam item daftar tertentu.
Dalam contoh ini, perintah Favorit diwakili oleh tombol yang ditentukan langsung di PodcastUserControl. Perhatikan bahwa tombol dalam contoh ini menggunakan perintah yang sama, FavoriteCommand, seperti sebelumnya. Untuk mengalihkan visibilitas tombol ini, Anda dapat menggunakan VisualStateManager untuk beralih antar status visual saat penunjuk memasuki dan keluar dari kontrol.
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>
Tombol arahkan kursor akan muncul dan menghilang saat mouse masuk dan keluar dari item. Untuk menanggapi peristiwa mouse, Anda dapat menggunakan peristiwa PointerEntered dan PointerExited di 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);
}
Tombol yang ditampilkan dalam status hover hanya akan dapat diakses melalui jenis input penunjuk. Karena tombol ini terbatas pada input pointer, Anda dapat memilih untuk meminimalkan atau menghapus padding di sekitar ikon tombol untuk mengoptimalkan input pointer. Jika Anda memilih untuk melakukannya, pastikan bahwa jejak tombol setidaknya 20x20px untuk tetap dapat digunakan dengan pena dan mouse.
Akselerator sentuh
Babatan
Perintah gesek adalah akselerator sentuh yang memungkinkan pengguna pada perangkat sentuh melakukan tindakan sekunder umum menggunakan sentuhan. Geser memberdayakan pengguna untuk berinteraksi dengan konten dengan cepat dan alami, menggunakan tindakan umum seperti Swipe-to-Delete atau Swipe-to-Invoke. Lihat artikel perintah gesek untuk mempelajari selengkapnya.
Untuk mengintegrasikan gesek ke dalam koleksi Anda, Anda memerlukan dua komponen: SwipeItems, yang menghosting perintah; dan SwipeControl, yang membungkus item dan memungkinkan interaksi gesek.
SwipeItems dapat didefinisikan sebagai Sumber Daya di PodcastUserControl. Dalam contoh ini, SwipeItems berisi perintah ke Favoritkan item.
<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 membungkus item dan memungkinkan pengguna untuk berinteraksi dengannya menggunakan gerakan gesek. Perhatikan bahwa SwipeControl berisi referensi ke SwipeItems sebagai RightItems-nya. Item Favorit akan ditampilkan saat pengguna menggesek dari kanan ke kiri.
<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>
Ketika pengguna menggesek untuk memanggil perintah Favorit, metode Yang dipanggil dipanggil dipanggil.
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);
}
Tarik untuk menyegarkan
Tarik untuk menyegarkan memungkinkan pengguna menarik ke bawah pada kumpulan data menggunakan sentuhan untuk mengambil lebih banyak data. Lihat artikel tarik untuk me-refresh untuk mempelajari selengkapnya.
Akselerator pena
Jenis input pena menyediakan presisi input pointer. Pengguna dapat melakukan tindakan umum seperti membuka menu konteks menggunakan akselerator berbasis pena. Untuk membuka menu konteks, pengguna dapat mengetuk layar dengan tombol laras ditekan, atau menekan lama konten. Pengguna juga dapat menggunakan pena untuk mengarahkan kursor ke konten untuk mendapatkan pemahaman yang lebih mendalam tentang UI seperti menampilkan tipsalat, atau untuk mengungkapkan tindakan hover sekunder, mirip dengan mouse.
Untuk mengoptimalkan aplikasi Anda untuk input pena, lihat artikel interaksi pena dan stylus.
Rekomendasi
- Pastikan pengguna dapat mengakses semua perintah dari semua jenis perangkat Windows.
- Sertakan menu konteks yang menyediakan akses ke semua perintah yang tersedia untuk item koleksi.
- Berikan akselerator input untuk perintah yang sering digunakan.
- Gunakan antarmuka ICommand untuk mengimplementasikan perintah.
Topik terkait
Windows developer