Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este tema, se describe el comando en aplicaciones de Windows. En concreto, se explica cómo puedes usar las clases XamlUICommand y StandardUICommand (junto con la interfaz ICommand) para compartir y administrar comandos en varios tipos de control, independientemente del tipo de dispositivo y de entrada que se use.
Compartir comandos en varios controles, independientemente del tipo de dispositivo y de entrada
API importantes
Información general
Los comandos se pueden invocar directamente a través de interacciones de la interfaz de usuario, como hacer clic en un botón o seleccionar un elemento en un menú contextual. También se pueden invocar indirectamente a través de un dispositivo de entrada, como un acelerador de teclado, un gesto, un reconocimiento de voz o una herramienta de automatización o accesibilidad. Una vez invocado, el comando se puede controlar mediante un control (navegación de texto en un control de edición), una ventana (navegación atrás) o la aplicación (salir).
Los comandos pueden funcionar en un contexto específico dentro de la aplicación, como eliminar texto o deshacer una acción, o pueden ser sin contexto, como silenciar audio o ajustar el brillo.
En la imagen siguiente se muestran dos interfaces de comandos ( commandBar y un commandBarFlyout contextual flotante) que comparten algunos de los mismos comandos.
Barra de comandos
Menú contextual en la galería de fotos de Microsoft
Interacciones de comandos
Debido a la variedad de dispositivos, tipos de entrada y superficies de interfaz de usuario que pueden afectar a cómo se invoca un comando, se recomienda exponer los comandos a través de tantas superficies de comandos como sea posible. Estos pueden incluir una combinación de Swipe, MenuBar, CommandBar, CommandBarFlyout y el menú contextual tradicional.
Para los comandos críticos, use aceleradores específicos de entrada. Los aceleradores de entrada permiten al usuario realizar acciones más rápidamente en función del dispositivo de entrada que use.
Estos son algunos aceleradores de entrada comunes para varios tipos de entrada:
- Puntero : botones de mouse y lápiz al mantener el puntero
- Teclado : métodos abreviados (teclas de acceso y teclas de aceleración)
- Toque - Deslizar el dedo
- Tocar - Desliza hacia abajo para actualizar datos
Debe tener en cuenta el tipo de entrada y las experiencias de usuario para que la funcionalidad de la aplicación sea accesible universalmente. Por ejemplo, las colecciones (especialmente las editables por el usuario) suelen incluir una variedad de comandos específicos que se realizan de forma bastante diferente en función del dispositivo de entrada.
En la tabla siguiente se muestran algunos comandos de colección típicos y formas de exponer esos comandos.
| Command | Independiente de la entrada | Acelerador del mouse | Acelerador de teclado | Acelerador táctil |
|---|---|---|---|---|
| Eliminar elemento | Menú contextual | Botón flotante | Clave DEL | Deslizar el dedo para eliminar |
| Elemento de marca | Menú contextual | Botón flotante | Ctrl+Mayús+G | Deslizar el dedo para marcar |
| Actualizar datos | Menú contextual | N/A | Tecla F5 | Deslizar para actualizar |
| Marcar un elemento como favorito | Menú contextual | Botón flotante | F, Ctrl+S | Desliza para marcar como favorito |
Proporcionar siempre un menú contextual Se recomienda incluir todos los comandos contextuales pertinentes en un menú contextual tradicional o CommandBarFlyout, ya que ambos son compatibles con todos los tipos de entrada. Por ejemplo, si un comando solo se expone durante un evento de puntero al pasar el puntero, no se puede usar en un dispositivo solo táctil.
Comandos en aplicaciones de Windows
Hay varias maneras de compartir y administrar experiencias de comandos en una aplicación Windows. Puede definir controladores de eventos para interacciones estándar, como Click, en código subyacente (esto puede ser bastante ineficaz, dependiendo de la complejidad de la interfaz de usuario), puede enlazar el agente de escucha de eventos para interacciones estándar a un controlador compartido o puede enlazar la propiedad Command del control a una implementación de ICommand que describe la lógica de comandos.
Para proporcionar experiencias de usuario completas y enriquecidas en superficies de comandos de forma eficaz y con una duplicación mínima de código, se recomienda usar las características de enlace de comandos descritas en este tema (para el control de eventos estándar, consulte los temas de eventos individuales).
Para enlazar un control a un recurso de comando compartido, puede implementar las interfaces ICommand usted mismo, o bien puede compilar el comando desde la clase base XamlUICommand o uno de los comandos de plataforma definidos por la clase derivada StandardUICommand .
- La interfaz ICommand (Windows.UI.Xaml.Input.ICommand o System.Windows.Input.ICommand) te permite crear comandos totalmente personalizados y reutilizables en toda la aplicación.
- XamlUICommand también proporciona esta funcionalidad, pero simplifica el desarrollo mediante la exposición de un conjunto de propiedades de comandos integradas, como el comportamiento del comando, los métodos abreviados de teclado (tecla de acceso y tecla de aceleración), el icono, la etiqueta y la descripción.
- StandardUICommand simplifica aún más las cosas al permitirle elegir entre un conjunto de comandos de plataforma estándar con propiedades predefinidas.
Importante
En las aplicaciones para UWP, los comandos son implementaciones de la interfaz Windows.UI.Xaml.Input.ICommand (C++) o system.Windows.Input.ICommand (C#), en función de tu marco de lenguaje elegido.
Experiencias de comandos mediante la clase StandardUICommand
Derivado de XamlUICommand (derivado de Windows.UI.Xaml.Input.ICommand para C++ o System.Windows.Input.ICommand para C#), la clase StandardUICommand expone un conjunto de comandos de plataforma estándar con propiedades predefinidas, como icono, acelerador de teclado y descripción.
StandardUICommand proporciona una manera rápida y coherente de definir comandos comunes como Save o Delete. Todo lo que tiene que hacer es proporcionar las funciones `execute` y `canExecute`.
Example
StandardUICommandSample
| Descargue el código de este ejemplo. |
|---|
| Ejemplo de comandos de UWP (StandardUICommand) |
En este ejemplo, se muestra cómo mejorar un control ListView básico con un comando Delete item implementado a través de la clase StandardUICommand , al tiempo que optimizamos la experiencia del usuario para una variedad de tipos de entrada mediante menuBar, control Swipe , botones de desplazamiento y menú contextual.
Nota:
Este ejemplo requiere el paquete NuGet Microsoft.UI.Xaml.Controls, una parte de WinUI 2.
Xaml:
La muestra de la interfaz de usuario incluye un ListView con cinco elementos. El comando StandardUICommand de eliminación está vinculado a un MenuBarItem, un SwipeItem, un AppBarButton y un menú 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>
Código subyacente
- En primer lugar, definimos una
ListItemDataclase que contiene una cadena de texto e ICommand para cada ListViewItem en nuestro ListView.
public class ListItemData
{
public String Text { get; set; }
public ICommand Command { get; set; }
}
- En la clase MainPage, definimos una colección de
ListItemDataobjetos para la clase DataTemplate de ListViewItemTemplate. A continuación, lo rellenamos con una colección inicial de cinco elementos (con texto y StandardUICommand Delete asociado).
/// <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;
}
- A continuación, definimos el controlador ICommand ExecuteRequested donde implementamos el comando de eliminación de elementos.
/// <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);
}
}
- Por último, definimos controladores para varios eventos ListView, incluidos los eventos PointerEntered, PointerExited y SelectionChanged . Los controladores de eventos de puntero se usan para mostrar u ocultar el botón Eliminar de cada elemento.
/// <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);
}
Experiencias de comandos mediante la clase XamlUICommand
Si necesitas crear un comando que no esté definido por la clase StandardUICommand , o quieres tener más control sobre la apariencia del comando, la clase XamlUICommand deriva de la interfaz ICommand , agregando varias propiedades de interfaz de usuario (como un icono, etiqueta, descripción y métodos abreviados de teclado), métodos y eventos para definir rápidamente la interfaz de usuario y el comportamiento de un comando personalizado.
XamlUICommand permite especificar la interfaz de usuario a través del enlace de control, como un icono, una etiqueta, una descripción y métodos abreviados de teclado (tanto una tecla de acceso como un acelerador de teclado), sin establecer las propiedades individuales.
Example
XamlUICommandSample
| Descargue el código de este ejemplo. |
|---|
| Ejemplo de comandos de UWP (XamlUICommand) |
En este ejemplo se comparte la funcionalidad Delete del ejemplo StandardUICommand anterior, pero se muestra cómo la clase XamlUICommand le permite definir un comando de eliminación personalizado con su propio icono de fuente, etiqueta, acelerador de teclado y descripción. Al igual que el ejemplo StandardUICommand , mejoramos un listView básico con un comando Delete item implementado a través de la clase XamlUICommand , al tiempo que optimizamos la experiencia del usuario para una variedad de tipos de entrada mediante una barra de menús, un control Swipe , botones de desplazamiento y un menú contextual.
Muchos controles de plataforma usan las propiedades XamlUICommand en segundo plano, al igual que nuestro ejemplo StandardUICommand en la sección anterior.
Nota:
Este ejemplo requiere el paquete NuGet Microsoft.UI.Xaml.Controls, una parte de WinUI 2.
Xaml:
La interfaz de usuario de ejemplo incluye un ListView de cinco elementos. El elemento XamlUICommand CustomXamlUICommand personalizado está enlazado a un MenuBarItem, un SwipeItem, un AppBarButton y un menú 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="M"/>
</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>
Código subyacente
- En primer lugar, definimos una
ListItemDataclase que contiene una cadena de texto e ICommand para cada ListViewItem en nuestro ListView.
public class ListItemData
{
public String Text { get; set; }
public ICommand Command { get; set; }
}
- En la clase MainPage, definimos una colección de
ListItemDataobjetos para la clase DataTemplate de ListViewItemTemplate. A continuación, lo rellenamos con una colección inicial de cinco elementos (con texto y XamlUICommand asociado).
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;
}
- A continuación, definimos el controlador ICommand ExecuteRequested donde implementamos el comando de eliminación de elementos.
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);
}
}
- Por último, definimos controladores para varios eventos ListView, incluidos los eventos PointerEntered, PointerExited y SelectionChanged . Los controladores de eventos de puntero se usan para mostrar u ocultar el botón Eliminar de cada elemento.
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);
}
Experiencias de comandos mediante la interfaz ICommand
Los controles estándar de UWP (botón, lista, selección, calendario, texto predictivo) proporcionan la base para muchas experiencias comunes de comandos. Para obtener una lista completa de los tipos de control, consulte Controles y patrones para aplicaciones de Windows.
La manera más básica de admitir una experiencia de comandos estructurado es definir una implementación de la interfaz ICommand (Windows.UI.Xaml.Input.ICommand para C++ o System.Windows.Input.ICommand para C#). Esta instancia de ICommand se puede enlazar a controles como botones.
Nota:
En algunos casos, enlazar un método al evento 'Click' y una propiedad a la propiedad 'IsEnabled' podría ser igual de eficaz.
Example
Ejemplo de ICommand
| Descargue el código de este ejemplo. |
|---|
| Ejemplo de comandos de UWP (ICommand) |
En este ejemplo básico, se muestra cómo se puede invocar un solo comando con un clic de botón, un acelerador de teclado y girar una rueda del mouse.
Usamos dos ListViews, uno rellenado con cinco elementos y el otro vacío, y dos botones, uno para mover elementos de listView de la izquierda a listView de la derecha y el otro para mover elementos de derecha a izquierda. Cada botón se enlaza a un comando correspondiente (ViewModel.MoveRightCommand y ViewModel.MoveLeftCommand, respectivamente), y se habilitan y deshabilitan automáticamente en función del número de elementos de su ListView asociado.
El siguiente código XAML define la interfaz de usuario para nuestro ejemplo.
<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=""
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=""
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>
Este es el código subyacente de la interfaz de usuario anterior.
En el código subyacente, nos conectamos al modelo de vista que contiene nuestro código de comando. Además, definimos un controlador para la entrada desde la rueda del mouse, que también conecta nuestro código de comando.
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;
}
}
}
}
}
Este es el código del ViewModel
Nuestro modelo de vista es donde definimos los detalles de ejecución de los dos comandos de nuestra aplicación, rellenamos una ListView y proporcionamos un convertidor de valores de opacidad para ocultar o mostrar alguna interfaz de usuario adicional basada en el recuento de elementos de cada 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();
}
}
}
Por último, esta es nuestra implementación de la interfaz ICommand.
Aquí, definimos un comando que implementa la interfaz ICommand y simplemente retransmite su funcionalidad a otros objetos.
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);
}
}
}
}
Resumen
La Plataforma universal de Windows proporciona un sistema de comandos sólido y flexible que permite crear aplicaciones que comparten y administran comandos en tipos de control, dispositivos y tipos de entrada.
Use los enfoques siguientes al compilar comandos para las aplicaciones de Windows:
- Escucha y maneja eventos en XAML/code-behind
- Enlazar a un método de control de eventos como Click
- Definir su propia implementación de ICommand
- Creación de objetos XamlUICommand con sus propios valores para un conjunto predefinido de propiedades
- Creación de objetos StandardUICommand con un conjunto de propiedades y valores de plataforma predefinidos
Pasos siguientes
Para obtener un ejemplo completo que muestra una implementación xamlUICommand y StandardUICommand , consulte el ejemplo de la galería de WinUI 2 .
Consulte también
Controles y patrones para aplicaciones de Windows
Samples
Ejemplos de temas
- Ejemplo de comandos de UWP (StandardUICommand)
- Ejemplo de comandos de UWP (XamlUICommand)
- Ejemplo de comandos de UWP (ICommand)