Partager via


Commandes dans les applications Windows à l’aide de StandardUICommand, XamlUICommand et ICommand

Dans cette rubrique, nous décrivons la commande dans les applications Windows. Plus précisément, nous expliquons comment utiliser les classes XamlUICommand et StandardUICommand (ainsi que l’interface ICommand) pour partager et gérer des commandes entre différents types de contrôle, quel que soit le type d’appareil et d’entrée utilisé.

Diagramme représentant une utilisation courante d’une commande partagée : plusieurs surfaces d’interface utilisateur avec une commande « favorite »

Partager des commandes entre différents contrôles, quel que soit le type d’appareil et d’entrée

API importantes

Aperçu

Les commandes peuvent être appelées directement par le biais d’interactions de l’interface utilisateur, comme cliquer sur un bouton ou sélectionner un élément dans un menu contextuel. Elles peuvent également être appelées indirectement par le biais d’un appareil d’entrée tel qu’un accélérateur clavier, un mouvement, une reconnaissance vocale ou un outil d’automatisation/d’accessibilité. Une fois appelée, la commande peut ensuite être gérée par un contrôle (navigation de texte dans un contrôle d’édition), une fenêtre (navigation arrière) ou l’application (sortie).

Les commandes peuvent fonctionner sur un contexte spécifique au sein de votre application, telles que la suppression de texte ou l’annulation d’une action, ou elles peuvent être sans contexte, telles que la désactivation de l’audio ou l’ajustement de la luminosité.

L’image suivante montre deux interfaces de commande (un CommandBar et un CommandBarFlyout contextuel flottant) qui partagent certaines des mêmes commandes.

Barre de commandes développée
Barre de commandes

Menu contextuel dans la galerie Photos Microsoft
Menu contextuel dans la galerie Photos Microsoft

Interactions de commande

En raison de la variété d’appareils, de types d’entrée et d’surfaces d’interface utilisateur qui peuvent affecter la façon dont une commande est appelée, nous vous recommandons d’exposer vos commandes via autant de surfaces de commande que possible. Il peut s’agir d’une combinaison de Swipe, MenuBar, CommandBar, CommandBarFlyout et de menu contextuel traditionnel.

Pour les commandes critiques, utilisez des accélérateurs spécifiques aux entrées. Les accélérateurs d’entrée permettent à un utilisateur d’effectuer des actions plus rapidement en fonction de l’appareil d’entrée qu’il utilise.

Voici quelques accélérateurs d’entrée courants pour différents types d’entrée :

  • Pointeur - Boutons de pointage de souris et de stylet
  • Clavier - Raccourcis (touches d’accès et touches d’accélérateur)
  • Touch - Balayer
  • Touch - Glisser pour actualiser les données

Vous devez prendre en compte le type d’entrée et les expériences utilisateur pour rendre les fonctionnalités de votre application accessibles universellement. Par exemple, les collections (en particulier les regroupements modifiables par l’utilisateur) incluent généralement une variété de commandes spécifiques qui sont effectuées différemment en fonction de l’appareil d’entrée.

Le tableau suivant présente certaines commandes de collection classiques et des moyens d’exposer ces commandes.

Command Indépendant de l’entrée Accélérateur de souris Raccourci clavier Accélérateur tactile
Supprimer un élément Menu contextuel Bouton au survol Touche DEL Balayez pour supprimer
Élément d’indicateur Menu contextuel Bouton au survol Ctrl+Maj+G Balayez pour marquer
Actualiser les données Menu contextuel N/A Touche F5 Tirer pour actualiser
Mettre un élément en favoris Menu contextuel Bouton au survol F, Ctrl+S Balayez vers le favori

Toujours fournir un menu contextuel Nous vous recommandons d’inclure toutes les commandes contextuelles pertinentes dans un menu contextuel traditionnel ou CommandBarFlyout, car les deux sont prises en charge pour tous les types d’entrée. Par exemple, si une commande est exposée uniquement lors d'un événement de survol du pointeur, elle ne peut pas être utilisée sur un appareil ne supportant que le tactile.

Commandes dans les applications Windows

Il existe plusieurs façons de partager et de gérer des expériences de commande dans une application Windows. Vous pouvez définir des gestionnaires d’événements pour les interactions standard, telles que Click, dans le code-behind (cela peut être assez inefficace, en fonction de la complexité de votre interface utilisateur), vous pouvez lier l’écouteur d’événements pour les interactions standard à un gestionnaire partagé, ou vous pouvez lier la propriété Command du contrôle à une implémentation ICommand qui décrit la logique de commande.

Pour fournir des expériences utilisateur enrichies et complètes sur les surfaces de commandes efficacement et avec une duplication de code minimale, nous vous recommandons d’utiliser les fonctionnalités de liaison de commande décrites dans cette rubrique (pour la gestion des événements standard, consultez les rubriques d’événements individuelles).

