Condividi tramite


Comandi contestuali per raccolte ed elenchi

Molte app contengono raccolte di contenuto 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 gli elementi. Questo articolo illustra come usare i comandi contestuali per implementare questi tipi di azioni in modo da garantire la migliore esperienza possibile per tutti i tipi di input.

API importanti: interfaccia ICommand, proprietà UIElement.ContextFlyout, interfaccia INotifyPropertyChanged

Usare un'ampia gamma di input per eseguire il comando Preferito

Creazione di comandi per tutti i tipi di input

Poiché gli utenti possono interagire con un'app di Windows usando un'ampia gamma di dispositivi e input, l'app deve esporre i comandi sia tramite menu contestuali indipendenti dall'input che acceleratori specifici dell'input. L'inclusione di entrambi consente all'utente di richiamare rapidamente i comandi sul contenuto, indipendentemente dal tipo di input o dispositivo.

Questa tabella mostra alcuni comandi di raccolta tipici e modi per esporre tali comandi.

Command indipendente dall'input Acceleratore del mouse Acceleratore da tastiera Acceleratore di tocco
Elimina elemento Menu di scelta rapida Pulsante al passaggio del cursore Tasto CANC Scorri per eliminare
Contrassegna elemento Menu di scelta rapida Pulsante al passaggio del cursore CTRL + MAIUSC + G Scorrimento rapido per contrassegnare
Aggiornare i dati Menu di scelta rapida N/A Tasto F5 Funzionalità Trascina verso il basso
Elemento preferito Menu di scelta rapida Pulsante al passaggio del cursore F, Ctrl+S Scorrere rapidamente fino ai preferiti
  • In generale, è consigliabile rendere disponibili tutti i comandi per un elemento nel menu di scelta rapida dell'elemento. I menu di scelta rapida sono 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 di frequente, è consigliabile usare gli acceleratori di input. Gli acceleratori di input consentono all'utente di eseguire rapidamente azioni in base al dispositivo di input. Gli acceleratori di input includono:

    • Scorri per azione (acceleratore tattile)
    • Trascina per aggiornare i dati (gesto di tocco)
    • Tasti di scelta rapida (acceleratore di tastiera)
    • Tasti di accesso (acceleratore da tastiera)
    • Pulsanti mouse e penna al passaggio del mouse (acceleratore puntatore)

Annotazioni

Gli utenti devono essere in grado di accedere a tutti i comandi da qualsiasi tipo di dispositivo. Ad esempio, se i comandi dell'app vengono esposti solo tramite i tasti di scelta rapida del puntatore del mouse, gli utenti con tocco non potranno accedervi. Usare almeno un menu di scelta rapida per fornire l'accesso a tutti i comandi.

Esempio: Modello di dati PodcastObject

Per dimostrare le nostre raccomandazioni autorevoli, questo articolo crea un elenco di podcast per un'app per podcast. Il codice di esempio illustra come consentire all'utente di "aggiungere preferiti" un particolare podcast da un elenco.

Ecco la definizione per l'oggetto podcast con cui lavoreremo:

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

Si noti che PodcastObject implementa INotifyPropertyChanged per rispondere alle modifiche alle proprietà quando l'utente attiva/disattiva la proprietà IsFavorite.

Definizione dei comandi con l'interfaccia ICommand

L'interfaccia ICommand consente di definire un comando disponibile per più tipi di input. Ad esempio, invece di scrivere lo stesso codice per un comando delete in due gestori eventi diversi, uno per quando l'utente preme il tasto Elimina e uno per quando l'utente fa clic con il pulsante destro del mouse su "Elimina" in un menu di scelta rapida, è possibile implementare la logica di eliminazione una sola volta, come ICommand, e quindi renderla disponibile per diversi tipi di input.

È necessario definire il comando ICommand che rappresenta l'azione "Preferito". Useremo il metodo Execute del comando per aggiungere un podcast preferito. Il podcast specifico verrà fornito al metodo execute tramite il parametro del comando, che può essere associato usando 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 più raccolte ed elementi, è possibile archiviare il comando come risorsa nella pagina o nell'app.

<Application.Resources>
    <local:FavoriteCommand x:Key="favoriteCommand" />
</Application.Resources>

Per eseguire il comando, chiamare il relativo metodo Execute .

// Favorite the item using the defined command
var favoriteCommand = Application.Current.Resources["favoriteCommand"] as ICommand;
favoriteCommand.Execute(PodcastObject);

Creazione di un oggetto UserControl per rispondere a un'ampia gamma di input

Quando si dispone di un elenco di elementi e ognuno di questi elementi deve rispondere a più input, è possibile semplificare il codice definendo un UserControl per l'elemento e usandolo per definire il menu di scelta rapida e i gestori eventi degli elementi.

