Esecuzione di comandi contestuali per le raccolte e gli elenchi
Numerose app contengono raccolte di contenuti sotto forma di elenchi, griglie e alberi che gli utenti possono modificare. Ad esempio, gli utenti potrebbero essere in grado di eliminare, rinominare, contrassegnare o aggiornare elementi. L'articolo illustra come usare i comandi contestuali per implementare queste tipologie di azioni in un modo che consenta la migliore esperienza possibile per tutti i tipi di input.
API importanti: Interfaccia ICommand, Proprietà UIElement.ContextFlyout, Interfaccia INotifyPropertyChanged
Creazione di comandi per tutti i tipi di input
Visto che gli utenti possono interagire con un'app di Windows usando un'ampia gamma di dispositivi e input, l'app dovrebbe esporre i comandi attraverso menu di scelta rapida indipendenti dall'input e acceleratori specifici dell'input. L'inclusione di entrambi consentirà all'utente di richiamare velocemente i comandi sul contenuto indipendentemente dal tipo di input o dal tipo di dispositivo.
La tabella mostra alcuni comandi tipici di raccolta e le modalità per la loro esposizione.
Comando | Indipendente dall'input | Acceleratore tramite mouse | Acceleratore tramite tastiera | Acceleratore tramite tocco |
---|---|---|---|---|
Elimina elemento | Menu di scelta rapida | Pulsante attivato al passaggio del mouse | Tasto CANC | Scorri rapidamente per eliminare |
Contrassegna elemento | Menu di scelta rapida | Pulsante attivato al passaggio del mouse | CTRL + MAIUSC + G | Scorri rapidamente per contrassegnare |
Aggiorna dati | Menu di scelta rapida | N/D | Tasto F5 | Funzionalità Trascina verso il basso |
Segna un elemento come preferito | Menu di scelta rapida | Pulsante attivato al passaggio del mouse | F, CTRL+S | Scorrere rapidamente per contrassegnare l'elemento come preferito |
In generale, dovresti rendere tutti i comandi di un elemento disponibili nel menu di scelta rapida dell'elemento stesso. I menu di scelta rapida devono essere resi accessibili agli utenti indipendentemente dal tipo di input e devono contenere tutti i comandi contestuali che l'utente può eseguire.
Per i comandi a cui si accede con frequenza, considera l'uso degli acceleratori per input. Gli acceleratori per input permettono all'utente di eseguire azioni rapidamente, in base al dispositivo di input. Gli acceleratori per input includono:
- Scorrere rapidamente per eseguire l'azione (acceleratore tramite tocco)
- Trascinare verso il basso per aggiornare i dati (acceleratore tramite tocco)
- Tasti di scelta rapida (acceleratore tramite tastiera)
- Tasti di scelta (acceleratore tramite tastiera)
- Pulsanti attivati al passaggio del mouse e della penna (acceleratore tramite puntatore)
Nota
Gli utenti dovrebbero essere in grado di accedere a tutti i comandi da qualsiasi tipo di dispositivo. Ad esempio, se i comandi dell'app sono esposti unicamente attraverso gli acceleratori tramite puntatore (pulsanti attivati al passaggio del mouse), gli utenti che usano il tocco non saranno in grado di accedervi. Usa almeno un menu di scelta rapida per rendere possibile l'accesso a tutti i comandi.
Esempio: modello di dati PodcastObject
Al fine di illustrare i nostri consigli relativi all'esecuzione dei comandi, in questo articolo viene creato un elenco di podcast per un'app Podcast. Il codice di esempio illustra come permettere all'utente di segnare un particolare podcast come "preferito" all'interno di un elenco.
Questa è la definizione dell'oggetto podcast che verrà usato:
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));
}
}
Tieni presente che PodcastObject implementa INotifyPropertyChanged per rispondere alle modifiche di proprietà quando l'utente attiva o disattiva la proprietà IsFavorite.
Definizione dei comandi con l'interfaccia ICommand
L'interfaccia ICommand è utile per definire un comando che sia disponibile per diversi tipi di input. Ad esempio, piuttosto che scrivere lo stesso codice per un comando di eliminazione in due gestori di eventi diversi, uno per quando l'utente preme il tasto CANC e uno per quando fa clic con il pulsante destro del mouse su "Elimina" in un menu di scelta rapida, puoi implementare la logica di eliminazione una sola volta, sotto forma di ICommand, e quindi renderla disponibile per diversi tipi di input.
Dobbiamo definire l'interfaccia ICommand che rappresenta l'azione "Segna come preferito". Useremo il metodo del comando Execute per contrassegnare un podcast come preferito. Questo particolare podcast sarà reso disponibile per il metodo di esecuzione attraverso il parametro del comando, che può essere associato tramite la proprietà 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;
}
}
Per usare lo stesso comando con diverse raccolte ed elementi, puoi archiviarlo come una risorsa nella pagina o nell'app.
<Application.Resources>
<local:FavoriteCommand x:Key="favoriteCommand" />
</Application.Resources>
Per eseguire il comando, devi chiamare il metodo Execute correlato.
// Favorite the item using the defined command
var favoriteCommand = Application.Current.Resources["favoriteCommand"] as ICommand;
favoriteCommand.Execute(PodcastObject);
Creazione di un elemento UserControl per rispondere a una varietà di input
Quando hai un elenco di elementi, ognuno dei quali deve rispondere a più input, puoi semplificare il codice definendo un UserControl per l'elemento e usandolo per definire il menu di scelta rapida degli elementi e i gestori di eventi.
Per creare un elemento UserControl in Visual Studio:
- In Esplora soluzioni fai clic con il pulsante destro del mouse sul progetto. Viene visualizzato un menu di scelta rapida.
- Selezionare Aggiungi > Nuovo elemento...
Viene visualizzata la finestra di dialogo Aggiungi nuovo elemento. - Seleziona UserControl dall'elenco di elementi. Assegnagli il nome che vuoi e fai clic su Aggiungi. Visual Studio genererà automaticamente uno stub UserControl.
Nel podcast di esempio ogni podcast sarà visualizzato all'interno di un elenco che esporrà un'ampia varietà di modi per contrassegnare un podcast come "Preferito". L'utente potrà eseguire le azioni seguenti per contrassegnare il podcast come "Preferito":
- Richiamare un menu di scelta rapida
- Usare i tasti di scelta rapida
- Mostrare un pulsante che si attiva al passaggio del mouse
- Eseguire un movimento di scorrimento rapido
Per incapsulare questi comportamenti e usare il FavoriteCommand, creeremo un UserControl denominato "PodcastUserControl" per rappresentare un podcast nell'elenco.
Il PodcastUserControl visualizza i campi del PodcastObject come oggetti TextBlock e risponde a diverse interazioni da parte dell'utente. In questo articolo faremo riferimento e amplieremo l'argomento del 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.
}
}
Il PodcastUserControl conserva un riferimento al PodcastObject come DependencyProperty. Ciò consente di eseguire il binding di PodcastObject al PodcastUserControl.
Dopo aver generato alcuni PodcastObject, puoi creare un elenco di podcast eseguendo il binding dei PodcastObject a una ListView. Gli oggetti PodcastUserControl descrivono la visualizzazione dei PodcastObject e sono pertanto impostati mediante la proprietà ItemTemplate di 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>
Creazione di menu di scelta rapida
I menu di scelta rapida mostrano un elenco di comandi o opzioni quando l'utente li richiede. I menu di scelta rapida forniscono i comandi contestuali correlati all'elemento associato e sono solitamente riservati per azioni secondarie specifiche di quel determinato elemento.
L'utente può richiamare i menu di scelta rapida usando queste "azioni contestuali":
Input | Azione contestuale |
---|---|
Mouse | Clic con il pulsante destro del mouse |
Tastiera | MAIUSC+F10, pulsante Menu |
Touch | Pressione prolungata sull'elemento |
Penna | Pressione sul pulsante della penna, pressione prolungata sull'elemento |
Gamepad | Pulsante Menu |
Dal momento che l'utente può aprire un menu di scelta rapida indipendentemente dal tipo di input, il menu di scelta rapida dovrebbe contenere tutti i comandi contestuali disponibili per l'elemento di elenco.
ContextFlyout
La proprietà ContextFlyout, definita dalla classe UIElement, rende più agevole la creazione di un menu di scelta rapida che funzioni con tutti i tipi di input. Fornisci un riquadro a comparsa che rappresenta il menu contestuale utilizzando MenuFlyout o CommandBarFlyout e quando l'utente esegue una "azione di contesto" come definita sopra, verrà visualizzato MenuFlyout o CommandBarFlyout corrispondente all'elemento.
Vedere menu e menu di scelta rapida per informazioni sull'identificazione di scenari di menu di scelta rapida e indicazioni su quando usare il riquadro a comparsa del menu a comparsa e la barra dei comandi.
Per questo esempio, useremo MenuFlyout e inizieremo aggiungendo un ContextFlyout al PodcastUserControl. Il MenuFlyout specificato come ContextFlyout contiene un singolo elemento per segnare un podcast come preferito. Questo MenuFlyoutItem usa il favoriteCommand definito in precedenza, con il CommandParameter associato al 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>
Puoi anche usare l'evento ContextRequested per rispondere alle azioni contestuali. L'evento ContextRequested non viene generato se è stato specificato un ContextFlyout.
Creazione di acceleratori per input
Sebbene ogni elemento nella raccolta debba avere un menu di scelta rapida contenente tutti i comandi contestuali, puoi consentire agli utenti di eseguire rapidamente un insieme più limitato di comandi usati di frequente. Ad esempio, un'app di posta elettronica potrebbe avere comandi secondari, come Rispondi, Archivia, Sposta nella cartella, Imposta contrassegno ed Elimina, che sono visualizzati nel menu di scelta rapida, ma i comandi più comuni sono Elimina e Contrassegna. Una volta identificati i comandi più comuni, puoi usare gli acceleratori basati su input per rendere più semplice agli utenti l'esecuzione di tali comandi.
Nell'app Podcast, il comando eseguito più di frequente è quello che contrassegna un elemento come"Preferito".
Acceleratori tramite tastiera
Gestione dei tasti di scelta rapida e dei tasti diretti
A seconda del tipo di contenuto, puoi identificare determinate combinazioni di tasti che consentono di eseguire un'azione. Ad esempio, in un'app di posta elettronica il tasto CANC potrebbe essere usato per eliminare il messaggio di posta elettronica selezionato. In un'app Podcast, i tasti CTRL+S o F potrebbero essere usati per segnare un podcast come preferito per un utilizzo successivo. Benché alcuni comandi abbiano tasti di scelta rapida comuni e ben noti, come CANC per eliminare, altri comandi hanno tasti di scelta rapida specifici dell'app o del dominio. Se possibile, usa collegamenti ben noti oppure specifica un testo di promemoria in una descrizione comando per spiegare all'utente cosa fa il comando di scelta rapida.
L'app può rispondere quando l'utente preme un tasto usando l'evento KeyDown. In generale, gli utenti si aspettano che l'app risponda appena premono il tasto, invece di aspettare che il tasto venga rilasciato.
Questo esempio illustra come aggiungere il gestore KeyDown al PodcastUserControl per contrassegnare un podcast come preferito quando l'utente preme CTRL+ S o F. Usa lo stesso comando usato in precedenza.
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);
}
}
Acceleratori tramite mouse
Gli utenti hanno familiarità con i menu di scelta rapida che compaiono facendo clic con il pulsante destro del mouse, tuttavia potresti voler consentire agli utenti di eseguire i comandi più comuni con un semplice clic del mouse. Per abilitare questa esperienza, puoi includere pulsanti dedicati nel canvas dell'elemento della raccolta. Per consentire agli utenti di agire rapidamente mediante il mouse e per ridurre al minimo la confusione visiva, puoi scegliere di visualizzare questi pulsanti solo quando l'utente posiziona il puntatore all'interno di uno specifico elemento elenco.
In questo esempio, il comando Preferito è rappresentato da un pulsante definito direttamente nel PodcastUserControl. Nota che il pulsante in questo esempio usa lo stesso comando, FavoriteCommand, usato in precedenza. Per attivare o disattivare la visibilità di questo pulsante, puoi usare VisualStateManager per spostarti tra gli stati di visualizzazione quando il puntatore entra ed esce dal controllo.
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>
I pulsanti che si attivano al passaggio del mouse dovrebbero comparire e scomparire quando il mouse entra ed esce dall'elemento. Per rispondere agli eventi del mouse, puoi usare gli eventi PointerEntered e PointerExited nel 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);
}
I pulsanti visualizzati nello stato di passaggio del mouse saranno accessibili solo mediante il tipo di input del puntatore. Dato che questi pulsanti sono limitati all'input del puntatore, puoi scegliere di ridurre al minimo o rimuovere la spaziatura attorno all'icona del pulsante per ottimizzare l'input del puntatore. In tal caso, verifica che la superficie del pulsante sia di almeno 20x20 px in modo che resti utilizzabile con penna e mouse.
Acceleratori tramite tocco
Scorrimento rapido
L'esecuzione del comando di scorrimento rapido è un acceleratore di tocco che consente agli utenti di dispositivi con touchscreen di eseguire comuni azioni secondarie mediante il tocco. Lo scorrimento rapido permette agli utenti di touchscreen di interagire in modo rapido e naturale con il contenuto usando azioni comuni, come Scorrere per eliminare o Scorrere per richiamare. Per altre informazioni, vedi l'articolo Swipe.
Per integrare lo scorrimento rapido nella raccolta, sono necessari due componenti: SwipeItems, che ospita i comandi; e swipeControl, che esegue il wrapping dell'elemento e consente l'interazione di scorrimento rapido.
SwipeItems può essere definito come risorsa in PodcastUserControl. In questo esempio, SwipeItems contiene un comando per contrassegnare un elemento come preferito.
<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 esegue il wrapping dell'elemento e consente all'utente di interagire con esso con il movimento di scorrimento rapido. SwipeControl contiene un riferimento a SwipeItems come RightItems associato. Quando l'utente scorre rapidamente da destra a sinistra, l'elemento impostato come preferito viene visualizzato.
<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>
Quando l'utente scorre rapidamente per richiamare il comando Preferito, viene chiamato il metodo 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);
}
Funzionalità Trascina verso il basso
L'aggiornamento tramite trascinamento verso il basso consente a un utente di trascinare verso il basso una raccolta di dati tramite tocco per recuperare più dati. Per altre informazioni, vedi l'articolo Aggiornamento tramite trascinamento verso il basso.
Acceleratori basati su penna
Il tipo di input penna offre la precisione dell'input del puntatore. Gli utenti possono eseguire operazioni comuni, come l'apertura di menu di scelta rapida, usando acceleratori basati su penna. Per aprire un menu di scelta rapida, gli utenti possono toccare lo schermo tenendo premuto il pulsante della penna o mediante una pressione prolungata sul contenuto. Gli utenti possono anche usare la penna per passare su un contenuto per ottenere informazioni più approfondite dell'interfaccia utente, ad esempio mediante la visualizzazione di descrizioni comando, oppure per visualizzare azioni al passaggio del puntatore secondarie, simili a quelle eseguite con il mouse.
Per ottimizzare l'app per l'input penna, vedi l'articolo Interazioni tramite penna e Windows Ink nelle app UWP.
Consigli
- Assicurati che gli utenti possano accedere a tutti i comandi da qualsiasi tipo di dispositivo di Windows.
- Includi un menu di scelta rapida che fornisca accesso a tutti i comandi disponibili per un elemento della raccolta.
- Fornisci acceleratori di input per i comandi usati di frequente.
- Usa l'interfaccia ICommand per implementare i comandi.