Pour lier un contrôle à une ressource de commande partagée, vous pouvez implémenter vous-même les interfaces ICommand, ou créer votre commande à partir de la classe de base XamlUICommand ou de l’une des commandes de plateforme définies par la classe dérivée StandardUICommand .

  • L’interface ICommand (Windows.UI.Xaml.Input.ICommand ou System.Windows.Input.ICommand) vous permet de créer des commandes entièrement personnalisées et réutilisables dans votre application.
  • XamlUICommand fournit également cette fonctionnalité, mais simplifie le développement en exposant un ensemble de propriétés de commande intégrées telles que le comportement de commande, les raccourcis clavier (touche d’accès et touche d’accélérateur), l’icône, l’étiquette et la description.
  • StandardUICommand simplifie les choses en vous permettant de choisir parmi un ensemble de commandes de plateforme standard avec des propriétés prédéfinies.

Important

Dans les applications UWP, les commandes sont des implémentations de l’interface Windows.UI.Xaml.Input.ICommand (C++) ou de l’interface System.Windows.Input.ICommand (C#), selon l’infrastructure de langage choisie.

Expériences de commande à l’aide de la classe StandardUICommand

Dérivée de XamlUICommand (dérivée de Windows.UI.Xaml.Input.ICommand pour C++ ou System.Windows.Input.ICommand pour C#), la classe StandardUICommand expose un ensemble de commandes de plateforme standard avec des propriétés prédéfinies telles que l’icône, l’accélérateur de clavier et la description.

StandardUICommand fournit un moyen rapide et cohérent de définir des commandes courantes telles que Save ou Delete. Il vous suffit de fournir les fonctions execute et canExecute.

Example

Exemple StandardUICommand

StandardUICommandSample

Télécharger le code de cet exemple
Exemple de commande UWP (StandardUICommand)

Dans cet exemple, nous montrons comment améliorer un ListView de base avec une commande d’élément Delete implémentée via la classe StandardUICommand , tout en optimisant l’expérience utilisateur pour divers types d’entrée à l’aide d’une barre de menus, d’un contrôle de balayage , de boutons de pointage et de menu contextuel.

Note

Cet exemple nécessite le package NuGet Microsoft.UI.Xaml.Controls, une partie de WinUI 2.

Xaml :

L’exemple d’interface utilisateur inclut un ListView de cinq éléments. Delete StandardUICommand est lié à un MenuBarItem, un SwipeItem, un AppBarButton et un menu ContextFlyout.

<Page
    x:Class="StandardUICommandSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:StandardUICommandSample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:muxcontrols="using:Microsoft.UI.Xaml.Controls"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Page.Resources>
        <Style x:Key="HorizontalSwipe" 
               TargetType="ListViewItem" 
               BasedOn="{StaticResource ListViewItemRevealStyle}">
            <Setter Property="Height" Value="60"/>
            <Setter Property="Padding" Value="0"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="VerticalContentAlignment" Value="Stretch"/>
            <Setter Property="BorderThickness" Value="0"/>
        </Style>
    </Page.Resources>

    <Grid Loaded="ControlExample_Loaded">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" 
                    Padding="10" 
                    BorderThickness="0,0,0,1" 
                    BorderBrush="LightBlue"
                    Background="AliceBlue">
            <TextBlock Style="{StaticResource HeaderTextBlockStyle}">
                StandardUICommand sample
            </TextBlock>
            <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Margin="0,0,0,10">
                This sample shows how to use the StandardUICommand class to 
                share a platform command and consistent user experiences 
                across various controls.
            </TextBlock>
            <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Margin="0,0,0,0">
                Specifically, we define a standard delete command and add it 
                to a variety of command surfaces, all of which share a common 
                icon, label, keyboard accelerator, and description.
            </TextBlock>
        </StackPanel>

        <muxcontrols:MenuBar Grid.Row="1" Padding="10">
            <muxcontrols:MenuBarItem Title="File">
            </muxcontrols:MenuBarItem>
            <muxcontrols:MenuBarItem Title="Edit">
                <MenuFlyoutItem x:Name="DeleteFlyoutItem"/>
            </muxcontrols:MenuBarItem>
            <muxcontrols:MenuBarItem Title="Help">
            </muxcontrols:MenuBarItem>
        </muxcontrols:MenuBar>

        <ListView x:Name="ListViewRight" Grid.Row="2" 
                  Loaded="ListView_Loaded" 
                  IsItemClickEnabled="True" 
                  SelectionMode="Single" 
                  SelectionChanged="ListView_SelectionChanged" 
                  ItemContainerStyle="{StaticResource HorizontalSwipe}">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="local:ListItemData">
                    <UserControl PointerEntered="ListViewSwipeContainer_PointerEntered" 
                                 PointerExited="ListViewSwipeContainer_PointerExited">
                        <UserControl.ContextFlyout>
                            <MenuFlyout>
                                <MenuFlyoutItem 
                                    Command="{x:Bind Command}" 
                                    CommandParameter="{x:Bind Text}" />
                            </MenuFlyout>
                        </UserControl.ContextFlyout>
                        <Grid AutomationProperties.Name="{x:Bind Text}">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="HoveringStates">
                                    <VisualState x:Name="HoverButtonsHidden" />
                                    <VisualState x:Name="HoverButtonsShown">
                                        <VisualState.Setters>
                                            <Setter Target="HoverButton.Visibility" 
                                                    Value="Visible" />
                                        </VisualState.Setters>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <SwipeControl x:Name="ListViewSwipeContainer" >
                                <SwipeControl.RightItems>
                                    <SwipeItems Mode="Execute">
                                        <SwipeItem x:Name="DeleteSwipeItem" 
                                                   Background="Red" 
                                                   Command="{x:Bind Command}" 
                                                   CommandParameter="{x:Bind Text}"/>
                                    </SwipeItems>
                                </SwipeControl.RightItems>
                                <Grid VerticalAlignment="Center">
                                    <TextBlock Text="{x:Bind Text}" 
                                               Margin="10" 
                                               FontSize="18" 
                                               HorizontalAlignment="Left" 
                                               VerticalAlignment="Center"/>
                                    <AppBarButton x:Name="HoverButton" 
                                                  IsTabStop="False" 
                                                  HorizontalAlignment="Right" 
                                                  Visibility="Collapsed" 
                                                  Command="{x:Bind Command}" 
                                                  CommandParameter="{x:Bind Text}"/>
                                </Grid>
                            </SwipeControl>
                        </Grid>
                    </UserControl>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Page>

Code-behind

  1. Tout d’abord, nous définissons une classe qui contient une ListItemData chaîne de texte et ICommand pour chaque ListViewItem dans notre ListView.
public class ListItemData
{
    public String Text { get; set; }
    public ICommand Command { get; set; }
}
  1. Dans la classe MainPage, nous définissons une collection d’objets ListItemData pour le DataTemplate de l’élément ListViewItemTemplate. Nous le remplissons puis avec une collection initiale de cinq éléments (avec du texte et la commande StandardUICommand Delete associée).
/// <summary>
/// ListView item collection.
/// </summary>
ObservableCollection<ListItemData> collection = 
    new ObservableCollection<ListItemData>();

/// <summary>
/// Handler for the layout Grid control load event.
/// </summary>
/// <param name="sender">Source of the control loaded event</param>
/// <param name="e">Event args for the loaded event</param>
private void ControlExample_Loaded(object sender, RoutedEventArgs e)
{
    // Create the standard Delete command.
    var deleteCommand = new StandardUICommand(StandardUICommandKind.Delete);
    deleteCommand.ExecuteRequested += DeleteCommand_ExecuteRequested;

    DeleteFlyoutItem.Command = deleteCommand;

    for (var i = 0; i < 5; i++)
    {
        collection.Add(
            new ListItemData {
                Text = "List item " + i.ToString(),
                Command = deleteCommand });
    }
}

/// <summary>
/// Handler for the ListView control load event.
/// </summary>
/// <param name="sender">Source of the control loaded event</param>
/// <param name="e">Event args for the loaded event</param>
private void ListView_Loaded(object sender, RoutedEventArgs e)
{
    var listView = (ListView)sender;
    // Populate the ListView with the item collection.
    listView.ItemsSource = collection;
}
  1. Ensuite, nous définissons le gestionnaire ICommand ExecuteRequested où nous implémentons la commande de suppression d’élément.
/// <summary>
/// Handler for the Delete command.
/// </summary>
/// <param name="sender">Source of the command event</param>
/// <param name="e">Event args for the command event</param>
private void DeleteCommand_ExecuteRequested(
    XamlUICommand sender, ExecuteRequestedEventArgs args)
{
    // If possible, remove specified item from collection.
    if (args.Parameter != null)
    {
        foreach (var i in collection)
        {
            if (i.Text == (args.Parameter as string))
            {
                collection.Remove(i);
                return;
            }
        }
    }
    if (ListViewRight.SelectedIndex != -1)
    {
        collection.RemoveAt(ListViewRight.SelectedIndex);
    }
}
  1. Enfin, nous définissons des gestionnaires pour différents événements ListView, notamment PointerEntered, PointerExited et SelectionChanged . Les gestionnaires d’événements de pointeur sont utilisés pour afficher ou masquer le bouton Supprimer pour chaque élément.
/// <summary>
/// Handler for the ListView selection changed event.
/// </summary>
/// <param name="sender">Source of the selection changed event</param>
/// <param name="e">Event args for the selection changed event</param>
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (ListViewRight.SelectedIndex != -1)
    {
        var item = collection[ListViewRight.SelectedIndex];
    }
}

/// <summary>
/// Handler for the pointer entered event.
/// Displays the delete item "hover" buttons.
/// </summary>
/// <param name="sender">Source of the pointer entered event</param>
/// <param name="e">Event args for the pointer entered event</param>
private void ListViewSwipeContainer_PointerEntered(
    object sender, PointerRoutedEventArgs e)
{
    if (e.Pointer.PointerDeviceType == 
        Windows.Devices.Input.PointerDeviceType.Mouse || 
        e.Pointer.PointerDeviceType == 
        Windows.Devices.Input.PointerDeviceType.Pen)
    {
        VisualStateManager.GoToState(
            sender as Control, "HoverButtonsShown", true);
    }
}

/// <summary>
/// Handler for the pointer exited event.
/// Hides the delete item "hover" buttons.
/// </summary>
/// <param name="sender">Source of the pointer exited event</param>
/// <param name="e">Event args for the pointer exited event</param>

private void ListViewSwipeContainer_PointerExited(
    object sender, PointerRoutedEventArgs e)
{
    VisualStateManager.GoToState(
        sender as Control, "HoverButtonsHidden", true);
}

Expériences de commande à l’aide de la classe XamlUICommand

Si vous devez créer une commande qui n’est pas définie par la classe StandardUICommand ou que vous souhaitez plus de contrôle sur l’apparence de la commande, la classe XamlUICommand dérive de l’interface ICommand , en ajoutant différentes propriétés d’interface utilisateur (telles qu’une icône, une étiquette, une description et des raccourcis clavier), des méthodes et des événements pour définir rapidement l’interface utilisateur et le comportement d’une commande personnalisée.

XamlUICommand vous permet de spécifier l’interface utilisateur via la liaison de contrôle, telle qu’une icône, une étiquette, une description et des raccourcis clavier (une touche d’accès et un accélérateur clavier), sans définir les propriétés individuelles.

Example

Exemple XamlUICommand

XamlUICommandSample

Télécharger le code de cet exemple
Exemple de commande UWP (XamlUICommand)

Cet exemple partage la fonctionnalité Delete de l’exemple StandardUICommand précédent, mais montre comment la classe XamlUICommand vous permet de définir une commande delete personnalisée avec votre propre icône de police, étiquette, accélérateur de clavier et description. Comme dans l’exemple StandardUICommand , nous améliorons un ListView de base avec une commande d’élément Delete implémentée via la classe XamlUICommand , tout en optimisant l’expérience utilisateur pour divers types d’entrée à l’aide d’une barre de menus, d’un contrôle De balayage , de boutons de pointage et du menu contextuel.

De nombreux contrôles de plateformes utilisent en arrière-plan les propriétés XamlUICommand, tout comme notre exemple StandardUICommand dans la section précédente.

Note

Cet exemple nécessite le package NuGet Microsoft.UI.Xaml.Controls, une partie de WinUI 2.

Xaml :

L’exemple d’interface utilisateur inclut un ListView de cinq éléments. Le XamlUICommand personnalisé est lié à un MenuBarItem, un SwipeItem, un AppBarButton et un menu ContextFlyout.

<Page
    x:Class="XamlUICommand_Sample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlUICommand_Sample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:muxcontrols="using:Microsoft.UI.Xaml.Controls"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Page.Resources>
        <XamlUICommand x:Name="CustomXamlUICommand" 
                       ExecuteRequested="DeleteCommand_ExecuteRequested"
                       Description="Custom XamlUICommand" 
                       Label="Custom XamlUICommand">
            <XamlUICommand.IconSource>
                <FontIconSource FontFamily="Wingdings" Glyph="&#x4D;"/>
            </XamlUICommand.IconSource>
            <XamlUICommand.KeyboardAccelerators>
                <KeyboardAccelerator Key="D" Modifiers="Control"/>
            </XamlUICommand.KeyboardAccelerators>
        </XamlUICommand>

        <Style x:Key="HorizontalSwipe" 
               TargetType="ListViewItem" 
               BasedOn="{StaticResource ListViewItemRevealStyle}">
            <Setter Property="Height" Value="70"/>
            <Setter Property="Padding" Value="0"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="VerticalContentAlignment" Value="Stretch"/>
            <Setter Property="BorderThickness" Value="0"/>
        </Style>
        
    </Page.Resources>

    <Grid Loaded="ControlExample_Loaded" Name="MainGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <StackPanel Grid.Row="0" 
                    Padding="10" 
                    BorderThickness="0,0,0,1" 
                    BorderBrush="LightBlue"
                    Background="AliceBlue">
            <TextBlock Style="{StaticResource HeaderTextBlockStyle}">
                XamlUICommand sample
            </TextBlock>
            <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Margin="0,0,0,10">
                This sample shows how to use the XamlUICommand class to 
                share a custom command with consistent user experiences 
                across various controls.
            </TextBlock>
            <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Margin="0,0,0,0">
                Specifically, we define a custom delete command and add it 
                to a variety of command surfaces, all of which share a common 
                icon, label, keyboard accelerator, and description.
            </TextBlock>
        </StackPanel>

        <muxcontrols:MenuBar Grid.Row="1">
            <muxcontrols:MenuBarItem Title="File">
            </muxcontrols:MenuBarItem>
            <muxcontrols:MenuBarItem Title="Edit">
                <MenuFlyoutItem x:Name="DeleteFlyoutItem" 
                                Command="{StaticResource CustomXamlUICommand}"/>
            </muxcontrols:MenuBarItem>
            <muxcontrols:MenuBarItem Title="Help">
            </muxcontrols:MenuBarItem>
        </muxcontrols:MenuBar>

        <ListView x:Name="ListViewRight" Grid.Row="2" 
                  Loaded="ListView_Loaded" 
                  IsItemClickEnabled="True"
                  SelectionMode="Single" 
                  SelectionChanged="ListView_SelectionChanged" 
                  ItemContainerStyle="{StaticResource HorizontalSwipe}">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="local:ListItemData">
                    <UserControl PointerEntered="ListViewSwipeContainer_PointerEntered"
                                 PointerExited="ListViewSwipeContainer_PointerExited">
                        <UserControl.ContextFlyout>
                            <MenuFlyout>
                                <MenuFlyoutItem 
                                    Command="{x:Bind Command}" 
                                    CommandParameter="{x:Bind Text}" />
                            </MenuFlyout>
                        </UserControl.ContextFlyout>
                        <Grid AutomationProperties.Name="{x:Bind Text}">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="HoveringStates">
                                    <VisualState x:Name="HoverButtonsHidden" />
                                    <VisualState x:Name="HoverButtonsShown">
                                        <VisualState.Setters>
                                            <Setter Target="HoverButton.Visibility" 
                                                    Value="Visible" />
                                        </VisualState.Setters>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <SwipeControl x:Name="ListViewSwipeContainer">
                                <SwipeControl.RightItems>
                                    <SwipeItems Mode="Execute">
                                        <SwipeItem x:Name="DeleteSwipeItem"
                                                   Background="Red" 
                                                   Command="{x:Bind Command}" 
                                                   CommandParameter="{x:Bind Text}"/>
                                    </SwipeItems>
                                </SwipeControl.RightItems>
                                <Grid VerticalAlignment="Center">
                                    <TextBlock Text="{x:Bind Text}" 
                                               Margin="10" 
                                               FontSize="18" 
                                               HorizontalAlignment="Left"       
                                               VerticalAlignment="Center"/>
                                    <AppBarButton x:Name="HoverButton" 
                                                  IsTabStop="False" 
                                                  HorizontalAlignment="Right" 
                                                  Visibility="Collapsed" 
                                                  Command="{x:Bind Command}" 
                                                  CommandParameter="{x:Bind Text}"/>
                                </Grid>
                            </SwipeControl>
                        </Grid>
                    </UserControl>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Page>

Code-behind

  1. Tout d’abord, nous définissons une classe qui contient une ListItemData chaîne de texte et ICommand pour chaque ListViewItem dans notre ListView.
public class ListItemData
{
    public String Text { get; set; }
    public ICommand Command { get; set; }
}
  1. Dans la classe MainPage, nous définissons une collection d’objets ListItemData pour le DataTemplate de l’élément ListViewItemTemplate. Nous le remplissons ensuite avec une collection initiale de cinq éléments (avec du texte et XamlUICommand associé).
ObservableCollection<ListItemData> collection = new ObservableCollection<ListItemData>();

private void ControlExample_Loaded(object sender, RoutedEventArgs e)
{
    for (var i = 0; i < 5; i++)
    {
        collection.Add(
           new ListItemData { Text = "List item " + i.ToString(), Command = CustomXamlUICommand });
    }
}

private void ListView_Loaded(object sender, RoutedEventArgs e)
{
    var listView = (ListView)sender;
    listView.ItemsSource = collection;
}
  1. Ensuite, nous définissons le gestionnaire ICommand ExecuteRequested où nous implémentons la commande de suppression d'un élément.
private void DeleteCommand_ExecuteRequested(
   XamlUICommand sender, ExecuteRequestedEventArgs args)
{
    if (args.Parameter != null)
    {
        foreach (var i in collection)
        {
            if (i.Text == (args.Parameter as string))
            {
                collection.Remove(i);
                return;
            }
        }
    }
    if (ListViewRight.SelectedIndex != -1)
    {
        collection.RemoveAt(ListViewRight.SelectedIndex);
    }
}
  1. Enfin, nous définissons des gestionnaires pour différents événements ListView, notamment PointerEntered, PointerExited et SelectionChanged . Les gestionnaires d’événements de pointeur sont utilisés pour afficher ou masquer le bouton Supprimer pour chaque élément.
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (ListViewRight.SelectedIndex != -1)
    {
        var item = collection[ListViewRight.SelectedIndex];
    }
}