Per creare un oggetto UserControl in Visual Studio:

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto. Viene visualizzato un menu di scelta rapida.
  2. Selezionare Aggiungi > nuovo elemento...
    Verrà visualizzata la finestra di dialogo Aggiungi nuovo elemento .
  3. Selezionare UserControl nell'elenco di elementi. Assegna il nome desiderato e fai clic su Aggiungi. Visual Studio genererà automaticamente uno stub UserControl.

Nell'esempio di podcast, ogni podcast verrà visualizzato in un elenco, che espone una varietà di modi per mettere tra i preferiti un podcast. L'utente sarà in grado di eseguire le azioni seguenti per aggiungere il podcast ai Preferiti:

  • Richiamare un menu di scelta rapida
  • Eseguire scelte rapide da tastiera
  • Mostra un pulsante di hover
  • Eseguire un movimento di scorrimento rapido

Per incapsulare questi comportamenti e usare FavoriteCommand, creiamo un nuovo UserControl denominato "PodcastUserControl" per rappresentare un podcast nell'elenco.

PodcastUserControl visualizza i campi del PodcastObject come TextBlocks e risponde a varie interazioni dell'utente. Faremo riferimento ed espanderemo il PodcastUserControl in tutto questo articolo.

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

Si noti che PodcastUserControl mantiene un riferimento a PodcastObject come DependencyProperty. In questo modo è possibile associare PodcastObjects al PodcastUserControl.

Dopo aver generato alcuni PodcastObject, puoi creare un elenco di podcast associando PodcastObject a un Controllo ListView. Gli oggetti PodcastUserControl descrivono la visualizzazione dei PodcastObject e vengono quindi impostati usando 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 visualizzano un elenco di comandi o opzioni quando l'utente li richiede. I menu di scelta rapida forniscono comandi contestuali correlati all'elemento associato e sono in genere riservati alle azioni secondarie specifiche di tale elemento.

Mostra un menu di scelta rapida nella voce

L'utente può richiamare i menu di scelta rapida usando queste "azioni di contesto":

Input Azione di contesto
Mouse Fare clic con il pulsante destro del mouse
Keyboard MAIUSC+F10, pulsante Menu
Touch Pressione lunga sull'elemento
Penna Pressione del pulsante cilindrico, pressione prolungata sull'oggetto
Game pad Pulsante Menu

Poiché l'utente può aprire un menu di scelta rapida indipendentemente dal tipo di input, il menu di scelta rapida deve contenere tutti i comandi contestuali disponibili per la voce di elenco.

ContextFlyout

La proprietà ContextFlyout, definita dalla classe UIElement, semplifica la creazione di un menu di scelta rapida che funziona con tutti i tipi di input. Fornisci un riquadro a comparsa che rappresenta il menu di scelta rapida utilizzando MenuFlyout o CommandBarFlyout e, quando l'utente esegue un'azione contestuale come definito in precedenza, verrà visualizzato il MenuFlyout o il CommandBarFlyout corrispondente all'elemento.

Vedere menu e menu contestuali per aiuto nell'identificazione degli scenari di menu contestuali e indicazioni su quando usare il riquadro a comparsa e il riquadro a comparsa della barra dei comandi.

Per questo esempio, useremo MenuFlyout e inizieremo aggiungendo un ContextFlyout al PodcastUserControl. Il MenuFlyout specificato quale ContextFlyout contiene un singolo elemento per segnare come preferito un podcast. Si noti che questo MenuFlyoutItem utilizza il comando preferito definito in precedenza, con il CommandParameter associato a 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>

Si noti che è anche possibile usare l'evento ContextRequested per rispondere alle azioni del contesto. L'evento ContextRequested non verrà attivato se è stato specificato ContextFlyout.

Creazione di acceleratori di input

Anche se ogni elemento della raccolta deve avere un menu di scelta rapida contenente tutti i comandi contestuali, è possibile consentire agli utenti di eseguire rapidamente un set più piccolo di comandi eseguiti di frequente. Ad esempio, un'app di posta può avere comandi secondari come Rispondi, Archivia, Sposta in cartella, Imposta flag ed Elimina visualizzati in un menu di scelta rapida, ma i comandi più comuni sono Elimina e Flag. Dopo aver identificato quali comandi sono più comuni, è possibile usare acceleratori basati su input per semplificare l'esecuzione di questi comandi da parte di un utente.

Nell'app podcast, il comando eseguito di frequente è il comando "Preferito".

Acceleratori tramite tastiera

Tasti di scelta rapida e gestione diretta dei tasti

Premere CTRL e F per eseguire un'azione