private void ListViewSwipeContainer_PointerEntered(object sender, PointerRoutedEventArgs e)
{
    if (e.Pointer.PointerDeviceType == 
        Windows.Devices.Input.PointerDeviceType.Mouse || 
        e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Pen)
    {
        VisualStateManager.GoToState(sender as Control, "HoverButtonsShown", true);
    }
}

private void ListViewSwipeContainer_PointerExited(object sender, PointerRoutedEventArgs e)
{
    VisualStateManager.GoToState(sender as Control, "HoverButtonsHidden", true);
}

Expériences de commande à l’aide de l’interface ICommand

Les contrôles UWP standard (bouton, liste, sélection, calendrier, texte prédictif) constituent la base de nombreuses expériences de commande courantes. Pour obtenir la liste complète des types de contrôles, consultez Contrôles et modèles pour les applications Windows.

La façon la plus simple de prendre en charge une expérience de commande structurée consiste à définir une implémentation de l’interface ICommand (Windows.UI.Xaml.Input.ICommand pour C++ ou System.Windows.Input.ICommand pour C#). Cette instance ICommand peut ensuite être liée à des contrôles tels que des boutons.

Note

Dans certains cas, il peut s’avérer tout aussi efficace de lier une méthode à l’événement Click et à une propriété à la propriété IsEnabled.

Example

Exemple d’interface de commande

Exemple ICommand

Télécharger le code de cet exemple
Exemple de commande UWP (ICommand)

Dans cet exemple de base, nous montrons comment une seule commande peut être appelée avec un clic sur le bouton, un accélérateur clavier et la rotation d’une roulette de souris.

Nous utilisons deux ListViews, un rempli avec cinq éléments et l’autre vide, et deux boutons, un pour déplacer des éléments de ListView à gauche vers ListView à droite, et l’autre pour déplacer des éléments de droite à gauche. Chaque bouton est lié à une commande correspondante (ViewModel.MoveRightCommand et ViewModel.MoveLeftCommand, respectivement) et est activé et désactivé automatiquement en fonction du nombre d’éléments dans leur ListView associé.

Le code XAML suivant définit l’interface utilisateur de notre exemple.

<Page
    x:Class="UICommand1.View.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="using:UICommand1.ViewModel"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Page.Resources>
        <vm:OpacityConverter x:Key="opaque" />
    </Page.Resources>

    <Grid Name="ItemGrid"
          Background="AliceBlue"
          PointerWheelChanged="Page_PointerWheelChanged">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="2*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <ListView Grid.Column="0" VerticalAlignment="Center"
                  x:Name="CommandListView" 
                  ItemsSource="{x:Bind Path=ViewModel.ListItemLeft}" 
                  SelectionMode="None" IsItemClickEnabled="False" 
                  HorizontalAlignment="Right">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="vm:ListItemData">
                    <Grid VerticalAlignment="Center">
                        <AppBarButton Label="{x:Bind ListItemText}">
                            <AppBarButton.Icon>
                                <SymbolIcon Symbol="{x:Bind ListItemIcon}"/>
                            </AppBarButton.Icon>
                        </AppBarButton>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <Grid Grid.Column="1" Margin="0,0,0,0"
              HorizontalAlignment="Center" 
              VerticalAlignment="Center">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <StackPanel Grid.Row="1">
                <FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" 
                          FontSize="40" Glyph="&#xE893;" 
                          Opacity="{x:Bind Path=ViewModel.ListItemLeft.Count, 
                                        Mode=OneWay, Converter={StaticResource opaque}}"/>
                <Button Name="MoveItemRightButton"
                        Margin="0,10,0,10" Width="120" HorizontalAlignment="Center"
                        Command="{x:Bind Path=ViewModel.MoveRightCommand}">
                    <Button.KeyboardAccelerators>
                        <KeyboardAccelerator 
                            Modifiers="Control" 
                            Key="Add" />
                    </Button.KeyboardAccelerators>
                    <StackPanel>
                        <SymbolIcon Symbol="Next"/>
                        <TextBlock>Move item right</TextBlock>
                    </StackPanel>
                </Button>
                <Button Name="MoveItemLeftButton" 
                            Margin="0,10,0,10" Width="120" HorizontalAlignment="Center"
                            Command="{x:Bind Path=ViewModel.MoveLeftCommand}">
                    <Button.KeyboardAccelerators>
                        <KeyboardAccelerator 
                            Modifiers="Control" 
                            Key="Subtract" />
                    </Button.KeyboardAccelerators>
                    <StackPanel>
                        <SymbolIcon Symbol="Previous"/>
                        <TextBlock>Move item left</TextBlock>
                    </StackPanel>
                </Button>
                <FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" 
                          FontSize="40" Glyph="&#xE892;"
                          Opacity="{x:Bind Path=ViewModel.ListItemRight.Count, 
                                        Mode=OneWay, Converter={StaticResource opaque}}"/>
            </StackPanel>
        </Grid>
        <ListView Grid.Column="2" 
                  x:Name="CommandListViewRight" 
                  VerticalAlignment="Center" 
                  IsItemClickEnabled="False" 
                  SelectionMode="None"
                  ItemsSource="{x:Bind Path=ViewModel.ListItemRight}" 
                  HorizontalAlignment="Left">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="vm:ListItemData">
                    <Grid VerticalAlignment="Center">
                        <AppBarButton Label="{x:Bind ListItemText}">
                            <AppBarButton.Icon>
                                <SymbolIcon Symbol="{x:Bind ListItemIcon}"/>
                            </AppBarButton.Icon>
                        </AppBarButton>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Page>

Voici le code-behind (logique de code associée) de l’interface utilisateur précédente.

Dans code-behind, nous nous connectons à notre modèle d’affichage qui contient notre code de commande. En outre, nous définissons un gestionnaire pour l’entrée à partir de la roulette de la souris, qui connecte également notre code de commande.

using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Controls;
using UICommand1.ViewModel;
using Windows.System;
using Windows.UI.Core;

namespace UICommand1.View
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        // Reference to our view model.
        public UICommand1ViewModel ViewModel { get; set; }

        // Initialize our view and view model.
        public MainPage()
        {
            this.InitializeComponent();
            ViewModel = new UICommand1ViewModel();
        }

        /// <summary>
        /// Handle mouse wheel input and assign our
        /// commands to appropriate direction of rotation.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Page_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
        {
            var props = e.GetCurrentPoint(sender as UIElement).Properties;

            // Require CTRL key and accept only vertical mouse wheel movement 
            // to eliminate accidental wheel input.
            if ((Window.Current.CoreWindow.GetKeyState(VirtualKey.Control) != 
                CoreVirtualKeyStates.None) && !props.IsHorizontalMouseWheel)
            {
                bool delta = props.MouseWheelDelta < 0 ? true : false;

                switch (delta)
                {
                    case true:
                        ViewModel.MoveRight();
                        break;
                    case false:
                        ViewModel.MoveLeft();
                        break;
                    default:
                        break;
                }
            }
        }
    }
}