A seconda del tipo di contenuto, è possibile identificare determinate combinazioni di tasti che devono eseguire un'azione. In un'app di posta elettronica, ad esempio, la chiave DEL può essere usata per eliminare il messaggio di posta elettronica selezionato. In un'app podcast, i tasti Ctrl+S o F potrebbero contrassegnare un podcast come preferito per ascoltarlo in un secondo momento. Anche se alcuni comandi hanno tasti di scelta rapida comuni, come DEL da eliminare, altri comandi dispongono di tasti di scelta rapida specifici di un dominio o di app. Se possibile, usare scorciatoie note o prendere in considerazione la possibilità di fornire testo di promemoria in un tooltip per informare l'utente sulla scorciatoia.

L'app può rispondere quando l'utente preme un tasto usando l'evento KeyDown . In generale, gli utenti si aspettano che l'app risponda quando preme il tasto per la prima volta, anziché attendere fino a quando non rilascia il tasto.

Questo esempio illustra come aggiungere il gestore KeyDown a PodcastUserControl per aggiungere un podcast preferito quando l'utente preme CTRL+S o F. Usa lo stesso comando di prima.

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 del mouse

Passare il puntatore del mouse su un elemento per visualizzare un pulsante

Gli utenti hanno familiarità con i menu di scelta rapida del clic con il pulsante destro del mouse, ma è possibile consentire agli utenti di eseguire comandi comuni usando un solo clic del mouse. Per abilitare questa esperienza, è possibile includere pulsanti dedicati nel canvas dell'elemento della raccolta. Per consentire agli utenti di agire rapidamente usando il mouse e ridurre al minimo la confusione visiva, è possibile scegliere di visualizzare solo questi pulsanti quando l'utente ha il puntatore all'interno di una determinata voce di elenco.

In questo esempio, il comando Favorite è rappresentato da un pulsante definito direttamente in PodcastUserControl. Si noti che il pulsante in questo esempio usa lo stesso comando, FavoriteCommand, come in precedenza. Per attivare o disattivare la visibilità di questo pulsante, è possibile usare VisualStateManager per passare da uno stato di visualizzazione all'altro 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 di passaggio del mouse dovrebbero apparire e scomparire quando il mouse entra ed esce dall'elemento. Per rispondere agli eventi del mouse, puoi usare gli eventi PointerEntered e PointerExited su 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 del passaggio del mouse saranno accessibili solo tramite il tipo di input del puntatore. Poiché questi pulsanti sono limitati all'input del puntatore, è possibile scegliere di ridurre o rimuovere la spaziatura interna intorno all'icona del pulsante per ottimizzare l'input del puntatore. Se si sceglie di farlo, assicurarsi che l'impronta del pulsante sia almeno 20x20px per rimanere utilizzabile con penna e mouse.

Acceleratori di tocco

Swipe

Scorrere rapidamente un elemento per visualizzare il comando

Il comando Swipe è un acceleratore di tocco che consente agli utenti nei dispositivi di tocco di eseguire azioni secondarie comuni tramite tocco. Swipe consente agli utenti di tocco di interagire rapidamente e naturalmente con il contenuto, usando azioni comuni come Swipe-to-Delete o Swipe-to-Invoke. Per altre informazioni, vedere l'articolo sui comandi di scorrimento rapido .

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.

Gli SwipeItem possono essere definiti come risorsa in PodcastUserControl. In questo esempio SwipeItems contiene un comando a Preferiti per un elemento.

<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 avvolge l'elemento e consente all'utente di interagire con esso usando il gesto di scorrimento. Si noti che SwipeControl contiene un riferimento a SwipeItems come oggetto RightItems. L'elemento Preferito verrà visualizzato quando l'utente scorre rapidamente da destra a sinistra.

<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 Richiamato.

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

Il pull per l'aggiornamento consente a un utente di eseguire il pull verso il basso su una raccolta di dati tramite tocco per recuperare più dati. Per ulteriori informazioni, vedere l'articolo pull to refresh.

Gli acceleratori penne

Il tipo di input penna offre la precisione dell'input tramite puntatore. Gli utenti possono eseguire azioni comuni, ad esempio l'apertura dei menu di scelta rapida tramite acceleratori basati su penna. Per aprire un menu di scelta rapida, gli utenti possono toccare lo schermo tenendo premuto il pulsante a penna, oppure tenere premuto a lungo sul contenuto. Gli utenti possono anche usare la penna per sfiorare il contenuto per comprendere meglio l'interfaccia utente, come visualizzare suggerimenti o rivelare azioni secondarie al passaggio, in modo simile al mouse.

Per ottimizzare l'app per input tramite penna, vedi l'articolo sull'interazione tra penna e stilo.

Recommendations

  • Assicurarsi che gli utenti possano accedere a tutti i comandi da tutti i tipi di dispositivi Windows.
  • Includere un menu di scelta rapida che fornisce l'accesso a tutti i comandi disponibili per un elemento della raccolta.
  • Fornire acceleratori di input per i comandi usati di frequente.
  • Usare l'interfaccia ICommand per implementare i comandi.