Voici le code de notre modèle d’affichage

Notre modèle d’affichage est l’endroit où nous définissons les détails d’exécution pour les deux commandes de notre application, remplissez un ListView et fournissez un convertisseur de valeurs d’opacité pour masquer ou afficher une interface utilisateur supplémentaire en fonction du nombre d’éléments de chaque ListView.

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;

namespace UICommand1.ViewModel
{
    /// <summary>
    /// UI properties for our list items.
    /// </summary>
    public class ListItemData
    {
        /// <summary>
        /// Gets and sets the list item content string.
        /// </summary>
        public string ListItemText { get; set; }
        /// <summary>
        /// Gets and sets the list item icon.
        /// </summary>
        public Symbol ListItemIcon { get; set; }
    }

    /// <summary>
    /// View Model that sets up a command to handle invoking the move item buttons.
    /// </summary>
    public class UICommand1ViewModel
    {
        /// <summary>
        /// The command to invoke when the Move item left button is pressed.
        /// </summary>
        public RelayCommand MoveLeftCommand { get; private set; }

        /// <summary>
        /// The command to invoke when the Move item right button is pressed.
        /// </summary>
        public RelayCommand MoveRightCommand { get; private set; }

        // Item collections
        public ObservableCollection<ListItemData> ListItemLeft { get; } = 
           new ObservableCollection<ListItemData>();
        public ObservableCollection<ListItemData> ListItemRight { get; } = 
           new ObservableCollection<ListItemData>();

        public ListItemData listItem;

        /// <summary>
        /// Sets up a command to handle invoking the move item buttons.
        /// </summary>
        public UICommand1ViewModel()
        {
            MoveLeftCommand = 
               new RelayCommand(new Action(MoveLeft), CanExecuteMoveLeftCommand);
            MoveRightCommand = 
               new RelayCommand(new Action(MoveRight), CanExecuteMoveRightCommand);

            LoadItems();
        }

        /// <summary>
        ///  Populate our list of items.
        /// </summary>
        public void LoadItems()
        {
            for (var x = 0; x <= 4; x++)
            {
                listItem = new ListItemData();
                listItem.ListItemText = "Item " + (ListItemLeft.Count + 1).ToString();
                listItem.ListItemIcon = Symbol.Emoji;
                ListItemLeft.Add(listItem);
            }
        }

        /// <summary>
        /// Move left command valid when items present in the list on right.
        /// </summary>
        /// <returns>True, if count is greater than 0.</returns>
        private bool CanExecuteMoveLeftCommand()
        {
            return ListItemRight.Count > 0;
        }

        /// <summary>
        /// Move right command valid when items present in the list on left.
        /// </summary>
        /// <returns>True, if count is greater than 0.</returns>
        private bool CanExecuteMoveRightCommand()
        {
            return ListItemLeft.Count > 0;
        }

        /// <summary>
        /// The command implementation to execute when the Move item right button is pressed.
        /// </summary>
        public void MoveRight()
        {
            if (ListItemLeft.Count > 0)
            {
                listItem = new ListItemData();
                ListItemRight.Add(listItem);
                listItem.ListItemText = "Item " + ListItemRight.Count.ToString();
                listItem.ListItemIcon = Symbol.Emoji;
                ListItemLeft.RemoveAt(ListItemLeft.Count - 1);
                MoveRightCommand.RaiseCanExecuteChanged();
                MoveLeftCommand.RaiseCanExecuteChanged();
            }
        }

        /// <summary>
        /// The command implementation to execute when the Move item left button is pressed.
        /// </summary>
        public void MoveLeft()
        {
            if (ListItemRight.Count > 0)
            {
                listItem = new ListItemData();
                ListItemLeft.Add(listItem);
                listItem.ListItemText = "Item " + ListItemLeft.Count.ToString();
                listItem.ListItemIcon = Symbol.Emoji;
                ListItemRight.RemoveAt(ListItemRight.Count - 1);
                MoveRightCommand.RaiseCanExecuteChanged();
                MoveLeftCommand.RaiseCanExecuteChanged();
            }
        }

        /// <summary>
        /// Views subscribe to this event to get notified of property updates.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Notify subscribers of updates to the named property
        /// </summary>
        /// <param name="propertyName">The full, case-sensitive, name of a property.</param>
        protected void NotifyPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                PropertyChangedEventArgs args = new PropertyChangedEventArgs(propertyName);
                handler(this, args);
            }
        }
    }

    /// <summary>
    /// Convert a collection count to an opacity value of 0.0 or 1.0.
    /// </summary>
    public class OpacityConverter : IValueConverter
    {
        /// <summary>
        /// Converts a collection count to an opacity value of 0.0 or 1.0.
        /// </summary>
        /// <param name="value">The count passed in</param>
        /// <param name="targetType">Ignored.</param>
        /// <param name="parameter">Ignored</param>
        /// <param name="language">Ignored</param>
        /// <returns>1.0 if count > 0, otherwise returns 0.0</returns>
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            return ((int)value > 0 ? 1.0 : 0.0);
        }

        /// <summary>
        /// Not used, converter is not intended for two-way binding. 
        /// </summary>
        /// <param name="value">Ignored</param>
        /// <param name="targetType">Ignored</param>
        /// <param name="parameter">Ignored</param>
        /// <param name="language">Ignored</param>
        /// <returns></returns>
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }
}

Enfin, voici notre implémentation de l’interface ICommand

Ici, nous définissons une commande qui implémente l’interface ICommand et relaye simplement ses fonctionnalités vers d’autres objets.

using System;
using System.Windows.Input;

namespace UICommand1
{
    /// <summary>
    /// A command whose sole purpose is to relay its functionality 
    /// to other objects by invoking delegates. 
    /// The default return value for the CanExecute method is 'true'.
    /// <see cref="RaiseCanExecuteChanged"/> needs to be called whenever
    /// <see cref="CanExecute"/> is expected to return a different value.
    /// </summary>
    public class RelayCommand : ICommand
    {
        private readonly Action _execute;
        private readonly Func<bool> _canExecute;

        /// <summary>
        /// Raised when RaiseCanExecuteChanged is called.
        /// </summary>
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Creates a new command that can always execute.
        /// </summary>
        /// <param name="execute">The execution logic.</param>
        public RelayCommand(Action execute)
            : this(execute, null)
        {
        }

        /// <summary>
        /// Creates a new command.
        /// </summary>
        /// <param name="execute">The execution logic.</param>
        /// <param name="canExecute">The execution status logic.</param>
        public RelayCommand(Action execute, Func<bool> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            _execute = execute;
            _canExecute = canExecute;
        }

        /// <summary>
        /// Determines whether this <see cref="RelayCommand"/> can execute in its current state.
        /// </summary>
        /// <param name="parameter">
        /// Data used by the command. If the command does not require 
        /// data to be passed, this object can be set to null.
        /// </param>
        /// <returns>true if this command can be executed; otherwise, false.</returns>
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute();
        }

        /// <summary>
        /// Executes the <see cref="RelayCommand"/> on the current command target.
        /// </summary>
        /// <param name="parameter">
        /// Data used by the command. If the command does not require 
        /// data to be passed, this object can be set to null.
        /// </param>
        public void Execute(object parameter)
        {
            _execute();
        }

        /// <summary>
        /// Method used to raise the <see cref="CanExecuteChanged"/> event
        /// to indicate that the return value of the <see cref="CanExecute"/>
        /// method has changed.
        /// </summary>
        public void RaiseCanExecuteChanged()
        {
            var handler = CanExecuteChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }
}

Résumé

La plateforme Windows universelle fournit un système de commande robuste et flexible qui vous permet de créer des applications qui partagent et gèrent des commandes entre les types de contrôle, les appareils et les types d’entrée.

Utilisez les approches suivantes lors de la génération de commandes pour vos applications Windows :

  • Écouter et gérer des événements en XAML/code-behind
  • Lier à une méthode de gestion des événements telle que Click
  • Définir votre propre implémentation ICommand
  • Créer des objets XamlUICommand avec vos propres valeurs pour un ensemble prédéfini de propriétés
  • Créer des objets StandardUICommand avec un ensemble de propriétés et de valeurs de plateforme prédéfinies

Étapes suivantes

Pour obtenir un exemple complet qui illustre une implémentation XamlUICommand et StandardUICommand , consultez l’exemple de galerie WinUI 2 .

Voir aussi

Contrôles et modèles pour les applications Windows

Samples

Exemples de rubriques

Autres exemples